VirtualBox

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

Last change on this file since 69734 was 68986, checked in by vboxsync, 7 years ago

VMM: Add new QueryGenericObject entry to VMM2USER callback table, for querying API objects by UUID.
PDM: Add new QueryGenericUserObject entry to trusted DevHlp callback table, for querying API objects by UUID from PDM devices.
Main/Session+Console+Machine+Snapshot: Implement this callback and provide the console and snapshot objects this way.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 517.0 KB
Line 
1/* $Id: MachineImpl.cpp 68986 2017-10-04 14:37:38Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69
70#include <VBox/com/array.h>
71#include <VBox/com/list.h>
72
73#include <VBox/err.h>
74#include <VBox/param.h>
75#include <VBox/settings.h>
76#include <VBox/vmm/ssm.h>
77
78#ifdef VBOX_WITH_GUEST_PROPS
79# include <VBox/HostServices/GuestPropertySvc.h>
80# include <VBox/com/array.h>
81#endif
82
83#include "VBox/com/MultiResult.h"
84
85#include <algorithm>
86
87#ifdef VBOX_WITH_DTRACE_R3_MAIN
88# include "dtrace/VBoxAPI.h"
89#endif
90
91#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
92# define HOSTSUFF_EXE ".exe"
93#else /* !RT_OS_WINDOWS */
94# define HOSTSUFF_EXE ""
95#endif /* !RT_OS_WINDOWS */
96
97// defines / prototypes
98/////////////////////////////////////////////////////////////////////////////
99
100/////////////////////////////////////////////////////////////////////////////
101// Machine::Data structure
102/////////////////////////////////////////////////////////////////////////////
103
104Machine::Data::Data()
105{
106 mRegistered = FALSE;
107 pMachineConfigFile = NULL;
108 /* Contains hints on what has changed when the user is using the VM (config
109 * changes, running the VM, ...). This is used to decide if a config needs
110 * to be written to disk. */
111 flModifications = 0;
112 /* VM modification usually also trigger setting the current state to
113 * "Modified". Although this is not always the case. An e.g. is the VM
114 * initialization phase or when snapshot related data is changed. The
115 * actually behavior is controlled by the following flag. */
116 m_fAllowStateModification = false;
117 mAccessible = FALSE;
118 /* mUuid is initialized in Machine::init() */
119
120 mMachineState = MachineState_PoweredOff;
121 RTTimeNow(&mLastStateChange);
122
123 mMachineStateDeps = 0;
124 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
125 mMachineStateChangePending = 0;
126
127 mCurrentStateModified = TRUE;
128 mGuestPropertiesModified = FALSE;
129
130 mSession.mPID = NIL_RTPROCESS;
131 mSession.mLockType = LockType_Null;
132 mSession.mState = SessionState_Unlocked;
133}
134
135Machine::Data::~Data()
136{
137 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
138 {
139 RTSemEventMultiDestroy(mMachineStateDepsSem);
140 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
141 }
142 if (pMachineConfigFile)
143 {
144 delete pMachineConfigFile;
145 pMachineConfigFile = NULL;
146 }
147}
148
149/////////////////////////////////////////////////////////////////////////////
150// Machine::HWData structure
151/////////////////////////////////////////////////////////////////////////////
152
153Machine::HWData::HWData()
154{
155 /* default values for a newly created machine */
156 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
157 mMemorySize = 128;
158 mCPUCount = 1;
159 mCPUHotPlugEnabled = false;
160 mMemoryBalloonSize = 0;
161 mPageFusionEnabled = false;
162 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
163 mVRAMSize = 8;
164 mAccelerate3DEnabled = false;
165 mAccelerate2DVideoEnabled = false;
166 mMonitorCount = 1;
167 mVideoCaptureWidth = 1024;
168 mVideoCaptureHeight = 768;
169 mVideoCaptureRate = 512;
170 mVideoCaptureFPS = 25;
171 mVideoCaptureMaxTime = 0;
172 mVideoCaptureMaxFileSize = 0;
173 mVideoCaptureEnabled = false;
174 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
175 maVideoCaptureScreens[i] = true;
176
177 mHWVirtExEnabled = true;
178 mHWVirtExNestedPagingEnabled = true;
179#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
180 mHWVirtExLargePagesEnabled = true;
181#else
182 /* Not supported on 32 bits hosts. */
183 mHWVirtExLargePagesEnabled = false;
184#endif
185 mHWVirtExVPIDEnabled = true;
186 mHWVirtExUXEnabled = true;
187 mHWVirtExForceEnabled = false;
188#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
189 mPAEEnabled = true;
190#else
191 mPAEEnabled = false;
192#endif
193 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
194 mTripleFaultReset = false;
195 mAPIC = true;
196 mX2APIC = 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 aOsType OS Type of this machine or NULL.
274 * @param aId UUID for the new machine.
275 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
276 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
277 * scheme (includes the UUID).
278 *
279 * @return Success indicator. if not S_OK, the machine object is invalid
280 */
281HRESULT Machine::init(VirtualBox *aParent,
282 const Utf8Str &strConfigFile,
283 const Utf8Str &strName,
284 const StringsList &llGroups,
285 GuestOSType *aOsType,
286 const Guid &aId,
287 bool fForceOverwrite,
288 bool fDirectoryIncludesUUID)
289{
290 LogFlowThisFuncEnter();
291 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
292
293 /* Enclose the state transition NotReady->InInit->Ready */
294 AutoInitSpan autoInitSpan(this);
295 AssertReturn(autoInitSpan.isOk(), E_FAIL);
296
297 HRESULT rc = initImpl(aParent, strConfigFile);
298 if (FAILED(rc)) return rc;
299
300 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
301 if (FAILED(rc)) return rc;
302
303 if (SUCCEEDED(rc))
304 {
305 // create an empty machine config
306 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
307
308 rc = initDataAndChildObjects();
309 }
310
311 if (SUCCEEDED(rc))
312 {
313 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
314 mData->mAccessible = TRUE;
315
316 unconst(mData->mUuid) = aId;
317
318 mUserData->s.strName = strName;
319
320 mUserData->s.llGroups = llGroups;
321
322 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
323 // the "name sync" flag determines whether the machine directory gets renamed along
324 // with the machine file; say so if the settings file name is the same as the
325 // settings file parent directory (machine directory)
326 mUserData->s.fNameSync = i_isInOwnDir();
327
328 // initialize the default snapshots folder
329 rc = COMSETTER(SnapshotFolder)(NULL);
330 AssertComRC(rc);
331
332 if (aOsType)
333 {
334 /* Store OS type */
335 mUserData->s.strOsType = aOsType->i_id();
336
337 /* Apply BIOS defaults */
338 mBIOSSettings->i_applyDefaults(aOsType);
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
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Apply parallel port defaults */
357 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
358 mParallelPorts[slot]->i_applyDefaults();
359
360 /* At this point the changing of the current state modification
361 * flag is allowed. */
362 i_allowStateModification();
363
364 /* commit all changes made during the initialization */
365 i_commit();
366 }
367
368 /* Confirm a successful initialization when it's the case */
369 if (SUCCEEDED(rc))
370 {
371 if (mData->mAccessible)
372 autoInitSpan.setSucceeded();
373 else
374 autoInitSpan.setLimited();
375 }
376
377 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
378 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
379 mData->mRegistered,
380 mData->mAccessible,
381 rc));
382
383 LogFlowThisFuncLeave();
384
385 return rc;
386}
387
388/**
389 * Initializes a new instance with data from machine XML (formerly Init_Registered).
390 * Gets called in two modes:
391 *
392 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
393 * UUID is specified and we mark the machine as "registered";
394 *
395 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
396 * and the machine remains unregistered until RegisterMachine() is called.
397 *
398 * @param aParent Associated parent object
399 * @param strConfigFile Local file system path to the VM settings file (can
400 * be relative to the VirtualBox config directory).
401 * @param aId UUID of the machine or NULL (see above).
402 *
403 * @return Success indicator. if not S_OK, the machine object is invalid
404 */
405HRESULT Machine::initFromSettings(VirtualBox *aParent,
406 const Utf8Str &strConfigFile,
407 const Guid *aId)
408{
409 LogFlowThisFuncEnter();
410 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
411
412 /* Enclose the state transition NotReady->InInit->Ready */
413 AutoInitSpan autoInitSpan(this);
414 AssertReturn(autoInitSpan.isOk(), E_FAIL);
415
416 HRESULT rc = initImpl(aParent, strConfigFile);
417 if (FAILED(rc)) return rc;
418
419 if (aId)
420 {
421 // loading a registered VM:
422 unconst(mData->mUuid) = *aId;
423 mData->mRegistered = TRUE;
424 // now load the settings from XML:
425 rc = i_registeredInit();
426 // this calls initDataAndChildObjects() and loadSettings()
427 }
428 else
429 {
430 // opening an unregistered VM (VirtualBox::OpenMachine()):
431 rc = initDataAndChildObjects();
432
433 if (SUCCEEDED(rc))
434 {
435 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
436 mData->mAccessible = TRUE;
437
438 try
439 {
440 // load and parse machine XML; this will throw on XML or logic errors
441 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
442
443 // reject VM UUID duplicates, they can happen if someone
444 // tries to register an already known VM config again
445 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
446 true /* fPermitInaccessible */,
447 false /* aDoSetError */,
448 NULL) != VBOX_E_OBJECT_NOT_FOUND)
449 {
450 throw setError(E_FAIL,
451 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
452 mData->m_strConfigFile.c_str());
453 }
454
455 // use UUID from machine config
456 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
457
458 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
459 NULL /* puuidRegistry */);
460 if (FAILED(rc)) throw rc;
461
462 /* At this point the changing of the current state modification
463 * flag is allowed. */
464 i_allowStateModification();
465
466 i_commit();
467 }
468 catch (HRESULT err)
469 {
470 /* we assume that error info is set by the thrower */
471 rc = err;
472 }
473 catch (...)
474 {
475 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
476 }
477 }
478 }
479
480 /* Confirm a successful initialization when it's the case */
481 if (SUCCEEDED(rc))
482 {
483 if (mData->mAccessible)
484 autoInitSpan.setSucceeded();
485 else
486 {
487 autoInitSpan.setLimited();
488
489 // uninit media from this machine's media registry, or else
490 // reloading the settings will fail
491 mParent->i_unregisterMachineMedia(i_getId());
492 }
493 }
494
495 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
496 "rc=%08X\n",
497 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
498 mData->mRegistered, mData->mAccessible, rc));
499
500 LogFlowThisFuncLeave();
501
502 return rc;
503}
504
505/**
506 * Initializes a new instance from a machine config that is already in memory
507 * (import OVF case). Since we are importing, the UUID in the machine
508 * config is ignored and we always generate a fresh one.
509 *
510 * @param aParent Associated parent object.
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->i_getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = i_loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 i_allowStateModification();
565
566 /* commit all changes made during the initialization */
567 i_commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 /* Ignore all errors from unregistering, they would destroy
579- * the more interesting error information we already have,
580- * pinpointing the issue with the VM config. */
581 ErrorInfoKeeper eik;
582
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->i_unregisterMachineMedia(i_getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent The VirtualBox object.
604 * @param strConfigFile Settings file.
605 * @return
606 */
607HRESULT Machine::initImpl(VirtualBox *aParent,
608 const Utf8Str &strConfigFile)
609{
610 LogFlowThisFuncEnter();
611
612 AssertReturn(aParent, E_INVALIDARG);
613 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
614
615 HRESULT rc = S_OK;
616
617 /* share the parent weakly */
618 unconst(mParent) = aParent;
619
620 /* allocate the essential machine data structure (the rest will be
621 * allocated later by initDataAndChildObjects() */
622 mData.allocate();
623
624 /* memorize the config file name (as provided) */
625 mData->m_strConfigFile = strConfigFile;
626
627 /* get the full file name */
628 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
629 if (RT_FAILURE(vrc1))
630 return setError(VBOX_E_FILE_ERROR,
631 tr("Invalid machine settings file name '%s' (%Rrc)"),
632 strConfigFile.c_str(),
633 vrc1);
634
635 LogFlowThisFuncLeave();
636
637 return rc;
638}
639
640/**
641 * Tries to create a machine settings file in the path stored in the machine
642 * instance data. Used when a new machine is created to fail gracefully if
643 * the settings file could not be written (e.g. because machine dir is read-only).
644 * @return
645 */
646HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
647{
648 HRESULT rc = S_OK;
649
650 // when we create a new machine, we must be able to create the settings file
651 RTFILE f = NIL_RTFILE;
652 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
653 if ( RT_SUCCESS(vrc)
654 || vrc == VERR_SHARING_VIOLATION
655 )
656 {
657 if (RT_SUCCESS(vrc))
658 RTFileClose(f);
659 if (!fForceOverwrite)
660 rc = setError(VBOX_E_FILE_ERROR,
661 tr("Machine settings file '%s' already exists"),
662 mData->m_strConfigFileFull.c_str());
663 else
664 {
665 /* try to delete the config file, as otherwise the creation
666 * of a new settings file will fail. */
667 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
668 if (RT_FAILURE(vrc2))
669 rc = setError(VBOX_E_FILE_ERROR,
670 tr("Could not delete the existing settings file '%s' (%Rrc)"),
671 mData->m_strConfigFileFull.c_str(), vrc2);
672 }
673 }
674 else if ( vrc != VERR_FILE_NOT_FOUND
675 && vrc != VERR_PATH_NOT_FOUND
676 )
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Invalid machine settings file name '%s' (%Rrc)"),
679 mData->m_strConfigFileFull.c_str(),
680 vrc);
681 return rc;
682}
683
684/**
685 * Initializes the registered machine by loading the settings file.
686 * This method is separated from #init() in order to make it possible to
687 * retry the operation after VirtualBox startup instead of refusing to
688 * startup the whole VirtualBox server in case if the settings file of some
689 * registered VM is invalid or inaccessible.
690 *
691 * @note Must be always called from this object's write lock
692 * (unless called from #init() that doesn't need any locking).
693 * @note Locks the mUSBController method for writing.
694 * @note Subclasses must not call this method.
695 */
696HRESULT Machine::i_registeredInit()
697{
698 AssertReturn(!i_isSessionMachine(), E_FAIL);
699 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
700 AssertReturn(mData->mUuid.isValid(), E_FAIL);
701 AssertReturn(!mData->mAccessible, E_FAIL);
702
703 HRESULT rc = initDataAndChildObjects();
704
705 if (SUCCEEDED(rc))
706 {
707 /* Temporarily reset the registered flag in order to let setters
708 * potentially called from loadSettings() succeed (isMutable() used in
709 * all setters will return FALSE for a Machine instance if mRegistered
710 * is TRUE). */
711 mData->mRegistered = FALSE;
712
713 try
714 {
715 // load and parse machine XML; this will throw on XML or logic errors
716 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
717
718 if (mData->mUuid != mData->pMachineConfigFile->uuid)
719 throw setError(E_FAIL,
720 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
721 mData->pMachineConfigFile->uuid.raw(),
722 mData->m_strConfigFileFull.c_str(),
723 mData->mUuid.toString().c_str(),
724 mParent->i_settingsFilePath().c_str());
725
726 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
727 NULL /* const Guid *puuidRegistry */);
728 if (FAILED(rc)) throw rc;
729 }
730 catch (HRESULT err)
731 {
732 /* we assume that error info is set by the thrower */
733 rc = err;
734 }
735 catch (...)
736 {
737 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
738 }
739
740 /* Restore the registered flag (even on failure) */
741 mData->mRegistered = TRUE;
742 }
743
744 if (SUCCEEDED(rc))
745 {
746 /* Set mAccessible to TRUE only if we successfully locked and loaded
747 * the settings file */
748 mData->mAccessible = TRUE;
749
750 /* commit all changes made during loading the settings file */
751 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
752 /// @todo r=klaus for some reason the settings loading logic backs up
753 // the settings, and therefore a commit is needed. Should probably be changed.
754 }
755 else
756 {
757 /* If the machine is registered, then, instead of returning a
758 * failure, we mark it as inaccessible and set the result to
759 * success to give it a try later */
760
761 /* fetch the current error info */
762 mData->mAccessError = com::ErrorInfo();
763 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
764
765 /* rollback all changes */
766 i_rollback(false /* aNotify */);
767
768 // uninit media from this machine's media registry, or else
769 // reloading the settings will fail
770 mParent->i_unregisterMachineMedia(i_getId());
771
772 /* uninitialize the common part to make sure all data is reset to
773 * default (null) values */
774 uninitDataAndChildObjects();
775
776 rc = S_OK;
777 }
778
779 return rc;
780}
781
782/**
783 * Uninitializes the instance.
784 * Called either from FinalRelease() or by the parent when it gets destroyed.
785 *
786 * @note The caller of this method must make sure that this object
787 * a) doesn't have active callers on the current thread and b) is not locked
788 * by the current thread; otherwise uninit() will hang either a) due to
789 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
790 * a dead-lock caused by this thread waiting for all callers on the other
791 * threads are done but preventing them from doing so by holding a lock.
792 */
793void Machine::uninit()
794{
795 LogFlowThisFuncEnter();
796
797 Assert(!isWriteLockOnCurrentThread());
798
799 Assert(!uRegistryNeedsSaving);
800 if (uRegistryNeedsSaving)
801 {
802 AutoCaller autoCaller(this);
803 if (SUCCEEDED(autoCaller.rc()))
804 {
805 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
806 i_saveSettings(NULL, Machine::SaveS_Force);
807 }
808 }
809
810 /* Enclose the state transition Ready->InUninit->NotReady */
811 AutoUninitSpan autoUninitSpan(this);
812 if (autoUninitSpan.uninitDone())
813 return;
814
815 Assert(!i_isSnapshotMachine());
816 Assert(!i_isSessionMachine());
817 Assert(!!mData);
818
819 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
820 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
821
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823
824 if (!mData->mSession.mMachine.isNull())
825 {
826 /* Theoretically, this can only happen if the VirtualBox server has been
827 * terminated while there were clients running that owned open direct
828 * sessions. Since in this case we are definitely called by
829 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
830 * won't happen on the client watcher thread (because it has a
831 * VirtualBox caller for the duration of the
832 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
833 * cannot happen until the VirtualBox caller is released). This is
834 * important, because SessionMachine::uninit() cannot correctly operate
835 * after we return from this method (it expects the Machine instance is
836 * still valid). We'll call it ourselves below.
837 */
838 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
839 (SessionMachine*)mData->mSession.mMachine));
840
841 if (Global::IsOnlineOrTransient(mData->mMachineState))
842 {
843 Log1WarningThisFunc(("Setting state to Aborted!\n"));
844 /* set machine state using SessionMachine reimplementation */
845 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
846 }
847
848 /*
849 * Uninitialize SessionMachine using public uninit() to indicate
850 * an unexpected uninitialization.
851 */
852 mData->mSession.mMachine->uninit();
853 /* SessionMachine::uninit() must set mSession.mMachine to null */
854 Assert(mData->mSession.mMachine.isNull());
855 }
856
857 // uninit media from this machine's media registry, if they're still there
858 Guid uuidMachine(i_getId());
859
860 /* the lock is no more necessary (SessionMachine is uninitialized) */
861 alock.release();
862
863 /* XXX This will fail with
864 * "cannot be closed because it is still attached to 1 virtual machines"
865 * because at this point we did not call uninitDataAndChildObjects() yet
866 * and therefore also removeBackReference() for all these mediums was not called! */
867
868 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
869 mParent->i_unregisterMachineMedia(uuidMachine);
870
871 // has machine been modified?
872 if (mData->flModifications)
873 {
874 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
875 i_rollback(false /* aNotify */);
876 }
877
878 if (mData->mAccessible)
879 uninitDataAndChildObjects();
880
881 /* free the essential data structure last */
882 mData.free();
883
884 LogFlowThisFuncLeave();
885}
886
887// Wrapped IMachine properties
888/////////////////////////////////////////////////////////////////////////////
889HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
890{
891 /* mParent is constant during life time, no need to lock */
892 ComObjPtr<VirtualBox> pVirtualBox(mParent);
893 aParent = pVirtualBox;
894
895 return S_OK;
896}
897
898
899HRESULT Machine::getAccessible(BOOL *aAccessible)
900{
901 /* In some cases (medium registry related), it is necessary to be able to
902 * go through the list of all machines. Happens when an inaccessible VM
903 * has a sensible medium registry. */
904 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
906
907 HRESULT rc = S_OK;
908
909 if (!mData->mAccessible)
910 {
911 /* try to initialize the VM once more if not accessible */
912
913 AutoReinitSpan autoReinitSpan(this);
914 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
915
916#ifdef DEBUG
917 LogFlowThisFunc(("Dumping media backreferences\n"));
918 mParent->i_dumpAllBackRefs();
919#endif
920
921 if (mData->pMachineConfigFile)
922 {
923 // reset the XML file to force loadSettings() (called from i_registeredInit())
924 // to parse it again; the file might have changed
925 delete mData->pMachineConfigFile;
926 mData->pMachineConfigFile = NULL;
927 }
928
929 rc = i_registeredInit();
930
931 if (SUCCEEDED(rc) && mData->mAccessible)
932 {
933 autoReinitSpan.setSucceeded();
934
935 /* make sure interesting parties will notice the accessibility
936 * state change */
937 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
938 mParent->i_onMachineDataChange(mData->mUuid);
939 }
940 }
941
942 if (SUCCEEDED(rc))
943 *aAccessible = mData->mAccessible;
944
945 LogFlowThisFuncLeave();
946
947 return rc;
948}
949
950HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
951{
952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
953
954 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
955 {
956 /* return shortly */
957 aAccessError = NULL;
958 return S_OK;
959 }
960
961 HRESULT rc = S_OK;
962
963 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
964 rc = errorInfo.createObject();
965 if (SUCCEEDED(rc))
966 {
967 errorInfo->init(mData->mAccessError.getResultCode(),
968 mData->mAccessError.getInterfaceID().ref(),
969 Utf8Str(mData->mAccessError.getComponent()).c_str(),
970 Utf8Str(mData->mAccessError.getText()));
971 aAccessError = errorInfo;
972 }
973
974 return rc;
975}
976
977HRESULT Machine::getName(com::Utf8Str &aName)
978{
979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
980
981 aName = mUserData->s.strName;
982
983 return S_OK;
984}
985
986HRESULT Machine::setName(const com::Utf8Str &aName)
987{
988 // prohibit setting a UUID only as the machine name, or else it can
989 // never be found by findMachine()
990 Guid test(aName);
991
992 if (test.isValid())
993 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
994
995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 HRESULT rc = i_checkStateDependency(MutableStateDep);
998 if (FAILED(rc)) return rc;
999
1000 i_setModified(IsModified_MachineData);
1001 mUserData.backup();
1002 mUserData->s.strName = aName;
1003
1004 return S_OK;
1005}
1006
1007HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1008{
1009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1010
1011 aDescription = mUserData->s.strDescription;
1012
1013 return S_OK;
1014}
1015
1016HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1017{
1018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1019
1020 // this can be done in principle in any state as it doesn't affect the VM
1021 // significantly, but play safe by not messing around while complex
1022 // activities are going on
1023 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1024 if (FAILED(rc)) return rc;
1025
1026 i_setModified(IsModified_MachineData);
1027 mUserData.backup();
1028 mUserData->s.strDescription = aDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::getId(com::Guid &aId)
1034{
1035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 aId = mData->mUuid;
1038
1039 return S_OK;
1040}
1041
1042HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1043{
1044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1045 aGroups.resize(mUserData->s.llGroups.size());
1046 size_t i = 0;
1047 for (StringsList::const_iterator
1048 it = mUserData->s.llGroups.begin();
1049 it != mUserData->s.llGroups.end();
1050 ++it, ++i)
1051 aGroups[i] = (*it);
1052
1053 return S_OK;
1054}
1055
1056HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1057{
1058 StringsList llGroups;
1059 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1060 if (FAILED(rc))
1061 return rc;
1062
1063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1064
1065 rc = i_checkStateDependency(MutableOrSavedStateDep);
1066 if (FAILED(rc)) return rc;
1067
1068 i_setModified(IsModified_MachineData);
1069 mUserData.backup();
1070 mUserData->s.llGroups = llGroups;
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1076{
1077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1078
1079 aOSTypeId = mUserData->s.strOsType;
1080
1081 return S_OK;
1082}
1083
1084HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1085{
1086 /* look up the object by Id to check it is valid */
1087 ComObjPtr<GuestOSType> pGuestOSType;
1088 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1089 pGuestOSType);
1090 if (FAILED(rc)) return rc;
1091
1092 /* when setting, always use the "etalon" value for consistency -- lookup
1093 * by ID is case-insensitive and the input value may have different case */
1094 Utf8Str osTypeId = pGuestOSType->i_id();
1095
1096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 rc = i_checkStateDependency(MutableStateDep);
1099 if (FAILED(rc)) return rc;
1100
1101 i_setModified(IsModified_MachineData);
1102 mUserData.backup();
1103 mUserData->s.strOsType = osTypeId;
1104
1105 return S_OK;
1106}
1107
1108HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1109{
1110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 *aFirmwareType = mHWData->mFirmwareType;
1113
1114 return S_OK;
1115}
1116
1117HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1118{
1119 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1120
1121 HRESULT rc = i_checkStateDependency(MutableStateDep);
1122 if (FAILED(rc)) return rc;
1123
1124 i_setModified(IsModified_MachineData);
1125 mHWData.backup();
1126 mHWData->mFirmwareType = aFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1132{
1133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1136
1137 return S_OK;
1138}
1139
1140HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1141{
1142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1143
1144 HRESULT rc = i_checkStateDependency(MutableStateDep);
1145 if (FAILED(rc)) return rc;
1146
1147 i_setModified(IsModified_MachineData);
1148 mHWData.backup();
1149 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1155{
1156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 *aPointingHIDType = mHWData->mPointingHIDType;
1159
1160 return S_OK;
1161}
1162
1163HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1164{
1165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1166
1167 HRESULT rc = i_checkStateDependency(MutableStateDep);
1168 if (FAILED(rc)) return rc;
1169
1170 i_setModified(IsModified_MachineData);
1171 mHWData.backup();
1172 mHWData->mPointingHIDType = aPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1178{
1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 *aChipsetType = mHWData->mChipsetType;
1182
1183 return S_OK;
1184}
1185
1186HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1187{
1188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1189
1190 HRESULT rc = i_checkStateDependency(MutableStateDep);
1191 if (FAILED(rc)) return rc;
1192
1193 if (aChipsetType != mHWData->mChipsetType)
1194 {
1195 i_setModified(IsModified_MachineData);
1196 mHWData.backup();
1197 mHWData->mChipsetType = aChipsetType;
1198
1199 // Resize network adapter array, to be finalized on commit/rollback.
1200 // We must not throw away entries yet, otherwise settings are lost
1201 // without a way to roll back.
1202 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1203 size_t oldCount = mNetworkAdapters.size();
1204 if (newCount > oldCount)
1205 {
1206 mNetworkAdapters.resize(newCount);
1207 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1208 {
1209 unconst(mNetworkAdapters[slot]).createObject();
1210 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1211 }
1212 }
1213 }
1214
1215 return S_OK;
1216}
1217
1218HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1219{
1220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1221
1222 aParavirtDebug = mHWData->mParavirtDebug;
1223 return S_OK;
1224}
1225
1226HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1227{
1228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1229
1230 HRESULT rc = i_checkStateDependency(MutableStateDep);
1231 if (FAILED(rc)) return rc;
1232
1233 /** @todo Parse/validate options? */
1234 if (aParavirtDebug != mHWData->mParavirtDebug)
1235 {
1236 i_setModified(IsModified_MachineData);
1237 mHWData.backup();
1238 mHWData->mParavirtDebug = aParavirtDebug;
1239 }
1240
1241 return S_OK;
1242}
1243
1244HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1245{
1246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1247
1248 *aParavirtProvider = mHWData->mParavirtProvider;
1249
1250 return S_OK;
1251}
1252
1253HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1254{
1255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1256
1257 HRESULT rc = i_checkStateDependency(MutableStateDep);
1258 if (FAILED(rc)) return rc;
1259
1260 if (aParavirtProvider != mHWData->mParavirtProvider)
1261 {
1262 i_setModified(IsModified_MachineData);
1263 mHWData.backup();
1264 mHWData->mParavirtProvider = aParavirtProvider;
1265 }
1266
1267 return S_OK;
1268}
1269
1270HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1271{
1272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1273
1274 *aParavirtProvider = mHWData->mParavirtProvider;
1275 switch (mHWData->mParavirtProvider)
1276 {
1277 case ParavirtProvider_None:
1278 case ParavirtProvider_HyperV:
1279 case ParavirtProvider_KVM:
1280 case ParavirtProvider_Minimal:
1281 break;
1282
1283 /* Resolve dynamic provider types to the effective types. */
1284 default:
1285 {
1286 ComObjPtr<GuestOSType> pGuestOSType;
1287 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1288 pGuestOSType);
1289 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1290
1291 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1292 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1293
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_Legacy:
1297 {
1298 if (fOsXGuest)
1299 *aParavirtProvider = ParavirtProvider_Minimal;
1300 else
1301 *aParavirtProvider = ParavirtProvider_None;
1302 break;
1303 }
1304
1305 case ParavirtProvider_Default:
1306 {
1307 if (fOsXGuest)
1308 *aParavirtProvider = ParavirtProvider_Minimal;
1309 else if ( mUserData->s.strOsType == "Windows10"
1310 || mUserData->s.strOsType == "Windows10_64"
1311 || mUserData->s.strOsType == "Windows81"
1312 || mUserData->s.strOsType == "Windows81_64"
1313 || mUserData->s.strOsType == "Windows8"
1314 || mUserData->s.strOsType == "Windows8_64"
1315 || mUserData->s.strOsType == "Windows7"
1316 || mUserData->s.strOsType == "Windows7_64"
1317 || mUserData->s.strOsType == "WindowsVista"
1318 || mUserData->s.strOsType == "WindowsVista_64"
1319 || mUserData->s.strOsType == "Windows2012"
1320 || mUserData->s.strOsType == "Windows2012_64"
1321 || mUserData->s.strOsType == "Windows2008"
1322 || mUserData->s.strOsType == "Windows2008_64")
1323 {
1324 *aParavirtProvider = ParavirtProvider_HyperV;
1325 }
1326 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1327 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1328 || mUserData->s.strOsType == "Linux"
1329 || mUserData->s.strOsType == "Linux_64"
1330 || mUserData->s.strOsType == "ArchLinux"
1331 || mUserData->s.strOsType == "ArchLinux_64"
1332 || mUserData->s.strOsType == "Debian"
1333 || mUserData->s.strOsType == "Debian_64"
1334 || mUserData->s.strOsType == "Fedora"
1335 || mUserData->s.strOsType == "Fedora_64"
1336 || mUserData->s.strOsType == "Gentoo"
1337 || mUserData->s.strOsType == "Gentoo_64"
1338 || mUserData->s.strOsType == "Mandriva"
1339 || mUserData->s.strOsType == "Mandriva_64"
1340 || mUserData->s.strOsType == "OpenSUSE"
1341 || mUserData->s.strOsType == "OpenSUSE_64"
1342 || mUserData->s.strOsType == "Oracle"
1343 || mUserData->s.strOsType == "Oracle_64"
1344 || mUserData->s.strOsType == "RedHat"
1345 || mUserData->s.strOsType == "RedHat_64"
1346 || mUserData->s.strOsType == "Turbolinux"
1347 || mUserData->s.strOsType == "Turbolinux_64"
1348 || mUserData->s.strOsType == "Ubuntu"
1349 || mUserData->s.strOsType == "Ubuntu_64"
1350 || mUserData->s.strOsType == "Xandros"
1351 || mUserData->s.strOsType == "Xandros_64")
1352 {
1353 *aParavirtProvider = ParavirtProvider_KVM;
1354 }
1355 else
1356 *aParavirtProvider = ParavirtProvider_None;
1357 break;
1358 }
1359
1360 default: AssertFailedBreak(); /* Shut up MSC. */
1361 }
1362 break;
1363 }
1364 }
1365
1366 Assert( *aParavirtProvider == ParavirtProvider_None
1367 || *aParavirtProvider == ParavirtProvider_Minimal
1368 || *aParavirtProvider == ParavirtProvider_HyperV
1369 || *aParavirtProvider == ParavirtProvider_KVM);
1370 return S_OK;
1371}
1372
1373HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1374{
1375 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1376
1377 aHardwareVersion = mHWData->mHWVersion;
1378
1379 return S_OK;
1380}
1381
1382HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1383{
1384 /* check known version */
1385 Utf8Str hwVersion = aHardwareVersion;
1386 if ( hwVersion.compare("1") != 0
1387 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1388 return setError(E_INVALIDARG,
1389 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1390
1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 HRESULT rc = i_checkStateDependency(MutableStateDep);
1394 if (FAILED(rc)) return rc;
1395
1396 i_setModified(IsModified_MachineData);
1397 mHWData.backup();
1398 mHWData->mHWVersion = aHardwareVersion;
1399
1400 return S_OK;
1401}
1402
1403HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1404{
1405 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1406
1407 if (!mHWData->mHardwareUUID.isZero())
1408 aHardwareUUID = mHWData->mHardwareUUID;
1409 else
1410 aHardwareUUID = mData->mUuid;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1416{
1417 if (!aHardwareUUID.isValid())
1418 return E_INVALIDARG;
1419
1420 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 HRESULT rc = i_checkStateDependency(MutableStateDep);
1423 if (FAILED(rc)) return rc;
1424
1425 i_setModified(IsModified_MachineData);
1426 mHWData.backup();
1427 if (aHardwareUUID == mData->mUuid)
1428 mHWData->mHardwareUUID.clear();
1429 else
1430 mHWData->mHardwareUUID = aHardwareUUID;
1431
1432 return S_OK;
1433}
1434
1435HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1436{
1437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1438
1439 *aMemorySize = mHWData->mMemorySize;
1440
1441 return S_OK;
1442}
1443
1444HRESULT Machine::setMemorySize(ULONG aMemorySize)
1445{
1446 /* check RAM limits */
1447 if ( aMemorySize < MM_RAM_MIN_IN_MB
1448 || aMemorySize > MM_RAM_MAX_IN_MB
1449 )
1450 return setError(E_INVALIDARG,
1451 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1452 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1453
1454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1455
1456 HRESULT rc = i_checkStateDependency(MutableStateDep);
1457 if (FAILED(rc)) return rc;
1458
1459 i_setModified(IsModified_MachineData);
1460 mHWData.backup();
1461 mHWData->mMemorySize = aMemorySize;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1467{
1468 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1469
1470 *aCPUCount = mHWData->mCPUCount;
1471
1472 return S_OK;
1473}
1474
1475HRESULT Machine::setCPUCount(ULONG aCPUCount)
1476{
1477 /* check CPU limits */
1478 if ( aCPUCount < SchemaDefs::MinCPUCount
1479 || aCPUCount > SchemaDefs::MaxCPUCount
1480 )
1481 return setError(E_INVALIDARG,
1482 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1483 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1484
1485 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1486
1487 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1488 if (mHWData->mCPUHotPlugEnabled)
1489 {
1490 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1491 {
1492 if (mHWData->mCPUAttached[idx])
1493 return setError(E_INVALIDARG,
1494 tr("There is still a CPU attached to socket %lu."
1495 "Detach the CPU before removing the socket"),
1496 aCPUCount, idx+1);
1497 }
1498 }
1499
1500 HRESULT rc = i_checkStateDependency(MutableStateDep);
1501 if (FAILED(rc)) return rc;
1502
1503 i_setModified(IsModified_MachineData);
1504 mHWData.backup();
1505 mHWData->mCPUCount = aCPUCount;
1506
1507 return S_OK;
1508}
1509
1510HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1511{
1512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1513
1514 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1515
1516 return S_OK;
1517}
1518
1519HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1520{
1521 HRESULT rc = S_OK;
1522
1523 /* check throttle limits */
1524 if ( aCPUExecutionCap < 1
1525 || aCPUExecutionCap > 100
1526 )
1527 return setError(E_INVALIDARG,
1528 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1529 aCPUExecutionCap, 1, 100);
1530
1531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1532
1533 alock.release();
1534 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1535 alock.acquire();
1536 if (FAILED(rc)) return rc;
1537
1538 i_setModified(IsModified_MachineData);
1539 mHWData.backup();
1540 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1541
1542 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1543 if (Global::IsOnline(mData->mMachineState))
1544 i_saveSettings(NULL);
1545
1546 return S_OK;
1547}
1548
1549HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1550{
1551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1552
1553 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1554
1555 return S_OK;
1556}
1557
1558HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1559{
1560 HRESULT rc = S_OK;
1561
1562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1563
1564 rc = i_checkStateDependency(MutableStateDep);
1565 if (FAILED(rc)) return rc;
1566
1567 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1568 {
1569 if (aCPUHotPlugEnabled)
1570 {
1571 i_setModified(IsModified_MachineData);
1572 mHWData.backup();
1573
1574 /* Add the amount of CPUs currently attached */
1575 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1576 mHWData->mCPUAttached[i] = true;
1577 }
1578 else
1579 {
1580 /*
1581 * We can disable hotplug only if the amount of maximum CPUs is equal
1582 * to the amount of attached CPUs
1583 */
1584 unsigned cCpusAttached = 0;
1585 unsigned iHighestId = 0;
1586
1587 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1588 {
1589 if (mHWData->mCPUAttached[i])
1590 {
1591 cCpusAttached++;
1592 iHighestId = i;
1593 }
1594 }
1595
1596 if ( (cCpusAttached != mHWData->mCPUCount)
1597 || (iHighestId >= mHWData->mCPUCount))
1598 return setError(E_INVALIDARG,
1599 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1600
1601 i_setModified(IsModified_MachineData);
1602 mHWData.backup();
1603 }
1604 }
1605
1606 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1607
1608 return rc;
1609}
1610
1611HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1612{
1613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1616
1617 return S_OK;
1618}
1619
1620HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1621{
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1625 if (SUCCEEDED(hrc))
1626 {
1627 i_setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1630 }
1631 return hrc;
1632}
1633
1634HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637 aCPUProfile = mHWData->mCpuProfile;
1638 return S_OK;
1639}
1640
1641HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1642{
1643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1644 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1645 if (SUCCEEDED(hrc))
1646 {
1647 i_setModified(IsModified_MachineData);
1648 mHWData.backup();
1649 /* Empty equals 'host'. */
1650 if (aCPUProfile.isNotEmpty())
1651 mHWData->mCpuProfile = aCPUProfile;
1652 else
1653 mHWData->mCpuProfile = "host";
1654 }
1655 return hrc;
1656}
1657
1658HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1659{
1660#ifdef VBOX_WITH_USB_CARDREADER
1661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1662
1663 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1664
1665 return S_OK;
1666#else
1667 NOREF(aEmulatedUSBCardReaderEnabled);
1668 return E_NOTIMPL;
1669#endif
1670}
1671
1672HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1673{
1674#ifdef VBOX_WITH_USB_CARDREADER
1675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1676
1677 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1678 if (FAILED(rc)) return rc;
1679
1680 i_setModified(IsModified_MachineData);
1681 mHWData.backup();
1682 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1683
1684 return S_OK;
1685#else
1686 NOREF(aEmulatedUSBCardReaderEnabled);
1687 return E_NOTIMPL;
1688#endif
1689}
1690
1691HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1692{
1693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 *aHPETEnabled = mHWData->mHPETEnabled;
1696
1697 return S_OK;
1698}
1699
1700HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1701{
1702 HRESULT rc = S_OK;
1703
1704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1705
1706 rc = i_checkStateDependency(MutableStateDep);
1707 if (FAILED(rc)) return rc;
1708
1709 i_setModified(IsModified_MachineData);
1710 mHWData.backup();
1711
1712 mHWData->mHPETEnabled = aHPETEnabled;
1713
1714 return rc;
1715}
1716
1717HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1718{
1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1722 return S_OK;
1723}
1724
1725HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1726{
1727 HRESULT rc = S_OK;
1728
1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1730
1731 i_setModified(IsModified_MachineData);
1732 mHWData.backup();
1733 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1734
1735 alock.release();
1736 rc = i_onVideoCaptureChange();
1737 alock.acquire();
1738 if (FAILED(rc))
1739 {
1740 /*
1741 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1742 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1743 * determine if it should start or stop capturing. Therefore we need to manually
1744 * undo change.
1745 */
1746 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1747 return rc;
1748 }
1749
1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1751 if (Global::IsOnline(mData->mMachineState))
1752 i_saveSettings(NULL);
1753
1754 return rc;
1755}
1756
1757HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1758{
1759 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1760 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1761 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1762 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1763 return S_OK;
1764}
1765
1766HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1767{
1768 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1769 bool fChanged = false;
1770
1771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1774 {
1775 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1776 {
1777 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1778 fChanged = true;
1779 }
1780 }
1781 if (fChanged)
1782 {
1783 alock.release();
1784 HRESULT rc = i_onVideoCaptureChange();
1785 alock.acquire();
1786 if (FAILED(rc)) return rc;
1787 i_setModified(IsModified_MachineData);
1788
1789 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1790 if (Global::IsOnline(mData->mMachineState))
1791 i_saveSettings(NULL);
1792 }
1793
1794 return S_OK;
1795}
1796
1797HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1798{
1799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1800 if (mHWData->mVideoCaptureFile.isEmpty())
1801 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1802 else
1803 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1804 return S_OK;
1805}
1806
1807HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1808{
1809 Utf8Str strFile(aVideoCaptureFile);
1810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1811
1812 if ( Global::IsOnline(mData->mMachineState)
1813 && mHWData->mVideoCaptureEnabled)
1814 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1815
1816 if (!RTPathStartsWithRoot(strFile.c_str()))
1817 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1818
1819 if (!strFile.isEmpty())
1820 {
1821 Utf8Str defaultFile;
1822 i_getDefaultVideoCaptureFile(defaultFile);
1823 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1824 strFile.setNull();
1825 }
1826
1827 i_setModified(IsModified_MachineData);
1828 mHWData.backup();
1829 mHWData->mVideoCaptureFile = strFile;
1830
1831 return S_OK;
1832}
1833
1834HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1835{
1836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1837 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1838 return S_OK;
1839}
1840
1841HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1842{
1843 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1844
1845 if ( Global::IsOnline(mData->mMachineState)
1846 && mHWData->mVideoCaptureEnabled)
1847 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1848
1849 i_setModified(IsModified_MachineData);
1850 mHWData.backup();
1851 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1852
1853 return S_OK;
1854}
1855
1856HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1857{
1858 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1859 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1860 return S_OK;
1861}
1862
1863HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1864{
1865 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 if ( Global::IsOnline(mData->mMachineState)
1868 && mHWData->mVideoCaptureEnabled)
1869 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1870
1871 i_setModified(IsModified_MachineData);
1872 mHWData.backup();
1873 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1874
1875 return S_OK;
1876}
1877
1878HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1879{
1880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1881 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1882 return S_OK;
1883}
1884
1885HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1886{
1887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1888
1889 if ( Global::IsOnline(mData->mMachineState)
1890 && mHWData->mVideoCaptureEnabled)
1891 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1892
1893 i_setModified(IsModified_MachineData);
1894 mHWData.backup();
1895 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1896
1897 return S_OK;
1898}
1899
1900HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1901{
1902 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1903 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1904 return S_OK;
1905}
1906
1907HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1908{
1909 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1910
1911 if ( Global::IsOnline(mData->mMachineState)
1912 && mHWData->mVideoCaptureEnabled)
1913 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1914
1915 i_setModified(IsModified_MachineData);
1916 mHWData.backup();
1917 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1918
1919 return S_OK;
1920}
1921
1922HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1923{
1924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1925 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1926 return S_OK;
1927}
1928
1929HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1930{
1931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1932
1933 if ( Global::IsOnline(mData->mMachineState)
1934 && mHWData->mVideoCaptureEnabled)
1935 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1936
1937 i_setModified(IsModified_MachineData);
1938 mHWData.backup();
1939 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1940
1941 return S_OK;
1942}
1943
1944HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1945{
1946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1947 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1948 return S_OK;
1949}
1950
1951HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1952{
1953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1954
1955 if ( Global::IsOnline(mData->mMachineState)
1956 && mHWData->mVideoCaptureEnabled)
1957 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1958
1959 i_setModified(IsModified_MachineData);
1960 mHWData.backup();
1961 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1962
1963 return S_OK;
1964}
1965
1966HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1967{
1968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1971 return S_OK;
1972}
1973
1974HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1975{
1976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1977
1978 if ( Global::IsOnline(mData->mMachineState)
1979 && mHWData->mVideoCaptureEnabled)
1980 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1981
1982 i_setModified(IsModified_MachineData);
1983 mHWData.backup();
1984 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1985
1986 return S_OK;
1987}
1988
1989HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1990{
1991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1994
1995 return S_OK;
1996}
1997
1998HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1999{
2000 switch (aGraphicsControllerType)
2001 {
2002 case GraphicsControllerType_Null:
2003 case GraphicsControllerType_VBoxVGA:
2004#ifdef VBOX_WITH_VMSVGA
2005 case GraphicsControllerType_VMSVGA:
2006#endif
2007 break;
2008 default:
2009 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2010 }
2011
2012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2013
2014 HRESULT rc = i_checkStateDependency(MutableStateDep);
2015 if (FAILED(rc)) return rc;
2016
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2020
2021 return S_OK;
2022}
2023
2024HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2025{
2026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2027
2028 *aVRAMSize = mHWData->mVRAMSize;
2029
2030 return S_OK;
2031}
2032
2033HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2034{
2035 /* check VRAM limits */
2036 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2037 return setError(E_INVALIDARG,
2038 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2039 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2040
2041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 HRESULT rc = i_checkStateDependency(MutableStateDep);
2044 if (FAILED(rc)) return rc;
2045
2046 i_setModified(IsModified_MachineData);
2047 mHWData.backup();
2048 mHWData->mVRAMSize = aVRAMSize;
2049
2050 return S_OK;
2051}
2052
2053/** @todo this method should not be public */
2054HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2055{
2056 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2059
2060 return S_OK;
2061}
2062
2063/**
2064 * Set the memory balloon size.
2065 *
2066 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2067 * we have to make sure that we never call IGuest from here.
2068 */
2069HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2070{
2071 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2072#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2073 /* check limits */
2074 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2075 return setError(E_INVALIDARG,
2076 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2077 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2078
2079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2080
2081 i_setModified(IsModified_MachineData);
2082 mHWData.backup();
2083 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2084
2085 return S_OK;
2086#else
2087 NOREF(aMemoryBalloonSize);
2088 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2089#endif
2090}
2091
2092HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2093{
2094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2097 return S_OK;
2098}
2099
2100HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2101{
2102#ifdef VBOX_WITH_PAGE_SHARING
2103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2104
2105 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2109 return S_OK;
2110#else
2111 NOREF(aPageFusionEnabled);
2112 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2113#endif
2114}
2115
2116HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2117{
2118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2121
2122 return S_OK;
2123}
2124
2125HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2126{
2127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2128
2129 HRESULT rc = i_checkStateDependency(MutableStateDep);
2130 if (FAILED(rc)) return rc;
2131
2132 /** @todo check validity! */
2133
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2137
2138 return S_OK;
2139}
2140
2141
2142HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2143{
2144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2145
2146 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2147
2148 return S_OK;
2149}
2150
2151HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2152{
2153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 HRESULT rc = i_checkStateDependency(MutableStateDep);
2156 if (FAILED(rc)) return rc;
2157
2158 /** @todo check validity! */
2159 i_setModified(IsModified_MachineData);
2160 mHWData.backup();
2161 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2167{
2168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 *aMonitorCount = mHWData->mMonitorCount;
2171
2172 return S_OK;
2173}
2174
2175HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2176{
2177 /* make sure monitor count is a sensible number */
2178 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2179 return setError(E_INVALIDARG,
2180 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2181 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2182
2183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 HRESULT rc = i_checkStateDependency(MutableStateDep);
2186 if (FAILED(rc)) return rc;
2187
2188 i_setModified(IsModified_MachineData);
2189 mHWData.backup();
2190 mHWData->mMonitorCount = aMonitorCount;
2191
2192 return S_OK;
2193}
2194
2195HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2196{
2197 /* mBIOSSettings is constant during life time, no need to lock */
2198 aBIOSSettings = mBIOSSettings;
2199
2200 return S_OK;
2201}
2202
2203HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2204{
2205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2206
2207 switch (aProperty)
2208 {
2209 case CPUPropertyType_PAE:
2210 *aValue = mHWData->mPAEEnabled;
2211 break;
2212
2213 case CPUPropertyType_LongMode:
2214 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2215 *aValue = TRUE;
2216 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2217 *aValue = FALSE;
2218#if HC_ARCH_BITS == 64
2219 else
2220 *aValue = TRUE;
2221#else
2222 else
2223 {
2224 *aValue = FALSE;
2225
2226 ComObjPtr<GuestOSType> pGuestOSType;
2227 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2228 pGuestOSType);
2229 if (SUCCEEDED(hrc2))
2230 {
2231 if (pGuestOSType->i_is64Bit())
2232 {
2233 ComObjPtr<Host> pHost = mParent->i_host();
2234 alock.release();
2235
2236 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2237 if (FAILED(hrc2))
2238 *aValue = FALSE;
2239 }
2240 }
2241 }
2242#endif
2243 break;
2244
2245 case CPUPropertyType_TripleFaultReset:
2246 *aValue = mHWData->mTripleFaultReset;
2247 break;
2248
2249 case CPUPropertyType_APIC:
2250 *aValue = mHWData->mAPIC;
2251 break;
2252
2253 case CPUPropertyType_X2APIC:
2254 *aValue = mHWData->mX2APIC;
2255 break;
2256
2257 default:
2258 return E_INVALIDARG;
2259 }
2260 return S_OK;
2261}
2262
2263HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2264{
2265 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2266
2267 HRESULT rc = i_checkStateDependency(MutableStateDep);
2268 if (FAILED(rc)) return rc;
2269
2270 switch (aProperty)
2271 {
2272 case CPUPropertyType_PAE:
2273 i_setModified(IsModified_MachineData);
2274 mHWData.backup();
2275 mHWData->mPAEEnabled = !!aValue;
2276 break;
2277
2278 case CPUPropertyType_LongMode:
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2282 break;
2283
2284 case CPUPropertyType_TripleFaultReset:
2285 i_setModified(IsModified_MachineData);
2286 mHWData.backup();
2287 mHWData->mTripleFaultReset = !!aValue;
2288 break;
2289
2290 case CPUPropertyType_APIC:
2291 if (mHWData->mX2APIC)
2292 aValue = TRUE;
2293 i_setModified(IsModified_MachineData);
2294 mHWData.backup();
2295 mHWData->mAPIC = !!aValue;
2296 break;
2297
2298 case CPUPropertyType_X2APIC:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mX2APIC = !!aValue;
2302 if (aValue)
2303 mHWData->mAPIC = !!aValue;
2304 break;
2305
2306 default:
2307 return E_INVALIDARG;
2308 }
2309 return S_OK;
2310}
2311
2312HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2313 ULONG *aValEcx, ULONG *aValEdx)
2314{
2315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2316 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2317 {
2318 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2319 it != mHWData->mCpuIdLeafList.end();
2320 ++it)
2321 {
2322 if (aOrdinal == 0)
2323 {
2324 const settings::CpuIdLeaf &rLeaf= *it;
2325 *aIdx = rLeaf.idx;
2326 *aSubIdx = rLeaf.idxSub;
2327 *aValEax = rLeaf.uEax;
2328 *aValEbx = rLeaf.uEbx;
2329 *aValEcx = rLeaf.uEcx;
2330 *aValEdx = rLeaf.uEdx;
2331 return S_OK;
2332 }
2333 aOrdinal--;
2334 }
2335 }
2336 return E_INVALIDARG;
2337}
2338
2339HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2340{
2341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2342
2343 /*
2344 * Search the list.
2345 */
2346 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2347 {
2348 const settings::CpuIdLeaf &rLeaf= *it;
2349 if ( rLeaf.idx == aIdx
2350 && ( aSubIdx == UINT32_MAX
2351 || rLeaf.idxSub == aSubIdx) )
2352 {
2353 *aValEax = rLeaf.uEax;
2354 *aValEbx = rLeaf.uEbx;
2355 *aValEcx = rLeaf.uEcx;
2356 *aValEdx = rLeaf.uEdx;
2357 return S_OK;
2358 }
2359 }
2360
2361 return E_INVALIDARG;
2362}
2363
2364
2365HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2366{
2367 /*
2368 * Validate input before taking locks and checking state.
2369 */
2370 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2371 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2372 if ( aIdx >= UINT32_C(0x20)
2373 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2374 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2375 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2376
2377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2378 HRESULT rc = i_checkStateDependency(MutableStateDep);
2379 if (FAILED(rc)) return rc;
2380
2381 /*
2382 * Impose a maximum number of leaves.
2383 */
2384 if (mHWData->mCpuIdLeafList.size() > 256)
2385 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2386
2387 /*
2388 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2389 */
2390 i_setModified(IsModified_MachineData);
2391 mHWData.backup();
2392
2393 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2394 {
2395 settings::CpuIdLeaf &rLeaf= *it;
2396 if ( rLeaf.idx == aIdx
2397 && ( aSubIdx == UINT32_MAX
2398 || rLeaf.idxSub == aSubIdx) )
2399 it = mHWData->mCpuIdLeafList.erase(it);
2400 else
2401 ++it;
2402 }
2403
2404 settings::CpuIdLeaf NewLeaf;
2405 NewLeaf.idx = aIdx;
2406 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2407 NewLeaf.uEax = aValEax;
2408 NewLeaf.uEbx = aValEbx;
2409 NewLeaf.uEcx = aValEcx;
2410 NewLeaf.uEdx = aValEdx;
2411 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2412 return S_OK;
2413}
2414
2415HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2416{
2417 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2418
2419 HRESULT rc = i_checkStateDependency(MutableStateDep);
2420 if (FAILED(rc)) return rc;
2421
2422 /*
2423 * Do the removal.
2424 */
2425 bool fModified = false;
2426 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2427 {
2428 settings::CpuIdLeaf &rLeaf= *it;
2429 if ( rLeaf.idx == aIdx
2430 && ( aSubIdx == UINT32_MAX
2431 || rLeaf.idxSub == aSubIdx) )
2432 {
2433 if (!fModified)
2434 {
2435 fModified = true;
2436 i_setModified(IsModified_MachineData);
2437 mHWData.backup();
2438 }
2439 it = mHWData->mCpuIdLeafList.erase(it);
2440 }
2441 else
2442 ++it;
2443 }
2444
2445 return S_OK;
2446}
2447
2448HRESULT Machine::removeAllCPUIDLeaves()
2449{
2450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2451
2452 HRESULT rc = i_checkStateDependency(MutableStateDep);
2453 if (FAILED(rc)) return rc;
2454
2455 if (mHWData->mCpuIdLeafList.size() > 0)
2456 {
2457 i_setModified(IsModified_MachineData);
2458 mHWData.backup();
2459
2460 mHWData->mCpuIdLeafList.clear();
2461 }
2462
2463 return S_OK;
2464}
2465HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2466{
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 switch(aProperty)
2470 {
2471 case HWVirtExPropertyType_Enabled:
2472 *aValue = mHWData->mHWVirtExEnabled;
2473 break;
2474
2475 case HWVirtExPropertyType_VPID:
2476 *aValue = mHWData->mHWVirtExVPIDEnabled;
2477 break;
2478
2479 case HWVirtExPropertyType_NestedPaging:
2480 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2481 break;
2482
2483 case HWVirtExPropertyType_UnrestrictedExecution:
2484 *aValue = mHWData->mHWVirtExUXEnabled;
2485 break;
2486
2487 case HWVirtExPropertyType_LargePages:
2488 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2489#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2490 *aValue = FALSE;
2491#endif
2492 break;
2493
2494 case HWVirtExPropertyType_Force:
2495 *aValue = mHWData->mHWVirtExForceEnabled;
2496 break;
2497
2498 default:
2499 return E_INVALIDARG;
2500 }
2501 return S_OK;
2502}
2503
2504HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2505{
2506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 switch(aProperty)
2512 {
2513 case HWVirtExPropertyType_Enabled:
2514 i_setModified(IsModified_MachineData);
2515 mHWData.backup();
2516 mHWData->mHWVirtExEnabled = !!aValue;
2517 break;
2518
2519 case HWVirtExPropertyType_VPID:
2520 i_setModified(IsModified_MachineData);
2521 mHWData.backup();
2522 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2523 break;
2524
2525 case HWVirtExPropertyType_NestedPaging:
2526 i_setModified(IsModified_MachineData);
2527 mHWData.backup();
2528 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2529 break;
2530
2531 case HWVirtExPropertyType_UnrestrictedExecution:
2532 i_setModified(IsModified_MachineData);
2533 mHWData.backup();
2534 mHWData->mHWVirtExUXEnabled = !!aValue;
2535 break;
2536
2537 case HWVirtExPropertyType_LargePages:
2538 i_setModified(IsModified_MachineData);
2539 mHWData.backup();
2540 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2541 break;
2542
2543 case HWVirtExPropertyType_Force:
2544 i_setModified(IsModified_MachineData);
2545 mHWData.backup();
2546 mHWData->mHWVirtExForceEnabled = !!aValue;
2547 break;
2548
2549 default:
2550 return E_INVALIDARG;
2551 }
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2566{
2567 /** @todo (r=dmik):
2568 * 1. Allow to change the name of the snapshot folder containing snapshots
2569 * 2. Rename the folder on disk instead of just changing the property
2570 * value (to be smart and not to leave garbage). Note that it cannot be
2571 * done here because the change may be rolled back. Thus, the right
2572 * place is #saveSettings().
2573 */
2574
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = i_checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 if (!mData->mCurrentSnapshot.isNull())
2581 return setError(E_FAIL,
2582 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2583
2584 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2585
2586 if (strSnapshotFolder.isEmpty())
2587 strSnapshotFolder = "Snapshots";
2588 int vrc = i_calculateFullPath(strSnapshotFolder,
2589 strSnapshotFolder);
2590 if (RT_FAILURE(vrc))
2591 return setError(E_FAIL,
2592 tr("Invalid snapshot folder '%s' (%Rrc)"),
2593 strSnapshotFolder.c_str(), vrc);
2594
2595 i_setModified(IsModified_MachineData);
2596 mUserData.backup();
2597
2598 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2599
2600 return S_OK;
2601}
2602
2603HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2604{
2605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2606
2607 aMediumAttachments.resize(mMediumAttachments->size());
2608 size_t i = 0;
2609 for (MediumAttachmentList::const_iterator
2610 it = mMediumAttachments->begin();
2611 it != mMediumAttachments->end();
2612 ++it, ++i)
2613 aMediumAttachments[i] = *it;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 Assert(!!mVRDEServer);
2623
2624 aVRDEServer = mVRDEServer;
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2630{
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 aAudioAdapter = mAudioAdapter;
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2639{
2640#ifdef VBOX_WITH_VUSB
2641 clearError();
2642 MultiResult rc(S_OK);
2643
2644# ifdef VBOX_WITH_USB
2645 rc = mParent->i_host()->i_checkUSBProxyService();
2646 if (FAILED(rc)) return rc;
2647# endif
2648
2649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2650
2651 aUSBControllers.resize(mUSBControllers->size());
2652 size_t i = 0;
2653 for (USBControllerList::const_iterator
2654 it = mUSBControllers->begin();
2655 it != mUSBControllers->end();
2656 ++it, ++i)
2657 aUSBControllers[i] = *it;
2658
2659 return S_OK;
2660#else
2661 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2662 * extended error info to indicate that USB is simply not available
2663 * (w/o treating it as a failure), for example, as in OSE */
2664 NOREF(aUSBControllers);
2665 ReturnComNotImplemented();
2666#endif /* VBOX_WITH_VUSB */
2667}
2668
2669HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2670{
2671#ifdef VBOX_WITH_VUSB
2672 clearError();
2673 MultiResult rc(S_OK);
2674
2675# ifdef VBOX_WITH_USB
2676 rc = mParent->i_host()->i_checkUSBProxyService();
2677 if (FAILED(rc)) return rc;
2678# endif
2679
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 aUSBDeviceFilters = mUSBDeviceFilters;
2683 return rc;
2684#else
2685 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2686 * extended error info to indicate that USB is simply not available
2687 * (w/o treating it as a failure), for example, as in OSE */
2688 NOREF(aUSBDeviceFilters);
2689 ReturnComNotImplemented();
2690#endif /* VBOX_WITH_VUSB */
2691}
2692
2693HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 aSettingsFilePath = mData->m_strConfigFileFull;
2698
2699 return S_OK;
2700}
2701
2702HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2703{
2704 RT_NOREF(aSettingsFilePath);
2705 ReturnComNotImplemented();
2706}
2707
2708HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2713 if (FAILED(rc)) return rc;
2714
2715 if (!mData->pMachineConfigFile->fileExists())
2716 // this is a new machine, and no config file exists yet:
2717 *aSettingsModified = TRUE;
2718 else
2719 *aSettingsModified = (mData->flModifications != 0);
2720
2721 return S_OK;
2722}
2723
2724HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2725{
2726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2727
2728 *aSessionState = mData->mSession.mState;
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2734{
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 aSessionName = mData->mSession.mName;
2738
2739 return S_OK;
2740}
2741
2742HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2743{
2744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 *aSessionPID = mData->mSession.mPID;
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::getState(MachineState_T *aState)
2752{
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 *aState = mData->mMachineState;
2756 Assert(mData->mMachineState != MachineState_Null);
2757
2758 return S_OK;
2759}
2760
2761HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2762{
2763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2764
2765 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aStateFilePath = mSSData->strStateFilePath;
2775
2776 return S_OK;
2777}
2778
2779HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2780{
2781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2782
2783 i_getLogFolder(aLogFolder);
2784
2785 return S_OK;
2786}
2787
2788HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2789{
2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2791
2792 aCurrentSnapshot = mData->mCurrentSnapshot;
2793
2794 return S_OK;
2795}
2796
2797HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2798{
2799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2800
2801 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2802 ? 0
2803 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 /* Note: for machines with no snapshots, we always return FALSE
2813 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2814 * reasons :) */
2815
2816 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2817 ? FALSE
2818 : mData->mCurrentStateModified;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aSharedFolders.resize(mHWData->mSharedFolders.size());
2828 size_t i = 0;
2829 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2830 it = mHWData->mSharedFolders.begin();
2831 it != mHWData->mSharedFolders.end();
2832 ++it, ++i)
2833 aSharedFolders[i] = *it;
2834
2835 return S_OK;
2836}
2837
2838HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2839{
2840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2841
2842 *aClipboardMode = mHWData->mClipboardMode;
2843
2844 return S_OK;
2845}
2846
2847HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2848{
2849 HRESULT rc = S_OK;
2850
2851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2852
2853 alock.release();
2854 rc = i_onClipboardModeChange(aClipboardMode);
2855 alock.acquire();
2856 if (FAILED(rc)) return rc;
2857
2858 i_setModified(IsModified_MachineData);
2859 mHWData.backup();
2860 mHWData->mClipboardMode = aClipboardMode;
2861
2862 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2863 if (Global::IsOnline(mData->mMachineState))
2864 i_saveSettings(NULL);
2865
2866 return S_OK;
2867}
2868
2869HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aDnDMode = mHWData->mDnDMode;
2874
2875 return S_OK;
2876}
2877
2878HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2879{
2880 HRESULT rc = S_OK;
2881
2882 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2883
2884 alock.release();
2885 rc = i_onDnDModeChange(aDnDMode);
2886
2887 alock.acquire();
2888 if (FAILED(rc)) return rc;
2889
2890 i_setModified(IsModified_MachineData);
2891 mHWData.backup();
2892 mHWData->mDnDMode = aDnDMode;
2893
2894 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2895 if (Global::IsOnline(mData->mMachineState))
2896 i_saveSettings(NULL);
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2902{
2903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2904
2905 aStorageControllers.resize(mStorageControllers->size());
2906 size_t i = 0;
2907 for (StorageControllerList::const_iterator
2908 it = mStorageControllers->begin();
2909 it != mStorageControllers->end();
2910 ++it, ++i)
2911 aStorageControllers[i] = *it;
2912
2913 return S_OK;
2914}
2915
2916HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2917{
2918 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2919
2920 *aEnabled = mUserData->s.fTeleporterEnabled;
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2926{
2927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 /* Only allow it to be set to true when PoweredOff or Aborted.
2930 (Clearing it is always permitted.) */
2931 if ( aTeleporterEnabled
2932 && mData->mRegistered
2933 && ( !i_isSessionMachine()
2934 || ( mData->mMachineState != MachineState_PoweredOff
2935 && mData->mMachineState != MachineState_Teleported
2936 && mData->mMachineState != MachineState_Aborted
2937 )
2938 )
2939 )
2940 return setError(VBOX_E_INVALID_VM_STATE,
2941 tr("The machine is not powered off (state is %s)"),
2942 Global::stringifyMachineState(mData->mMachineState));
2943
2944 i_setModified(IsModified_MachineData);
2945 mUserData.backup();
2946 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2961{
2962 if (aTeleporterPort >= _64K)
2963 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2964
2965 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2966
2967 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2968 if (FAILED(rc)) return rc;
2969
2970 i_setModified(IsModified_MachineData);
2971 mUserData.backup();
2972 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2973
2974 return S_OK;
2975}
2976
2977HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2978{
2979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2980
2981 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2987{
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3004
3005 return S_OK;
3006}
3007
3008HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3009{
3010 /*
3011 * Hash the password first.
3012 */
3013 com::Utf8Str aT = aTeleporterPassword;
3014
3015 if (!aT.isEmpty())
3016 {
3017 if (VBoxIsPasswordHashed(&aT))
3018 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3019 VBoxHashPassword(&aT);
3020 }
3021
3022 /*
3023 * Do the update.
3024 */
3025 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3026 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3027 if (SUCCEEDED(hrc))
3028 {
3029 i_setModified(IsModified_MachineData);
3030 mUserData.backup();
3031 mUserData->s.strTeleporterPassword = aT;
3032 }
3033
3034 return hrc;
3035}
3036
3037HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3038{
3039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3040
3041 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3042 return S_OK;
3043}
3044
3045HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3046{
3047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3048
3049 /** @todo deal with running state change. */
3050 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3051 if (FAILED(rc)) return rc;
3052
3053 i_setModified(IsModified_MachineData);
3054 mUserData.backup();
3055 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3056 return S_OK;
3057}
3058
3059HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3060{
3061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3062
3063 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3064 return S_OK;
3065}
3066
3067HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3068{
3069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3070
3071 /** @todo deal with running state change. */
3072 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3073 if (FAILED(rc)) return rc;
3074
3075 i_setModified(IsModified_MachineData);
3076 mUserData.backup();
3077 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3078 return S_OK;
3079}
3080
3081HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3082{
3083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3084
3085 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3086 return S_OK;
3087}
3088
3089HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3090{
3091 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3092
3093 /** @todo deal with running state change. */
3094 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3095 if (FAILED(rc)) return rc;
3096
3097 i_setModified(IsModified_MachineData);
3098 mUserData.backup();
3099 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3100 return S_OK;
3101}
3102
3103HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3104{
3105 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3106
3107 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3108
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3113{
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 /** @todo deal with running state change. */
3117 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3123
3124 return S_OK;
3125}
3126
3127HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3128{
3129 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3130
3131 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3132 return S_OK;
3133}
3134
3135HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3136{
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /** @todo deal with running state change. */
3140 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 i_setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3146 return S_OK;
3147}
3148
3149HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3150{
3151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3152
3153 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3154
3155 return S_OK;
3156}
3157
3158HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3159{
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /* Only allow it to be set to true when PoweredOff or Aborted.
3163 (Clearing it is always permitted.) */
3164 if ( aRTCUseUTC
3165 && mData->mRegistered
3166 && ( !i_isSessionMachine()
3167 || ( mData->mMachineState != MachineState_PoweredOff
3168 && mData->mMachineState != MachineState_Teleported
3169 && mData->mMachineState != MachineState_Aborted
3170 )
3171 )
3172 )
3173 return setError(VBOX_E_INVALID_VM_STATE,
3174 tr("The machine is not powered off (state is %s)"),
3175 Global::stringifyMachineState(mData->mMachineState));
3176
3177 i_setModified(IsModified_MachineData);
3178 mUserData.backup();
3179 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3180
3181 return S_OK;
3182}
3183
3184HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3185{
3186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3189
3190 return S_OK;
3191}
3192
3193HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3194{
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 HRESULT rc = i_checkStateDependency(MutableStateDep);
3198 if (FAILED(rc)) return rc;
3199
3200 i_setModified(IsModified_MachineData);
3201 mHWData.backup();
3202 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3208{
3209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 *aIOCacheSize = mHWData->mIOCacheSize;
3212
3213 return S_OK;
3214}
3215
3216HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3217{
3218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 HRESULT rc = i_checkStateDependency(MutableStateDep);
3221 if (FAILED(rc)) return rc;
3222
3223 i_setModified(IsModified_MachineData);
3224 mHWData.backup();
3225 mHWData->mIOCacheSize = aIOCacheSize;
3226
3227 return S_OK;
3228}
3229
3230
3231/**
3232 * @note Locks objects!
3233 */
3234HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3235 LockType_T aLockType)
3236{
3237 /* check the session state */
3238 SessionState_T state;
3239 HRESULT rc = aSession->COMGETTER(State)(&state);
3240 if (FAILED(rc)) return rc;
3241
3242 if (state != SessionState_Unlocked)
3243 return setError(VBOX_E_INVALID_OBJECT_STATE,
3244 tr("The given session is busy"));
3245
3246 // get the client's IInternalSessionControl interface
3247 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3248 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3249 E_INVALIDARG);
3250
3251 // session name (only used in some code paths)
3252 Utf8Str strSessionName;
3253
3254 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3255
3256 if (!mData->mRegistered)
3257 return setError(E_UNEXPECTED,
3258 tr("The machine '%s' is not registered"),
3259 mUserData->s.strName.c_str());
3260
3261 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3262
3263 SessionState_T oldState = mData->mSession.mState;
3264 /* Hack: in case the session is closing and there is a progress object
3265 * which allows waiting for the session to be closed, take the opportunity
3266 * and do a limited wait (max. 1 second). This helps a lot when the system
3267 * is busy and thus session closing can take a little while. */
3268 if ( mData->mSession.mState == SessionState_Unlocking
3269 && mData->mSession.mProgress)
3270 {
3271 alock.release();
3272 mData->mSession.mProgress->WaitForCompletion(1000);
3273 alock.acquire();
3274 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3275 }
3276
3277 // try again now
3278 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3279 // (i.e. session machine exists)
3280 && (aLockType == LockType_Shared) // caller wants a shared link to the
3281 // existing session that holds the write lock:
3282 )
3283 {
3284 // OK, share the session... we are now dealing with three processes:
3285 // 1) VBoxSVC (where this code runs);
3286 // 2) process C: the caller's client process (who wants a shared session);
3287 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3288
3289 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3290 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3291 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3292 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3293 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3294
3295 /*
3296 * Release the lock before calling the client process. It's safe here
3297 * since the only thing to do after we get the lock again is to add
3298 * the remote control to the list (which doesn't directly influence
3299 * anything).
3300 */
3301 alock.release();
3302
3303 // get the console of the session holding the write lock (this is a remote call)
3304 ComPtr<IConsole> pConsoleW;
3305 if (mData->mSession.mLockType == LockType_VM)
3306 {
3307 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3308 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3309 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3310 if (FAILED(rc))
3311 // the failure may occur w/o any error info (from RPC), so provide one
3312 return setError(VBOX_E_VM_ERROR,
3313 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3314 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3315 }
3316
3317 // share the session machine and W's console with the caller's session
3318 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3319 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3320 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3321
3322 if (FAILED(rc))
3323 // the failure may occur w/o any error info (from RPC), so provide one
3324 return setError(VBOX_E_VM_ERROR,
3325 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3326 alock.acquire();
3327
3328 // need to revalidate the state after acquiring the lock again
3329 if (mData->mSession.mState != SessionState_Locked)
3330 {
3331 pSessionControl->Uninitialize();
3332 return setError(VBOX_E_INVALID_SESSION_STATE,
3333 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3334 mUserData->s.strName.c_str());
3335 }
3336
3337 // add the caller's session to the list
3338 mData->mSession.mRemoteControls.push_back(pSessionControl);
3339 }
3340 else if ( mData->mSession.mState == SessionState_Locked
3341 || mData->mSession.mState == SessionState_Unlocking
3342 )
3343 {
3344 // sharing not permitted, or machine still unlocking:
3345 return setError(VBOX_E_INVALID_OBJECT_STATE,
3346 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3347 mUserData->s.strName.c_str());
3348 }
3349 else
3350 {
3351 // machine is not locked: then write-lock the machine (create the session machine)
3352
3353 // must not be busy
3354 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3355
3356 // get the caller's session PID
3357 RTPROCESS pid = NIL_RTPROCESS;
3358 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3359 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3360 Assert(pid != NIL_RTPROCESS);
3361
3362 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3363
3364 if (fLaunchingVMProcess)
3365 {
3366 if (mData->mSession.mPID == NIL_RTPROCESS)
3367 {
3368 // two or more clients racing for a lock, the one which set the
3369 // session state to Spawning will win, the others will get an
3370 // error as we can't decide here if waiting a little would help
3371 // (only for shared locks this would avoid an error)
3372 return setError(VBOX_E_INVALID_OBJECT_STATE,
3373 tr("The machine '%s' already has a lock request pending"),
3374 mUserData->s.strName.c_str());
3375 }
3376
3377 // this machine is awaiting for a spawning session to be opened:
3378 // then the calling process must be the one that got started by
3379 // LaunchVMProcess()
3380
3381 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3382 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3383
3384#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3385 /* Hardened windows builds spawns three processes when a VM is
3386 launched, the 3rd one is the one that will end up here. */
3387 RTPROCESS ppid;
3388 int rc = RTProcQueryParent(pid, &ppid);
3389 if (RT_SUCCESS(rc))
3390 rc = RTProcQueryParent(ppid, &ppid);
3391 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3392 || rc == VERR_ACCESS_DENIED)
3393 {
3394 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3395 mData->mSession.mPID = pid;
3396 }
3397#endif
3398
3399 if (mData->mSession.mPID != pid)
3400 return setError(E_ACCESSDENIED,
3401 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3402 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3403 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3404 }
3405
3406 // create the mutable SessionMachine from the current machine
3407 ComObjPtr<SessionMachine> sessionMachine;
3408 sessionMachine.createObject();
3409 rc = sessionMachine->init(this);
3410 AssertComRC(rc);
3411
3412 /* NOTE: doing return from this function after this point but
3413 * before the end is forbidden since it may call SessionMachine::uninit()
3414 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3415 * lock while still holding the Machine lock in alock so that a deadlock
3416 * is possible due to the wrong lock order. */
3417
3418 if (SUCCEEDED(rc))
3419 {
3420 /*
3421 * Set the session state to Spawning to protect against subsequent
3422 * attempts to open a session and to unregister the machine after
3423 * we release the lock.
3424 */
3425 SessionState_T origState = mData->mSession.mState;
3426 mData->mSession.mState = SessionState_Spawning;
3427
3428#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3429 /* Get the client token ID to be passed to the client process */
3430 Utf8Str strTokenId;
3431 sessionMachine->i_getTokenId(strTokenId);
3432 Assert(!strTokenId.isEmpty());
3433#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3434 /* Get the client token to be passed to the client process */
3435 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3436 /* The token is now "owned" by pToken, fix refcount */
3437 if (!pToken.isNull())
3438 pToken->Release();
3439#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3440
3441 /*
3442 * Release the lock before calling the client process -- it will call
3443 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3444 * because the state is Spawning, so that LaunchVMProcess() and
3445 * LockMachine() calls will fail. This method, called before we
3446 * acquire the lock again, will fail because of the wrong PID.
3447 *
3448 * Note that mData->mSession.mRemoteControls accessed outside
3449 * the lock may not be modified when state is Spawning, so it's safe.
3450 */
3451 alock.release();
3452
3453 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3454#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3455 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3457 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3458 /* Now the token is owned by the client process. */
3459 pToken.setNull();
3460#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3461 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3462
3463 /* The failure may occur w/o any error info (from RPC), so provide one */
3464 if (FAILED(rc))
3465 setError(VBOX_E_VM_ERROR,
3466 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3467
3468 // get session name, either to remember or to compare against
3469 // the already known session name.
3470 {
3471 Bstr bstrSessionName;
3472 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3473 if (SUCCEEDED(rc2))
3474 strSessionName = bstrSessionName;
3475 }
3476
3477 if ( SUCCEEDED(rc)
3478 && fLaunchingVMProcess
3479 )
3480 {
3481 /* complete the remote session initialization */
3482
3483 /* get the console from the direct session */
3484 ComPtr<IConsole> console;
3485 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3486 ComAssertComRC(rc);
3487
3488 if (SUCCEEDED(rc) && !console)
3489 {
3490 ComAssert(!!console);
3491 rc = E_FAIL;
3492 }
3493
3494 /* assign machine & console to the remote session */
3495 if (SUCCEEDED(rc))
3496 {
3497 /*
3498 * after LaunchVMProcess(), the first and the only
3499 * entry in remoteControls is that remote session
3500 */
3501 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3502 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3503 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3504
3505 /* The failure may occur w/o any error info (from RPC), so provide one */
3506 if (FAILED(rc))
3507 setError(VBOX_E_VM_ERROR,
3508 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3509 }
3510
3511 if (FAILED(rc))
3512 pSessionControl->Uninitialize();
3513 }
3514
3515 /* acquire the lock again */
3516 alock.acquire();
3517
3518 /* Restore the session state */
3519 mData->mSession.mState = origState;
3520 }
3521
3522 // finalize spawning anyway (this is why we don't return on errors above)
3523 if (fLaunchingVMProcess)
3524 {
3525 Assert(mData->mSession.mName == strSessionName);
3526 /* Note that the progress object is finalized later */
3527 /** @todo Consider checking mData->mSession.mProgress for cancellation
3528 * around here. */
3529
3530 /* We don't reset mSession.mPID here because it is necessary for
3531 * SessionMachine::uninit() to reap the child process later. */
3532
3533 if (FAILED(rc))
3534 {
3535 /* Close the remote session, remove the remote control from the list
3536 * and reset session state to Closed (@note keep the code in sync
3537 * with the relevant part in checkForSpawnFailure()). */
3538
3539 Assert(mData->mSession.mRemoteControls.size() == 1);
3540 if (mData->mSession.mRemoteControls.size() == 1)
3541 {
3542 ErrorInfoKeeper eik;
3543 mData->mSession.mRemoteControls.front()->Uninitialize();
3544 }
3545
3546 mData->mSession.mRemoteControls.clear();
3547 mData->mSession.mState = SessionState_Unlocked;
3548 }
3549 }
3550 else
3551 {
3552 /* memorize PID of the directly opened session */
3553 if (SUCCEEDED(rc))
3554 mData->mSession.mPID = pid;
3555 }
3556
3557 if (SUCCEEDED(rc))
3558 {
3559 mData->mSession.mLockType = aLockType;
3560 /* memorize the direct session control and cache IUnknown for it */
3561 mData->mSession.mDirectControl = pSessionControl;
3562 mData->mSession.mState = SessionState_Locked;
3563 if (!fLaunchingVMProcess)
3564 mData->mSession.mName = strSessionName;
3565 /* associate the SessionMachine with this Machine */
3566 mData->mSession.mMachine = sessionMachine;
3567
3568 /* request an IUnknown pointer early from the remote party for later
3569 * identity checks (it will be internally cached within mDirectControl
3570 * at least on XPCOM) */
3571 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3572 NOREF(unk);
3573 }
3574
3575 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3576 * would break the lock order */
3577 alock.release();
3578
3579 /* uninitialize the created session machine on failure */
3580 if (FAILED(rc))
3581 sessionMachine->uninit();
3582 }
3583
3584 if (SUCCEEDED(rc))
3585 {
3586 /*
3587 * tell the client watcher thread to update the set of
3588 * machines that have open sessions
3589 */
3590 mParent->i_updateClientWatcher();
3591
3592 if (oldState != SessionState_Locked)
3593 /* fire an event */
3594 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3595 }
3596
3597 return rc;
3598}
3599
3600/**
3601 * @note Locks objects!
3602 */
3603HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3604 const com::Utf8Str &aName,
3605 const com::Utf8Str &aEnvironment,
3606 ComPtr<IProgress> &aProgress)
3607{
3608 Utf8Str strFrontend(aName);
3609 /* "emergencystop" doesn't need the session, so skip the checks/interface
3610 * retrieval. This code doesn't quite fit in here, but introducing a
3611 * special API method would be even more effort, and would require explicit
3612 * support by every API client. It's better to hide the feature a bit. */
3613 if (strFrontend != "emergencystop")
3614 CheckComArgNotNull(aSession);
3615
3616 HRESULT rc = S_OK;
3617 if (strFrontend.isEmpty())
3618 {
3619 Bstr bstrFrontend;
3620 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3621 if (FAILED(rc))
3622 return rc;
3623 strFrontend = bstrFrontend;
3624 if (strFrontend.isEmpty())
3625 {
3626 ComPtr<ISystemProperties> systemProperties;
3627 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3628 if (FAILED(rc))
3629 return rc;
3630 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3631 if (FAILED(rc))
3632 return rc;
3633 strFrontend = bstrFrontend;
3634 }
3635 /* paranoia - emergencystop is not a valid default */
3636 if (strFrontend == "emergencystop")
3637 strFrontend = Utf8Str::Empty;
3638 }
3639 /* default frontend: Qt GUI */
3640 if (strFrontend.isEmpty())
3641 strFrontend = "GUI/Qt";
3642
3643 if (strFrontend != "emergencystop")
3644 {
3645 /* check the session state */
3646 SessionState_T state;
3647 rc = aSession->COMGETTER(State)(&state);
3648 if (FAILED(rc))
3649 return rc;
3650
3651 if (state != SessionState_Unlocked)
3652 return setError(VBOX_E_INVALID_OBJECT_STATE,
3653 tr("The given session is busy"));
3654
3655 /* get the IInternalSessionControl interface */
3656 ComPtr<IInternalSessionControl> control(aSession);
3657 ComAssertMsgRet(!control.isNull(),
3658 ("No IInternalSessionControl interface"),
3659 E_INVALIDARG);
3660
3661 /* get the teleporter enable state for the progress object init. */
3662 BOOL fTeleporterEnabled;
3663 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3664 if (FAILED(rc))
3665 return rc;
3666
3667 /* create a progress object */
3668 ComObjPtr<ProgressProxy> progress;
3669 progress.createObject();
3670 rc = progress->init(mParent,
3671 static_cast<IMachine*>(this),
3672 Bstr(tr("Starting VM")).raw(),
3673 TRUE /* aCancelable */,
3674 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3675 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3676 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3677 2 /* uFirstOperationWeight */,
3678 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3679
3680 if (SUCCEEDED(rc))
3681 {
3682 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3683 if (SUCCEEDED(rc))
3684 {
3685 aProgress = progress;
3686
3687 /* signal the client watcher thread */
3688 mParent->i_updateClientWatcher();
3689
3690 /* fire an event */
3691 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3692 }
3693 }
3694 }
3695 else
3696 {
3697 /* no progress object - either instant success or failure */
3698 aProgress = NULL;
3699
3700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3701
3702 if (mData->mSession.mState != SessionState_Locked)
3703 return setError(VBOX_E_INVALID_OBJECT_STATE,
3704 tr("The machine '%s' is not locked by a session"),
3705 mUserData->s.strName.c_str());
3706
3707 /* must have a VM process associated - do not kill normal API clients
3708 * with an open session */
3709 if (!Global::IsOnline(mData->mMachineState))
3710 return setError(VBOX_E_INVALID_OBJECT_STATE,
3711 tr("The machine '%s' does not have a VM process"),
3712 mUserData->s.strName.c_str());
3713
3714 /* forcibly terminate the VM process */
3715 if (mData->mSession.mPID != NIL_RTPROCESS)
3716 RTProcTerminate(mData->mSession.mPID);
3717
3718 /* signal the client watcher thread, as most likely the client has
3719 * been terminated */
3720 mParent->i_updateClientWatcher();
3721 }
3722
3723 return rc;
3724}
3725
3726HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3727{
3728 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3729 return setError(E_INVALIDARG,
3730 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3731 aPosition, SchemaDefs::MaxBootPosition);
3732
3733 if (aDevice == DeviceType_USB)
3734 return setError(E_NOTIMPL,
3735 tr("Booting from USB device is currently not supported"));
3736
3737 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3738
3739 HRESULT rc = i_checkStateDependency(MutableStateDep);
3740 if (FAILED(rc)) return rc;
3741
3742 i_setModified(IsModified_MachineData);
3743 mHWData.backup();
3744 mHWData->mBootOrder[aPosition - 1] = aDevice;
3745
3746 return S_OK;
3747}
3748
3749HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3750{
3751 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3752 return setError(E_INVALIDARG,
3753 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3754 aPosition, SchemaDefs::MaxBootPosition);
3755
3756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3757
3758 *aDevice = mHWData->mBootOrder[aPosition - 1];
3759
3760 return S_OK;
3761}
3762
3763HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3764 LONG aControllerPort,
3765 LONG aDevice,
3766 DeviceType_T aType,
3767 const ComPtr<IMedium> &aMedium)
3768{
3769 IMedium *aM = aMedium;
3770 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3771 aName.c_str(), aControllerPort, aDevice, aType, aM));
3772
3773 // request the host lock first, since might be calling Host methods for getting host drives;
3774 // next, protect the media tree all the while we're in here, as well as our member variables
3775 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3776 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3777
3778 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3779 if (FAILED(rc)) return rc;
3780
3781 /// @todo NEWMEDIA implicit machine registration
3782 if (!mData->mRegistered)
3783 return setError(VBOX_E_INVALID_OBJECT_STATE,
3784 tr("Cannot attach storage devices to an unregistered machine"));
3785
3786 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3787
3788 /* Check for an existing controller. */
3789 ComObjPtr<StorageController> ctl;
3790 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3791 if (FAILED(rc)) return rc;
3792
3793 StorageControllerType_T ctrlType;
3794 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3795 if (FAILED(rc))
3796 return setError(E_FAIL,
3797 tr("Could not get type of controller '%s'"),
3798 aName.c_str());
3799
3800 bool fSilent = false;
3801 Utf8Str strReconfig;
3802
3803 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3804 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3805 if ( mData->mMachineState == MachineState_Paused
3806 && strReconfig == "1")
3807 fSilent = true;
3808
3809 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3810 bool fHotplug = false;
3811 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3812 fHotplug = true;
3813
3814 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3815 return setError(VBOX_E_INVALID_VM_STATE,
3816 tr("Controller '%s' does not support hotplugging"),
3817 aName.c_str());
3818
3819 // check that the port and device are not out of range
3820 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3821 if (FAILED(rc)) return rc;
3822
3823 /* check if the device slot is already busy */
3824 MediumAttachment *pAttachTemp;
3825 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3826 aName,
3827 aControllerPort,
3828 aDevice)))
3829 {
3830 Medium *pMedium = pAttachTemp->i_getMedium();
3831 if (pMedium)
3832 {
3833 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3834 return setError(VBOX_E_OBJECT_IN_USE,
3835 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3836 pMedium->i_getLocationFull().c_str(),
3837 aControllerPort,
3838 aDevice,
3839 aName.c_str());
3840 }
3841 else
3842 return setError(VBOX_E_OBJECT_IN_USE,
3843 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3844 aControllerPort, aDevice, aName.c_str());
3845 }
3846
3847 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3848 if (aMedium && medium.isNull())
3849 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3850
3851 AutoCaller mediumCaller(medium);
3852 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3853
3854 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3855
3856 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3857 && !medium.isNull()
3858 )
3859 return setError(VBOX_E_OBJECT_IN_USE,
3860 tr("Medium '%s' is already attached to this virtual machine"),
3861 medium->i_getLocationFull().c_str());
3862
3863 if (!medium.isNull())
3864 {
3865 MediumType_T mtype = medium->i_getType();
3866 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3867 // For DVDs it's not written to the config file, so needs no global config
3868 // version bump. For floppies it's a new attribute "type", which is ignored
3869 // by older VirtualBox version, so needs no global config version bump either.
3870 // For hard disks this type is not accepted.
3871 if (mtype == MediumType_MultiAttach)
3872 {
3873 // This type is new with VirtualBox 4.0 and therefore requires settings
3874 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3875 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3876 // two reasons: The medium type is a property of the media registry tree, which
3877 // can reside in the global config file (for pre-4.0 media); we would therefore
3878 // possibly need to bump the global config version. We don't want to do that though
3879 // because that might make downgrading to pre-4.0 impossible.
3880 // As a result, we can only use these two new types if the medium is NOT in the
3881 // global registry:
3882 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3883 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3884 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3885 )
3886 return setError(VBOX_E_INVALID_OBJECT_STATE,
3887 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3888 "to machines that were created with VirtualBox 4.0 or later"),
3889 medium->i_getLocationFull().c_str());
3890 }
3891 }
3892
3893 bool fIndirect = false;
3894 if (!medium.isNull())
3895 fIndirect = medium->i_isReadOnly();
3896 bool associate = true;
3897
3898 do
3899 {
3900 if ( aType == DeviceType_HardDisk
3901 && mMediumAttachments.isBackedUp())
3902 {
3903 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3904
3905 /* check if the medium was attached to the VM before we started
3906 * changing attachments in which case the attachment just needs to
3907 * be restored */
3908 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3909 {
3910 AssertReturn(!fIndirect, E_FAIL);
3911
3912 /* see if it's the same bus/channel/device */
3913 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3914 {
3915 /* the simplest case: restore the whole attachment
3916 * and return, nothing else to do */
3917 mMediumAttachments->push_back(pAttachTemp);
3918
3919 /* Reattach the medium to the VM. */
3920 if (fHotplug || fSilent)
3921 {
3922 mediumLock.release();
3923 treeLock.release();
3924 alock.release();
3925
3926 MediumLockList *pMediumLockList(new MediumLockList());
3927
3928 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3929 medium /* pToLockWrite */,
3930 false /* fMediumLockWriteAll */,
3931 NULL,
3932 *pMediumLockList);
3933 alock.acquire();
3934 if (FAILED(rc))
3935 delete pMediumLockList;
3936 else
3937 {
3938 mData->mSession.mLockedMedia.Unlock();
3939 alock.release();
3940 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3941 mData->mSession.mLockedMedia.Lock();
3942 alock.acquire();
3943 }
3944 alock.release();
3945
3946 if (SUCCEEDED(rc))
3947 {
3948 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3949 /* Remove lock list in case of error. */
3950 if (FAILED(rc))
3951 {
3952 mData->mSession.mLockedMedia.Unlock();
3953 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3954 mData->mSession.mLockedMedia.Lock();
3955 }
3956 }
3957 }
3958
3959 return S_OK;
3960 }
3961
3962 /* bus/channel/device differ; we need a new attachment object,
3963 * but don't try to associate it again */
3964 associate = false;
3965 break;
3966 }
3967 }
3968
3969 /* go further only if the attachment is to be indirect */
3970 if (!fIndirect)
3971 break;
3972
3973 /* perform the so called smart attachment logic for indirect
3974 * attachments. Note that smart attachment is only applicable to base
3975 * hard disks. */
3976
3977 if (medium->i_getParent().isNull())
3978 {
3979 /* first, investigate the backup copy of the current hard disk
3980 * attachments to make it possible to re-attach existing diffs to
3981 * another device slot w/o losing their contents */
3982 if (mMediumAttachments.isBackedUp())
3983 {
3984 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3985
3986 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3987 uint32_t foundLevel = 0;
3988
3989 for (MediumAttachmentList::const_iterator
3990 it = oldAtts.begin();
3991 it != oldAtts.end();
3992 ++it)
3993 {
3994 uint32_t level = 0;
3995 MediumAttachment *pAttach = *it;
3996 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3997 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3998 if (pMedium.isNull())
3999 continue;
4000
4001 if (pMedium->i_getBase(&level) == medium)
4002 {
4003 /* skip the hard disk if its currently attached (we
4004 * cannot attach the same hard disk twice) */
4005 if (i_findAttachment(*mMediumAttachments.data(),
4006 pMedium))
4007 continue;
4008
4009 /* matched device, channel and bus (i.e. attached to the
4010 * same place) will win and immediately stop the search;
4011 * otherwise the attachment that has the youngest
4012 * descendant of medium will be used
4013 */
4014 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4015 {
4016 /* the simplest case: restore the whole attachment
4017 * and return, nothing else to do */
4018 mMediumAttachments->push_back(*it);
4019
4020 /* Reattach the medium to the VM. */
4021 if (fHotplug || fSilent)
4022 {
4023 mediumLock.release();
4024 treeLock.release();
4025 alock.release();
4026
4027 MediumLockList *pMediumLockList(new MediumLockList());
4028
4029 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4030 medium /* pToLockWrite */,
4031 false /* fMediumLockWriteAll */,
4032 NULL,
4033 *pMediumLockList);
4034 alock.acquire();
4035 if (FAILED(rc))
4036 delete pMediumLockList;
4037 else
4038 {
4039 mData->mSession.mLockedMedia.Unlock();
4040 alock.release();
4041 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4042 mData->mSession.mLockedMedia.Lock();
4043 alock.acquire();
4044 }
4045 alock.release();
4046
4047 if (SUCCEEDED(rc))
4048 {
4049 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4050 /* Remove lock list in case of error. */
4051 if (FAILED(rc))
4052 {
4053 mData->mSession.mLockedMedia.Unlock();
4054 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4055 mData->mSession.mLockedMedia.Lock();
4056 }
4057 }
4058 }
4059
4060 return S_OK;
4061 }
4062 else if ( foundIt == oldAtts.end()
4063 || level > foundLevel /* prefer younger */
4064 )
4065 {
4066 foundIt = it;
4067 foundLevel = level;
4068 }
4069 }
4070 }
4071
4072 if (foundIt != oldAtts.end())
4073 {
4074 /* use the previously attached hard disk */
4075 medium = (*foundIt)->i_getMedium();
4076 mediumCaller.attach(medium);
4077 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4078 mediumLock.attach(medium);
4079 /* not implicit, doesn't require association with this VM */
4080 fIndirect = false;
4081 associate = false;
4082 /* go right to the MediumAttachment creation */
4083 break;
4084 }
4085 }
4086
4087 /* must give up the medium lock and medium tree lock as below we
4088 * go over snapshots, which needs a lock with higher lock order. */
4089 mediumLock.release();
4090 treeLock.release();
4091
4092 /* then, search through snapshots for the best diff in the given
4093 * hard disk's chain to base the new diff on */
4094
4095 ComObjPtr<Medium> base;
4096 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4097 while (snap)
4098 {
4099 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4100
4101 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4102
4103 MediumAttachment *pAttachFound = NULL;
4104 uint32_t foundLevel = 0;
4105
4106 for (MediumAttachmentList::const_iterator
4107 it = snapAtts.begin();
4108 it != snapAtts.end();
4109 ++it)
4110 {
4111 MediumAttachment *pAttach = *it;
4112 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4113 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4114 if (pMedium.isNull())
4115 continue;
4116
4117 uint32_t level = 0;
4118 if (pMedium->i_getBase(&level) == medium)
4119 {
4120 /* matched device, channel and bus (i.e. attached to the
4121 * same place) will win and immediately stop the search;
4122 * otherwise the attachment that has the youngest
4123 * descendant of medium will be used
4124 */
4125 if ( pAttach->i_getDevice() == aDevice
4126 && pAttach->i_getPort() == aControllerPort
4127 && pAttach->i_getControllerName() == aName
4128 )
4129 {
4130 pAttachFound = pAttach;
4131 break;
4132 }
4133 else if ( !pAttachFound
4134 || level > foundLevel /* prefer younger */
4135 )
4136 {
4137 pAttachFound = pAttach;
4138 foundLevel = level;
4139 }
4140 }
4141 }
4142
4143 if (pAttachFound)
4144 {
4145 base = pAttachFound->i_getMedium();
4146 break;
4147 }
4148
4149 snap = snap->i_getParent();
4150 }
4151
4152 /* re-lock medium tree and the medium, as we need it below */
4153 treeLock.acquire();
4154 mediumLock.acquire();
4155
4156 /* found a suitable diff, use it as a base */
4157 if (!base.isNull())
4158 {
4159 medium = base;
4160 mediumCaller.attach(medium);
4161 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4162 mediumLock.attach(medium);
4163 }
4164 }
4165
4166 Utf8Str strFullSnapshotFolder;
4167 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4168
4169 ComObjPtr<Medium> diff;
4170 diff.createObject();
4171 // store this diff in the same registry as the parent
4172 Guid uuidRegistryParent;
4173 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4174 {
4175 // parent image has no registry: this can happen if we're attaching a new immutable
4176 // image that has not yet been attached (medium then points to the base and we're
4177 // creating the diff image for the immutable, and the parent is not yet registered);
4178 // put the parent in the machine registry then
4179 mediumLock.release();
4180 treeLock.release();
4181 alock.release();
4182 i_addMediumToRegistry(medium);
4183 alock.acquire();
4184 treeLock.acquire();
4185 mediumLock.acquire();
4186 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4187 }
4188 rc = diff->init(mParent,
4189 medium->i_getPreferredDiffFormat(),
4190 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4191 uuidRegistryParent,
4192 DeviceType_HardDisk);
4193 if (FAILED(rc)) return rc;
4194
4195 /* Apply the normal locking logic to the entire chain. */
4196 MediumLockList *pMediumLockList(new MediumLockList());
4197 mediumLock.release();
4198 treeLock.release();
4199 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4200 diff /* pToLockWrite */,
4201 false /* fMediumLockWriteAll */,
4202 medium,
4203 *pMediumLockList);
4204 treeLock.acquire();
4205 mediumLock.acquire();
4206 if (SUCCEEDED(rc))
4207 {
4208 mediumLock.release();
4209 treeLock.release();
4210 rc = pMediumLockList->Lock();
4211 treeLock.acquire();
4212 mediumLock.acquire();
4213 if (FAILED(rc))
4214 setError(rc,
4215 tr("Could not lock medium when creating diff '%s'"),
4216 diff->i_getLocationFull().c_str());
4217 else
4218 {
4219 /* will release the lock before the potentially lengthy
4220 * operation, so protect with the special state */
4221 MachineState_T oldState = mData->mMachineState;
4222 i_setMachineState(MachineState_SettingUp);
4223
4224 mediumLock.release();
4225 treeLock.release();
4226 alock.release();
4227
4228 rc = medium->i_createDiffStorage(diff,
4229 medium->i_getPreferredDiffVariant(),
4230 pMediumLockList,
4231 NULL /* aProgress */,
4232 true /* aWait */);
4233
4234 alock.acquire();
4235 treeLock.acquire();
4236 mediumLock.acquire();
4237
4238 i_setMachineState(oldState);
4239 }
4240 }
4241
4242 /* Unlock the media and free the associated memory. */
4243 delete pMediumLockList;
4244
4245 if (FAILED(rc)) return rc;
4246
4247 /* use the created diff for the actual attachment */
4248 medium = diff;
4249 mediumCaller.attach(medium);
4250 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4251 mediumLock.attach(medium);
4252 }
4253 while (0);
4254
4255 ComObjPtr<MediumAttachment> attachment;
4256 attachment.createObject();
4257 rc = attachment->init(this,
4258 medium,
4259 aName,
4260 aControllerPort,
4261 aDevice,
4262 aType,
4263 fIndirect,
4264 false /* fPassthrough */,
4265 false /* fTempEject */,
4266 false /* fNonRotational */,
4267 false /* fDiscard */,
4268 fHotplug /* fHotPluggable */,
4269 Utf8Str::Empty);
4270 if (FAILED(rc)) return rc;
4271
4272 if (associate && !medium.isNull())
4273 {
4274 // as the last step, associate the medium to the VM
4275 rc = medium->i_addBackReference(mData->mUuid);
4276 // here we can fail because of Deleting, or being in process of creating a Diff
4277 if (FAILED(rc)) return rc;
4278
4279 mediumLock.release();
4280 treeLock.release();
4281 alock.release();
4282 i_addMediumToRegistry(medium);
4283 alock.acquire();
4284 treeLock.acquire();
4285 mediumLock.acquire();
4286 }
4287
4288 /* success: finally remember the attachment */
4289 i_setModified(IsModified_Storage);
4290 mMediumAttachments.backup();
4291 mMediumAttachments->push_back(attachment);
4292
4293 mediumLock.release();
4294 treeLock.release();
4295 alock.release();
4296
4297 if (fHotplug || fSilent)
4298 {
4299 if (!medium.isNull())
4300 {
4301 MediumLockList *pMediumLockList(new MediumLockList());
4302
4303 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4304 medium /* pToLockWrite */,
4305 false /* fMediumLockWriteAll */,
4306 NULL,
4307 *pMediumLockList);
4308 alock.acquire();
4309 if (FAILED(rc))
4310 delete pMediumLockList;
4311 else
4312 {
4313 mData->mSession.mLockedMedia.Unlock();
4314 alock.release();
4315 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4316 mData->mSession.mLockedMedia.Lock();
4317 alock.acquire();
4318 }
4319 alock.release();
4320 }
4321
4322 if (SUCCEEDED(rc))
4323 {
4324 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4325 /* Remove lock list in case of error. */
4326 if (FAILED(rc))
4327 {
4328 mData->mSession.mLockedMedia.Unlock();
4329 mData->mSession.mLockedMedia.Remove(attachment);
4330 mData->mSession.mLockedMedia.Lock();
4331 }
4332 }
4333 }
4334
4335 /* Save modified registries, but skip this machine as it's the caller's
4336 * job to save its settings like all other settings changes. */
4337 mParent->i_unmarkRegistryModified(i_getId());
4338 mParent->i_saveModifiedRegistries();
4339
4340 return rc;
4341}
4342
4343HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4344 LONG aDevice)
4345{
4346 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4347 aName.c_str(), aControllerPort, aDevice));
4348
4349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4350
4351 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4352 if (FAILED(rc)) return rc;
4353
4354 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4355
4356 /* Check for an existing controller. */
4357 ComObjPtr<StorageController> ctl;
4358 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4359 if (FAILED(rc)) return rc;
4360
4361 StorageControllerType_T ctrlType;
4362 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4363 if (FAILED(rc))
4364 return setError(E_FAIL,
4365 tr("Could not get type of controller '%s'"),
4366 aName.c_str());
4367
4368 bool fSilent = false;
4369 Utf8Str strReconfig;
4370
4371 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4372 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4373 if ( mData->mMachineState == MachineState_Paused
4374 && strReconfig == "1")
4375 fSilent = true;
4376
4377 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4378 bool fHotplug = false;
4379 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4380 fHotplug = true;
4381
4382 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4383 return setError(VBOX_E_INVALID_VM_STATE,
4384 tr("Controller '%s' does not support hotplugging"),
4385 aName.c_str());
4386
4387 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4388 aName,
4389 aControllerPort,
4390 aDevice);
4391 if (!pAttach)
4392 return setError(VBOX_E_OBJECT_NOT_FOUND,
4393 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4394 aDevice, aControllerPort, aName.c_str());
4395
4396 if (fHotplug && !pAttach->i_getHotPluggable())
4397 return setError(VBOX_E_NOT_SUPPORTED,
4398 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4399 aDevice, aControllerPort, aName.c_str());
4400
4401 /*
4402 * The VM has to detach the device before we delete any implicit diffs.
4403 * If this fails we can roll back without loosing data.
4404 */
4405 if (fHotplug || fSilent)
4406 {
4407 alock.release();
4408 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4409 alock.acquire();
4410 }
4411 if (FAILED(rc)) return rc;
4412
4413 /* If we are here everything went well and we can delete the implicit now. */
4414 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4415
4416 alock.release();
4417
4418 /* Save modified registries, but skip this machine as it's the caller's
4419 * job to save its settings like all other settings changes. */
4420 mParent->i_unmarkRegistryModified(i_getId());
4421 mParent->i_saveModifiedRegistries();
4422
4423 return rc;
4424}
4425
4426HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4427 LONG aDevice, BOOL aPassthrough)
4428{
4429 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4430 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4431
4432 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4433
4434 HRESULT rc = i_checkStateDependency(MutableStateDep);
4435 if (FAILED(rc)) return rc;
4436
4437 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4438
4439 if (Global::IsOnlineOrTransient(mData->mMachineState))
4440 return setError(VBOX_E_INVALID_VM_STATE,
4441 tr("Invalid machine state: %s"),
4442 Global::stringifyMachineState(mData->mMachineState));
4443
4444 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4445 aName,
4446 aControllerPort,
4447 aDevice);
4448 if (!pAttach)
4449 return setError(VBOX_E_OBJECT_NOT_FOUND,
4450 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4451 aDevice, aControllerPort, aName.c_str());
4452
4453
4454 i_setModified(IsModified_Storage);
4455 mMediumAttachments.backup();
4456
4457 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4458
4459 if (pAttach->i_getType() != DeviceType_DVD)
4460 return setError(E_INVALIDARG,
4461 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4462 aDevice, aControllerPort, aName.c_str());
4463 pAttach->i_updatePassthrough(!!aPassthrough);
4464
4465 return S_OK;
4466}
4467
4468HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4469 LONG aDevice, BOOL aTemporaryEject)
4470{
4471
4472 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4473 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4474
4475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4476
4477 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4478 if (FAILED(rc)) return rc;
4479
4480 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4481 aName,
4482 aControllerPort,
4483 aDevice);
4484 if (!pAttach)
4485 return setError(VBOX_E_OBJECT_NOT_FOUND,
4486 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4487 aDevice, aControllerPort, aName.c_str());
4488
4489
4490 i_setModified(IsModified_Storage);
4491 mMediumAttachments.backup();
4492
4493 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4494
4495 if (pAttach->i_getType() != DeviceType_DVD)
4496 return setError(E_INVALIDARG,
4497 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4498 aDevice, aControllerPort, aName.c_str());
4499 pAttach->i_updateTempEject(!!aTemporaryEject);
4500
4501 return S_OK;
4502}
4503
4504HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4505 LONG aDevice, BOOL aNonRotational)
4506{
4507
4508 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4509 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4510
4511 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4512
4513 HRESULT rc = i_checkStateDependency(MutableStateDep);
4514 if (FAILED(rc)) return rc;
4515
4516 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4517
4518 if (Global::IsOnlineOrTransient(mData->mMachineState))
4519 return setError(VBOX_E_INVALID_VM_STATE,
4520 tr("Invalid machine state: %s"),
4521 Global::stringifyMachineState(mData->mMachineState));
4522
4523 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4524 aName,
4525 aControllerPort,
4526 aDevice);
4527 if (!pAttach)
4528 return setError(VBOX_E_OBJECT_NOT_FOUND,
4529 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4530 aDevice, aControllerPort, aName.c_str());
4531
4532
4533 i_setModified(IsModified_Storage);
4534 mMediumAttachments.backup();
4535
4536 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4537
4538 if (pAttach->i_getType() != DeviceType_HardDisk)
4539 return setError(E_INVALIDARG,
4540 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"),
4541 aDevice, aControllerPort, aName.c_str());
4542 pAttach->i_updateNonRotational(!!aNonRotational);
4543
4544 return S_OK;
4545}
4546
4547HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4548 LONG aDevice, BOOL aDiscard)
4549{
4550
4551 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4552 aName.c_str(), aControllerPort, aDevice, aDiscard));
4553
4554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4555
4556 HRESULT rc = i_checkStateDependency(MutableStateDep);
4557 if (FAILED(rc)) return rc;
4558
4559 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4560
4561 if (Global::IsOnlineOrTransient(mData->mMachineState))
4562 return setError(VBOX_E_INVALID_VM_STATE,
4563 tr("Invalid machine state: %s"),
4564 Global::stringifyMachineState(mData->mMachineState));
4565
4566 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4567 aName,
4568 aControllerPort,
4569 aDevice);
4570 if (!pAttach)
4571 return setError(VBOX_E_OBJECT_NOT_FOUND,
4572 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4573 aDevice, aControllerPort, aName.c_str());
4574
4575
4576 i_setModified(IsModified_Storage);
4577 mMediumAttachments.backup();
4578
4579 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4580
4581 if (pAttach->i_getType() != DeviceType_HardDisk)
4582 return setError(E_INVALIDARG,
4583 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"),
4584 aDevice, aControllerPort, aName.c_str());
4585 pAttach->i_updateDiscard(!!aDiscard);
4586
4587 return S_OK;
4588}
4589
4590HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4591 LONG aDevice, BOOL aHotPluggable)
4592{
4593 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4594 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4595
4596 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4597
4598 HRESULT rc = i_checkStateDependency(MutableStateDep);
4599 if (FAILED(rc)) return rc;
4600
4601 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4602
4603 if (Global::IsOnlineOrTransient(mData->mMachineState))
4604 return setError(VBOX_E_INVALID_VM_STATE,
4605 tr("Invalid machine state: %s"),
4606 Global::stringifyMachineState(mData->mMachineState));
4607
4608 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4609 aName,
4610 aControllerPort,
4611 aDevice);
4612 if (!pAttach)
4613 return setError(VBOX_E_OBJECT_NOT_FOUND,
4614 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4615 aDevice, aControllerPort, aName.c_str());
4616
4617 /* Check for an existing controller. */
4618 ComObjPtr<StorageController> ctl;
4619 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4620 if (FAILED(rc)) return rc;
4621
4622 StorageControllerType_T ctrlType;
4623 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4624 if (FAILED(rc))
4625 return setError(E_FAIL,
4626 tr("Could not get type of controller '%s'"),
4627 aName.c_str());
4628
4629 if (!i_isControllerHotplugCapable(ctrlType))
4630 return setError(VBOX_E_NOT_SUPPORTED,
4631 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4632 aName.c_str());
4633
4634 i_setModified(IsModified_Storage);
4635 mMediumAttachments.backup();
4636
4637 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4638
4639 if (pAttach->i_getType() == DeviceType_Floppy)
4640 return setError(E_INVALIDARG,
4641 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"),
4642 aDevice, aControllerPort, aName.c_str());
4643 pAttach->i_updateHotPluggable(!!aHotPluggable);
4644
4645 return S_OK;
4646}
4647
4648HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4649 LONG aDevice)
4650{
4651 int rc = S_OK;
4652 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4653 aName.c_str(), aControllerPort, aDevice));
4654
4655 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4656
4657 return rc;
4658}
4659
4660HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4661 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4662{
4663 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4664 aName.c_str(), aControllerPort, aDevice));
4665
4666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4667
4668 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4669 if (FAILED(rc)) return rc;
4670
4671 if (Global::IsOnlineOrTransient(mData->mMachineState))
4672 return setError(VBOX_E_INVALID_VM_STATE,
4673 tr("Invalid machine state: %s"),
4674 Global::stringifyMachineState(mData->mMachineState));
4675
4676 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4677 aName,
4678 aControllerPort,
4679 aDevice);
4680 if (!pAttach)
4681 return setError(VBOX_E_OBJECT_NOT_FOUND,
4682 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4683 aDevice, aControllerPort, aName.c_str());
4684
4685
4686 i_setModified(IsModified_Storage);
4687 mMediumAttachments.backup();
4688
4689 IBandwidthGroup *iB = aBandwidthGroup;
4690 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4691 if (aBandwidthGroup && group.isNull())
4692 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4693
4694 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4695
4696 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4697 if (strBandwidthGroupOld.isNotEmpty())
4698 {
4699 /* Get the bandwidth group object and release it - this must not fail. */
4700 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4701 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4702 Assert(SUCCEEDED(rc));
4703
4704 pBandwidthGroupOld->i_release();
4705 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4706 }
4707
4708 if (!group.isNull())
4709 {
4710 group->i_reference();
4711 pAttach->i_updateBandwidthGroup(group->i_getName());
4712 }
4713
4714 return S_OK;
4715}
4716
4717HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4718 LONG aControllerPort,
4719 LONG aDevice,
4720 DeviceType_T aType)
4721{
4722 HRESULT rc = S_OK;
4723
4724 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4725 aName.c_str(), aControllerPort, aDevice, aType));
4726
4727 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4728
4729 return rc;
4730}
4731
4732
4733HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4734 LONG aControllerPort,
4735 LONG aDevice,
4736 BOOL aForce)
4737{
4738 int rc = S_OK;
4739 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4740 aName.c_str(), aControllerPort, aForce));
4741
4742 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4743
4744 return rc;
4745}
4746
4747HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4748 LONG aControllerPort,
4749 LONG aDevice,
4750 const ComPtr<IMedium> &aMedium,
4751 BOOL aForce)
4752{
4753 int rc = S_OK;
4754 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4755 aName.c_str(), aControllerPort, aDevice, aForce));
4756
4757 // request the host lock first, since might be calling Host methods for getting host drives;
4758 // next, protect the media tree all the while we're in here, as well as our member variables
4759 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4760 this->lockHandle(),
4761 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4762
4763 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4764 aName,
4765 aControllerPort,
4766 aDevice);
4767 if (pAttach.isNull())
4768 return setError(VBOX_E_OBJECT_NOT_FOUND,
4769 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4770 aDevice, aControllerPort, aName.c_str());
4771
4772 /* Remember previously mounted medium. The medium before taking the
4773 * backup is not necessarily the same thing. */
4774 ComObjPtr<Medium> oldmedium;
4775 oldmedium = pAttach->i_getMedium();
4776
4777 IMedium *iM = aMedium;
4778 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4779 if (aMedium && pMedium.isNull())
4780 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4781
4782 AutoCaller mediumCaller(pMedium);
4783 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4784
4785 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4786 if (pMedium)
4787 {
4788 DeviceType_T mediumType = pAttach->i_getType();
4789 switch (mediumType)
4790 {
4791 case DeviceType_DVD:
4792 case DeviceType_Floppy:
4793 break;
4794
4795 default:
4796 return setError(VBOX_E_INVALID_OBJECT_STATE,
4797 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4798 aControllerPort,
4799 aDevice,
4800 aName.c_str());
4801 }
4802 }
4803
4804 i_setModified(IsModified_Storage);
4805 mMediumAttachments.backup();
4806
4807 {
4808 // The backup operation makes the pAttach reference point to the
4809 // old settings. Re-get the correct reference.
4810 pAttach = i_findAttachment(*mMediumAttachments.data(),
4811 aName,
4812 aControllerPort,
4813 aDevice);
4814 if (!oldmedium.isNull())
4815 oldmedium->i_removeBackReference(mData->mUuid);
4816 if (!pMedium.isNull())
4817 {
4818 pMedium->i_addBackReference(mData->mUuid);
4819
4820 mediumLock.release();
4821 multiLock.release();
4822 i_addMediumToRegistry(pMedium);
4823 multiLock.acquire();
4824 mediumLock.acquire();
4825 }
4826
4827 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4828 pAttach->i_updateMedium(pMedium);
4829 }
4830
4831 i_setModified(IsModified_Storage);
4832
4833 mediumLock.release();
4834 multiLock.release();
4835 rc = i_onMediumChange(pAttach, aForce);
4836 multiLock.acquire();
4837 mediumLock.acquire();
4838
4839 /* On error roll back this change only. */
4840 if (FAILED(rc))
4841 {
4842 if (!pMedium.isNull())
4843 pMedium->i_removeBackReference(mData->mUuid);
4844 pAttach = i_findAttachment(*mMediumAttachments.data(),
4845 aName,
4846 aControllerPort,
4847 aDevice);
4848 /* If the attachment is gone in the meantime, bail out. */
4849 if (pAttach.isNull())
4850 return rc;
4851 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4852 if (!oldmedium.isNull())
4853 oldmedium->i_addBackReference(mData->mUuid);
4854 pAttach->i_updateMedium(oldmedium);
4855 }
4856
4857 mediumLock.release();
4858 multiLock.release();
4859
4860 /* Save modified registries, but skip this machine as it's the caller's
4861 * job to save its settings like all other settings changes. */
4862 mParent->i_unmarkRegistryModified(i_getId());
4863 mParent->i_saveModifiedRegistries();
4864
4865 return rc;
4866}
4867HRESULT Machine::getMedium(const com::Utf8Str &aName,
4868 LONG aControllerPort,
4869 LONG aDevice,
4870 ComPtr<IMedium> &aMedium)
4871{
4872 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4873 aName.c_str(), aControllerPort, aDevice));
4874
4875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4876
4877 aMedium = NULL;
4878
4879 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4880 aName,
4881 aControllerPort,
4882 aDevice);
4883 if (pAttach.isNull())
4884 return setError(VBOX_E_OBJECT_NOT_FOUND,
4885 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4886 aDevice, aControllerPort, aName.c_str());
4887
4888 aMedium = pAttach->i_getMedium();
4889
4890 return S_OK;
4891}
4892
4893HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4894{
4895
4896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4897
4898 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4899
4900 return S_OK;
4901}
4902
4903HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4904{
4905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4906
4907 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4908
4909 return S_OK;
4910}
4911
4912HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4913{
4914 /* Do not assert if slot is out of range, just return the advertised
4915 status. testdriver/vbox.py triggers this in logVmInfo. */
4916 if (aSlot >= mNetworkAdapters.size())
4917 return setError(E_INVALIDARG,
4918 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4919 aSlot, mNetworkAdapters.size());
4920
4921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4922
4923 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4924
4925 return S_OK;
4926}
4927
4928HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4929{
4930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4931
4932 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4933 size_t i = 0;
4934 for (settings::StringsMap::const_iterator
4935 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4936 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4937 ++it, ++i)
4938 aKeys[i] = it->first;
4939
4940 return S_OK;
4941}
4942
4943 /**
4944 * @note Locks this object for reading.
4945 */
4946HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4947 com::Utf8Str &aValue)
4948{
4949 /* start with nothing found */
4950 aValue = "";
4951
4952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4955 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4956 // found:
4957 aValue = it->second; // source is a Utf8Str
4958
4959 /* return the result to caller (may be empty) */
4960 return S_OK;
4961}
4962
4963 /**
4964 * @note Locks mParent for writing + this object for writing.
4965 */
4966HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4967{
4968 Utf8Str strOldValue; // empty
4969
4970 // locking note: we only hold the read lock briefly to look up the old value,
4971 // then release it and call the onExtraCanChange callbacks. There is a small
4972 // chance of a race insofar as the callback might be called twice if two callers
4973 // change the same key at the same time, but that's a much better solution
4974 // than the deadlock we had here before. The actual changing of the extradata
4975 // is then performed under the write lock and race-free.
4976
4977 // look up the old value first; if nothing has changed then we need not do anything
4978 {
4979 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4980
4981 // For snapshots don't even think about allowing changes, extradata
4982 // is global for a machine, so there is nothing snapshot specific.
4983 if (i_isSnapshotMachine())
4984 return setError(VBOX_E_INVALID_VM_STATE,
4985 tr("Cannot set extradata for a snapshot"));
4986
4987 // check if the right IMachine instance is used
4988 if (mData->mRegistered && !i_isSessionMachine())
4989 return setError(VBOX_E_INVALID_VM_STATE,
4990 tr("Cannot set extradata for an immutable machine"));
4991
4992 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4993 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4994 strOldValue = it->second;
4995 }
4996
4997 bool fChanged;
4998 if ((fChanged = (strOldValue != aValue)))
4999 {
5000 // ask for permission from all listeners outside the locks;
5001 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5002 // lock to copy the list of callbacks to invoke
5003 Bstr error;
5004 Bstr bstrValue(aValue);
5005
5006 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5007 {
5008 const char *sep = error.isEmpty() ? "" : ": ";
5009 CBSTR err = error.raw();
5010 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5011 return setError(E_ACCESSDENIED,
5012 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5013 aKey.c_str(),
5014 aValue.c_str(),
5015 sep,
5016 err);
5017 }
5018
5019 // data is changing and change not vetoed: then write it out under the lock
5020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5021
5022 if (aValue.isEmpty())
5023 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5024 else
5025 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5026 // creates a new key if needed
5027
5028 bool fNeedsGlobalSaveSettings = false;
5029 // This saving of settings is tricky: there is no "old state" for the
5030 // extradata items at all (unlike all other settings), so the old/new
5031 // settings comparison would give a wrong result!
5032 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5033
5034 if (fNeedsGlobalSaveSettings)
5035 {
5036 // save the global settings; for that we should hold only the VirtualBox lock
5037 alock.release();
5038 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5039 mParent->i_saveSettings();
5040 }
5041 }
5042
5043 // fire notification outside the lock
5044 if (fChanged)
5045 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5046
5047 return S_OK;
5048}
5049
5050HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5051{
5052 aProgress = NULL;
5053 NOREF(aSettingsFilePath);
5054 ReturnComNotImplemented();
5055}
5056
5057HRESULT Machine::saveSettings()
5058{
5059 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5060
5061 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5062 if (FAILED(rc)) return rc;
5063
5064 /* the settings file path may never be null */
5065 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5066
5067 /* save all VM data excluding snapshots */
5068 bool fNeedsGlobalSaveSettings = false;
5069 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5070 mlock.release();
5071
5072 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5073 {
5074 // save the global settings; for that we should hold only the VirtualBox lock
5075 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5076 rc = mParent->i_saveSettings();
5077 }
5078
5079 return rc;
5080}
5081
5082
5083HRESULT Machine::discardSettings()
5084{
5085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5086
5087 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5088 if (FAILED(rc)) return rc;
5089
5090 /*
5091 * during this rollback, the session will be notified if data has
5092 * been actually changed
5093 */
5094 i_rollback(true /* aNotify */);
5095
5096 return S_OK;
5097}
5098
5099/** @note Locks objects! */
5100HRESULT Machine::unregister(AutoCaller &autoCaller,
5101 CleanupMode_T aCleanupMode,
5102 std::vector<ComPtr<IMedium> > &aMedia)
5103{
5104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5105
5106 Guid id(i_getId());
5107
5108 if (mData->mSession.mState != SessionState_Unlocked)
5109 return setError(VBOX_E_INVALID_OBJECT_STATE,
5110 tr("Cannot unregister the machine '%s' while it is locked"),
5111 mUserData->s.strName.c_str());
5112
5113 // wait for state dependents to drop to zero
5114 i_ensureNoStateDependencies();
5115
5116 if (!mData->mAccessible)
5117 {
5118 // inaccessible maschines can only be unregistered; uninitialize ourselves
5119 // here because currently there may be no unregistered that are inaccessible
5120 // (this state combination is not supported). Note releasing the caller and
5121 // leaving the lock before calling uninit()
5122 alock.release();
5123 autoCaller.release();
5124
5125 uninit();
5126
5127 mParent->i_unregisterMachine(this, id);
5128 // calls VirtualBox::i_saveSettings()
5129
5130 return S_OK;
5131 }
5132
5133 HRESULT rc = S_OK;
5134
5135 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5136 // discard saved state
5137 if (mData->mMachineState == MachineState_Saved)
5138 {
5139 // add the saved state file to the list of files the caller should delete
5140 Assert(!mSSData->strStateFilePath.isEmpty());
5141 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5142
5143 mSSData->strStateFilePath.setNull();
5144
5145 // unconditionally set the machine state to powered off, we now
5146 // know no session has locked the machine
5147 mData->mMachineState = MachineState_PoweredOff;
5148 }
5149
5150 size_t cSnapshots = 0;
5151 if (mData->mFirstSnapshot)
5152 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5153 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5154 // fail now before we start detaching media
5155 return setError(VBOX_E_INVALID_OBJECT_STATE,
5156 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5157 mUserData->s.strName.c_str(), cSnapshots);
5158
5159 // This list collects the medium objects from all medium attachments
5160 // which we will detach from the machine and its snapshots, in a specific
5161 // order which allows for closing all media without getting "media in use"
5162 // errors, simply by going through the list from the front to the back:
5163 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5164 // and must be closed before the parent media from the snapshots, or closing the parents
5165 // will fail because they still have children);
5166 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5167 // the root ("first") snapshot of the machine.
5168 MediaList llMedia;
5169
5170 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5171 && mMediumAttachments->size()
5172 )
5173 {
5174 // we have media attachments: detach them all and add the Medium objects to our list
5175 if (aCleanupMode != CleanupMode_UnregisterOnly)
5176 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5177 else
5178 return setError(VBOX_E_INVALID_OBJECT_STATE,
5179 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5180 mUserData->s.strName.c_str(), mMediumAttachments->size());
5181 }
5182
5183 if (cSnapshots)
5184 {
5185 // add the media from the medium attachments of the snapshots to llMedia
5186 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5187 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5188 // into the children first
5189
5190 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5191 MachineState_T oldState = mData->mMachineState;
5192 mData->mMachineState = MachineState_DeletingSnapshot;
5193
5194 // make a copy of the first snapshot so the refcount does not drop to 0
5195 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5196 // because of the AutoCaller voodoo)
5197 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5198
5199 // GO!
5200 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5201
5202 mData->mMachineState = oldState;
5203 }
5204
5205 if (FAILED(rc))
5206 {
5207 i_rollbackMedia();
5208 return rc;
5209 }
5210
5211 // commit all the media changes made above
5212 i_commitMedia();
5213
5214 mData->mRegistered = false;
5215
5216 // machine lock no longer needed
5217 alock.release();
5218
5219 // return media to caller
5220 aMedia.resize(llMedia.size());
5221 size_t i = 0;
5222 for (MediaList::const_iterator
5223 it = llMedia.begin();
5224 it != llMedia.end();
5225 ++it, ++i)
5226 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5227
5228 mParent->i_unregisterMachine(this, id);
5229 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5230
5231 return S_OK;
5232}
5233
5234/**
5235 * Task record for deleting a machine config.
5236 */
5237class Machine::DeleteConfigTask
5238 : public Machine::Task
5239{
5240public:
5241 DeleteConfigTask(Machine *m,
5242 Progress *p,
5243 const Utf8Str &t,
5244 const RTCList<ComPtr<IMedium> > &llMediums,
5245 const StringsList &llFilesToDelete)
5246 : Task(m, p, t),
5247 m_llMediums(llMediums),
5248 m_llFilesToDelete(llFilesToDelete)
5249 {}
5250
5251private:
5252 void handler()
5253 {
5254 try
5255 {
5256 m_pMachine->i_deleteConfigHandler(*this);
5257 }
5258 catch (...)
5259 {
5260 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5261 }
5262 }
5263
5264 RTCList<ComPtr<IMedium> > m_llMediums;
5265 StringsList m_llFilesToDelete;
5266
5267 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5268};
5269
5270/**
5271 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5272 * SessionMachine::taskHandler().
5273 *
5274 * @note Locks this object for writing.
5275 *
5276 * @param task
5277 * @return
5278 */
5279void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5280{
5281 LogFlowThisFuncEnter();
5282
5283 AutoCaller autoCaller(this);
5284 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5285 if (FAILED(autoCaller.rc()))
5286 {
5287 /* we might have been uninitialized because the session was accidentally
5288 * closed by the client, so don't assert */
5289 HRESULT rc = setError(E_FAIL,
5290 tr("The session has been accidentally closed"));
5291 task.m_pProgress->i_notifyComplete(rc);
5292 LogFlowThisFuncLeave();
5293 return;
5294 }
5295
5296 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5297
5298 HRESULT rc = S_OK;
5299
5300 try
5301 {
5302 ULONG uLogHistoryCount = 3;
5303 ComPtr<ISystemProperties> systemProperties;
5304 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5305 if (FAILED(rc)) throw rc;
5306
5307 if (!systemProperties.isNull())
5308 {
5309 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5310 if (FAILED(rc)) throw rc;
5311 }
5312
5313 MachineState_T oldState = mData->mMachineState;
5314 i_setMachineState(MachineState_SettingUp);
5315 alock.release();
5316 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5317 {
5318 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5319 {
5320 AutoCaller mac(pMedium);
5321 if (FAILED(mac.rc())) throw mac.rc();
5322 Utf8Str strLocation = pMedium->i_getLocationFull();
5323 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5324 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5325 if (FAILED(rc)) throw rc;
5326 }
5327 if (pMedium->i_isMediumFormatFile())
5328 {
5329 ComPtr<IProgress> pProgress2;
5330 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5331 if (FAILED(rc)) throw rc;
5332 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5333 if (FAILED(rc)) throw rc;
5334 /* Check the result of the asynchronous process. */
5335 LONG iRc;
5336 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5337 if (FAILED(rc)) throw rc;
5338 /* If the thread of the progress object has an error, then
5339 * retrieve the error info from there, or it'll be lost. */
5340 if (FAILED(iRc))
5341 throw setError(ProgressErrorInfo(pProgress2));
5342 }
5343
5344 /* Close the medium, deliberately without checking the return
5345 * code, and without leaving any trace in the error info, as
5346 * a failure here is a very minor issue, which shouldn't happen
5347 * as above we even managed to delete the medium. */
5348 {
5349 ErrorInfoKeeper eik;
5350 pMedium->Close();
5351 }
5352 }
5353 i_setMachineState(oldState);
5354 alock.acquire();
5355
5356 // delete the files pushed on the task list by Machine::Delete()
5357 // (this includes saved states of the machine and snapshots and
5358 // medium storage files from the IMedium list passed in, and the
5359 // machine XML file)
5360 for (StringsList::const_iterator
5361 it = task.m_llFilesToDelete.begin();
5362 it != task.m_llFilesToDelete.end();
5363 ++it)
5364 {
5365 const Utf8Str &strFile = *it;
5366 LogFunc(("Deleting file %s\n", strFile.c_str()));
5367 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5368 if (FAILED(rc)) throw rc;
5369
5370 int vrc = RTFileDelete(strFile.c_str());
5371 if (RT_FAILURE(vrc))
5372 throw setError(VBOX_E_IPRT_ERROR,
5373 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5374 }
5375
5376 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5377 if (FAILED(rc)) throw rc;
5378
5379 /* delete the settings only when the file actually exists */
5380 if (mData->pMachineConfigFile->fileExists())
5381 {
5382 /* Delete any backup or uncommitted XML files. Ignore failures.
5383 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5384 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5385 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5386 RTFileDelete(otherXml.c_str());
5387 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5388 RTFileDelete(otherXml.c_str());
5389
5390 /* delete the Logs folder, nothing important should be left
5391 * there (we don't check for errors because the user might have
5392 * some private files there that we don't want to delete) */
5393 Utf8Str logFolder;
5394 getLogFolder(logFolder);
5395 Assert(logFolder.length());
5396 if (RTDirExists(logFolder.c_str()))
5397 {
5398 /* Delete all VBox.log[.N] files from the Logs folder
5399 * (this must be in sync with the rotation logic in
5400 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5401 * files that may have been created by the GUI. */
5402 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5403 logFolder.c_str(), RTPATH_DELIMITER);
5404 RTFileDelete(log.c_str());
5405 log = Utf8StrFmt("%s%cVBox.png",
5406 logFolder.c_str(), RTPATH_DELIMITER);
5407 RTFileDelete(log.c_str());
5408 for (int i = uLogHistoryCount; i > 0; i--)
5409 {
5410 log = Utf8StrFmt("%s%cVBox.log.%d",
5411 logFolder.c_str(), RTPATH_DELIMITER, i);
5412 RTFileDelete(log.c_str());
5413 log = Utf8StrFmt("%s%cVBox.png.%d",
5414 logFolder.c_str(), RTPATH_DELIMITER, i);
5415 RTFileDelete(log.c_str());
5416 }
5417#if defined(RT_OS_WINDOWS)
5418 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5419 RTFileDelete(log.c_str());
5420 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5421 RTFileDelete(log.c_str());
5422#endif
5423
5424 RTDirRemove(logFolder.c_str());
5425 }
5426
5427 /* delete the Snapshots folder, nothing important should be left
5428 * there (we don't check for errors because the user might have
5429 * some private files there that we don't want to delete) */
5430 Utf8Str strFullSnapshotFolder;
5431 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5432 Assert(!strFullSnapshotFolder.isEmpty());
5433 if (RTDirExists(strFullSnapshotFolder.c_str()))
5434 RTDirRemove(strFullSnapshotFolder.c_str());
5435
5436 // delete the directory that contains the settings file, but only
5437 // if it matches the VM name
5438 Utf8Str settingsDir;
5439 if (i_isInOwnDir(&settingsDir))
5440 RTDirRemove(settingsDir.c_str());
5441 }
5442
5443 alock.release();
5444
5445 mParent->i_saveModifiedRegistries();
5446 }
5447 catch (HRESULT aRC) { rc = aRC; }
5448
5449 task.m_pProgress->i_notifyComplete(rc);
5450
5451 LogFlowThisFuncLeave();
5452}
5453
5454HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5455{
5456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5457
5458 HRESULT rc = i_checkStateDependency(MutableStateDep);
5459 if (FAILED(rc)) return rc;
5460
5461 if (mData->mRegistered)
5462 return setError(VBOX_E_INVALID_VM_STATE,
5463 tr("Cannot delete settings of a registered machine"));
5464
5465 // collect files to delete
5466 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5467 if (mData->pMachineConfigFile->fileExists())
5468 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5469
5470 RTCList<ComPtr<IMedium> > llMediums;
5471 for (size_t i = 0; i < aMedia.size(); ++i)
5472 {
5473 IMedium *pIMedium(aMedia[i]);
5474 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5475 if (pMedium.isNull())
5476 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5477 SafeArray<BSTR> ids;
5478 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5479 if (FAILED(rc)) return rc;
5480 /* At this point the medium should not have any back references
5481 * anymore. If it has it is attached to another VM and *must* not
5482 * deleted. */
5483 if (ids.size() < 1)
5484 llMediums.append(pMedium);
5485 }
5486
5487 ComObjPtr<Progress> pProgress;
5488 pProgress.createObject();
5489 rc = pProgress->init(i_getVirtualBox(),
5490 static_cast<IMachine*>(this) /* aInitiator */,
5491 tr("Deleting files"),
5492 true /* fCancellable */,
5493 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5494 tr("Collecting file inventory"));
5495 if (FAILED(rc))
5496 return rc;
5497
5498 /* create and start the task on a separate thread (note that it will not
5499 * start working until we release alock) */
5500 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5501 rc = pTask->createThread();
5502 if (FAILED(rc))
5503 return rc;
5504
5505 pProgress.queryInterfaceTo(aProgress.asOutParam());
5506
5507 LogFlowFuncLeave();
5508
5509 return S_OK;
5510}
5511
5512HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5513{
5514 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5515
5516 ComObjPtr<Snapshot> pSnapshot;
5517 HRESULT rc;
5518
5519 if (aNameOrId.isEmpty())
5520 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5521 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5522 else
5523 {
5524 Guid uuid(aNameOrId);
5525 if (uuid.isValid())
5526 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5527 else
5528 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5529 }
5530 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5531
5532 return rc;
5533}
5534
5535HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5536{
5537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5538
5539 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5540 if (FAILED(rc)) return rc;
5541
5542 ComObjPtr<SharedFolder> sharedFolder;
5543 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5544 if (SUCCEEDED(rc))
5545 return setError(VBOX_E_OBJECT_IN_USE,
5546 tr("Shared folder named '%s' already exists"),
5547 aName.c_str());
5548
5549 sharedFolder.createObject();
5550 rc = sharedFolder->init(i_getMachine(),
5551 aName,
5552 aHostPath,
5553 !!aWritable,
5554 !!aAutomount,
5555 true /* fFailOnError */);
5556 if (FAILED(rc)) return rc;
5557
5558 i_setModified(IsModified_SharedFolders);
5559 mHWData.backup();
5560 mHWData->mSharedFolders.push_back(sharedFolder);
5561
5562 /* inform the direct session if any */
5563 alock.release();
5564 i_onSharedFolderChange();
5565
5566 return S_OK;
5567}
5568
5569HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5570{
5571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5572
5573 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5574 if (FAILED(rc)) return rc;
5575
5576 ComObjPtr<SharedFolder> sharedFolder;
5577 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5578 if (FAILED(rc)) return rc;
5579
5580 i_setModified(IsModified_SharedFolders);
5581 mHWData.backup();
5582 mHWData->mSharedFolders.remove(sharedFolder);
5583
5584 /* inform the direct session if any */
5585 alock.release();
5586 i_onSharedFolderChange();
5587
5588 return S_OK;
5589}
5590
5591HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5592{
5593 /* start with No */
5594 *aCanShow = FALSE;
5595
5596 ComPtr<IInternalSessionControl> directControl;
5597 {
5598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5599
5600 if (mData->mSession.mState != SessionState_Locked)
5601 return setError(VBOX_E_INVALID_VM_STATE,
5602 tr("Machine is not locked for session (session state: %s)"),
5603 Global::stringifySessionState(mData->mSession.mState));
5604
5605 if (mData->mSession.mLockType == LockType_VM)
5606 directControl = mData->mSession.mDirectControl;
5607 }
5608
5609 /* ignore calls made after #OnSessionEnd() is called */
5610 if (!directControl)
5611 return S_OK;
5612
5613 LONG64 dummy;
5614 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5615}
5616
5617HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5618{
5619 ComPtr<IInternalSessionControl> directControl;
5620 {
5621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5622
5623 if (mData->mSession.mState != SessionState_Locked)
5624 return setError(E_FAIL,
5625 tr("Machine is not locked for session (session state: %s)"),
5626 Global::stringifySessionState(mData->mSession.mState));
5627
5628 if (mData->mSession.mLockType == LockType_VM)
5629 directControl = mData->mSession.mDirectControl;
5630 }
5631
5632 /* ignore calls made after #OnSessionEnd() is called */
5633 if (!directControl)
5634 return S_OK;
5635
5636 BOOL dummy;
5637 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5638}
5639
5640#ifdef VBOX_WITH_GUEST_PROPS
5641/**
5642 * Look up a guest property in VBoxSVC's internal structures.
5643 */
5644HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5645 com::Utf8Str &aValue,
5646 LONG64 *aTimestamp,
5647 com::Utf8Str &aFlags) const
5648{
5649 using namespace guestProp;
5650
5651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5652 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5653
5654 if (it != mHWData->mGuestProperties.end())
5655 {
5656 char szFlags[MAX_FLAGS_LEN + 1];
5657 aValue = it->second.strValue;
5658 *aTimestamp = it->second.mTimestamp;
5659 writeFlags(it->second.mFlags, szFlags);
5660 aFlags = Utf8Str(szFlags);
5661 }
5662
5663 return S_OK;
5664}
5665
5666/**
5667 * Query the VM that a guest property belongs to for the property.
5668 * @returns E_ACCESSDENIED if the VM process is not available or not
5669 * currently handling queries and the lookup should then be done in
5670 * VBoxSVC.
5671 */
5672HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5673 com::Utf8Str &aValue,
5674 LONG64 *aTimestamp,
5675 com::Utf8Str &aFlags) const
5676{
5677 HRESULT rc = S_OK;
5678 BSTR bValue = NULL;
5679 BSTR bFlags = NULL;
5680
5681 ComPtr<IInternalSessionControl> directControl;
5682 {
5683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5684 if (mData->mSession.mLockType == LockType_VM)
5685 directControl = mData->mSession.mDirectControl;
5686 }
5687
5688 /* ignore calls made after #OnSessionEnd() is called */
5689 if (!directControl)
5690 rc = E_ACCESSDENIED;
5691 else
5692 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5693 0 /* accessMode */,
5694 &bValue, aTimestamp, &bFlags);
5695
5696 aValue = bValue;
5697 aFlags = bFlags;
5698
5699 return rc;
5700}
5701#endif // VBOX_WITH_GUEST_PROPS
5702
5703HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5704 com::Utf8Str &aValue,
5705 LONG64 *aTimestamp,
5706 com::Utf8Str &aFlags)
5707{
5708#ifndef VBOX_WITH_GUEST_PROPS
5709 ReturnComNotImplemented();
5710#else // VBOX_WITH_GUEST_PROPS
5711
5712 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5713
5714 if (rc == E_ACCESSDENIED)
5715 /* The VM is not running or the service is not (yet) accessible */
5716 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5717 return rc;
5718#endif // VBOX_WITH_GUEST_PROPS
5719}
5720
5721HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5722{
5723 LONG64 dummyTimestamp;
5724 com::Utf8Str dummyFlags;
5725 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5726 return rc;
5727
5728}
5729HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5730{
5731 com::Utf8Str dummyFlags;
5732 com::Utf8Str dummyValue;
5733 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5734 return rc;
5735}
5736
5737#ifdef VBOX_WITH_GUEST_PROPS
5738/**
5739 * Set a guest property in VBoxSVC's internal structures.
5740 */
5741HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5742 const com::Utf8Str &aFlags, bool fDelete)
5743{
5744 using namespace guestProp;
5745
5746 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5747 HRESULT rc = S_OK;
5748
5749 rc = i_checkStateDependency(MutableOrSavedStateDep);
5750 if (FAILED(rc)) return rc;
5751
5752 try
5753 {
5754 uint32_t fFlags = NILFLAG;
5755 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5756 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5757
5758 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5759 if (it == mHWData->mGuestProperties.end())
5760 {
5761 if (!fDelete)
5762 {
5763 i_setModified(IsModified_MachineData);
5764 mHWData.backupEx();
5765
5766 RTTIMESPEC time;
5767 HWData::GuestProperty prop;
5768 prop.strValue = Bstr(aValue).raw();
5769 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5770 prop.mFlags = fFlags;
5771 mHWData->mGuestProperties[aName] = prop;
5772 }
5773 }
5774 else
5775 {
5776 if (it->second.mFlags & (RDONLYHOST))
5777 {
5778 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5779 }
5780 else
5781 {
5782 i_setModified(IsModified_MachineData);
5783 mHWData.backupEx();
5784
5785 /* The backupEx() operation invalidates our iterator,
5786 * so get a new one. */
5787 it = mHWData->mGuestProperties.find(aName);
5788 Assert(it != mHWData->mGuestProperties.end());
5789
5790 if (!fDelete)
5791 {
5792 RTTIMESPEC time;
5793 it->second.strValue = aValue;
5794 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5795 it->second.mFlags = fFlags;
5796 }
5797 else
5798 mHWData->mGuestProperties.erase(it);
5799 }
5800 }
5801
5802 if (SUCCEEDED(rc))
5803 {
5804 alock.release();
5805
5806 mParent->i_onGuestPropertyChange(mData->mUuid,
5807 Bstr(aName).raw(),
5808 Bstr(aValue).raw(),
5809 Bstr(aFlags).raw());
5810 }
5811 }
5812 catch (std::bad_alloc &)
5813 {
5814 rc = E_OUTOFMEMORY;
5815 }
5816
5817 return rc;
5818}
5819
5820/**
5821 * Set a property on the VM that that property belongs to.
5822 * @returns E_ACCESSDENIED if the VM process is not available or not
5823 * currently handling queries and the setting should then be done in
5824 * VBoxSVC.
5825 */
5826HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5827 const com::Utf8Str &aFlags, bool fDelete)
5828{
5829 HRESULT rc;
5830
5831 try
5832 {
5833 ComPtr<IInternalSessionControl> directControl;
5834 {
5835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5836 if (mData->mSession.mLockType == LockType_VM)
5837 directControl = mData->mSession.mDirectControl;
5838 }
5839
5840 BSTR dummy = NULL; /* will not be changed (setter) */
5841 LONG64 dummy64;
5842 if (!directControl)
5843 rc = E_ACCESSDENIED;
5844 else
5845 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5846 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5847 fDelete? 2: 1 /* accessMode */,
5848 &dummy, &dummy64, &dummy);
5849 }
5850 catch (std::bad_alloc &)
5851 {
5852 rc = E_OUTOFMEMORY;
5853 }
5854
5855 return rc;
5856}
5857#endif // VBOX_WITH_GUEST_PROPS
5858
5859HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5860 const com::Utf8Str &aFlags)
5861{
5862#ifndef VBOX_WITH_GUEST_PROPS
5863 ReturnComNotImplemented();
5864#else // VBOX_WITH_GUEST_PROPS
5865 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5866 if (rc == E_ACCESSDENIED)
5867 /* The VM is not running or the service is not (yet) accessible */
5868 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5869 return rc;
5870#endif // VBOX_WITH_GUEST_PROPS
5871}
5872
5873HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5874{
5875 return setGuestProperty(aProperty, aValue, "");
5876}
5877
5878HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5879{
5880#ifndef VBOX_WITH_GUEST_PROPS
5881 ReturnComNotImplemented();
5882#else // VBOX_WITH_GUEST_PROPS
5883 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5884 if (rc == E_ACCESSDENIED)
5885 /* The VM is not running or the service is not (yet) accessible */
5886 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5887 return rc;
5888#endif // VBOX_WITH_GUEST_PROPS
5889}
5890
5891#ifdef VBOX_WITH_GUEST_PROPS
5892/**
5893 * Enumerate the guest properties in VBoxSVC's internal structures.
5894 */
5895HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5896 std::vector<com::Utf8Str> &aNames,
5897 std::vector<com::Utf8Str> &aValues,
5898 std::vector<LONG64> &aTimestamps,
5899 std::vector<com::Utf8Str> &aFlags)
5900{
5901 using namespace guestProp;
5902
5903 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5904 Utf8Str strPatterns(aPatterns);
5905
5906 HWData::GuestPropertyMap propMap;
5907
5908 /*
5909 * Look for matching patterns and build up a list.
5910 */
5911 for (HWData::GuestPropertyMap::const_iterator
5912 it = mHWData->mGuestProperties.begin();
5913 it != mHWData->mGuestProperties.end();
5914 ++it)
5915 {
5916 if ( strPatterns.isEmpty()
5917 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5918 RTSTR_MAX,
5919 it->first.c_str(),
5920 RTSTR_MAX,
5921 NULL)
5922 )
5923 propMap.insert(*it);
5924 }
5925
5926 alock.release();
5927
5928 /*
5929 * And build up the arrays for returning the property information.
5930 */
5931 size_t cEntries = propMap.size();
5932
5933 aNames.resize(cEntries);
5934 aValues.resize(cEntries);
5935 aTimestamps.resize(cEntries);
5936 aFlags.resize(cEntries);
5937
5938 char szFlags[MAX_FLAGS_LEN + 1];
5939 size_t i = 0;
5940 for (HWData::GuestPropertyMap::const_iterator
5941 it = propMap.begin();
5942 it != propMap.end();
5943 ++it, ++i)
5944 {
5945 aNames[i] = it->first;
5946 aValues[i] = it->second.strValue;
5947 aTimestamps[i] = it->second.mTimestamp;
5948 writeFlags(it->second.mFlags, szFlags);
5949 aFlags[i] = Utf8Str(szFlags);
5950 }
5951
5952 return S_OK;
5953}
5954
5955/**
5956 * Enumerate the properties managed by a VM.
5957 * @returns E_ACCESSDENIED if the VM process is not available or not
5958 * currently handling queries and the setting should then be done in
5959 * VBoxSVC.
5960 */
5961HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5962 std::vector<com::Utf8Str> &aNames,
5963 std::vector<com::Utf8Str> &aValues,
5964 std::vector<LONG64> &aTimestamps,
5965 std::vector<com::Utf8Str> &aFlags)
5966{
5967 HRESULT rc;
5968 ComPtr<IInternalSessionControl> directControl;
5969 {
5970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5971 if (mData->mSession.mLockType == LockType_VM)
5972 directControl = mData->mSession.mDirectControl;
5973 }
5974
5975 com::SafeArray<BSTR> bNames;
5976 com::SafeArray<BSTR> bValues;
5977 com::SafeArray<LONG64> bTimestamps;
5978 com::SafeArray<BSTR> bFlags;
5979
5980 if (!directControl)
5981 rc = E_ACCESSDENIED;
5982 else
5983 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5984 ComSafeArrayAsOutParam(bNames),
5985 ComSafeArrayAsOutParam(bValues),
5986 ComSafeArrayAsOutParam(bTimestamps),
5987 ComSafeArrayAsOutParam(bFlags));
5988 size_t i;
5989 aNames.resize(bNames.size());
5990 for (i = 0; i < bNames.size(); ++i)
5991 aNames[i] = Utf8Str(bNames[i]);
5992 aValues.resize(bValues.size());
5993 for (i = 0; i < bValues.size(); ++i)
5994 aValues[i] = Utf8Str(bValues[i]);
5995 aTimestamps.resize(bTimestamps.size());
5996 for (i = 0; i < bTimestamps.size(); ++i)
5997 aTimestamps[i] = bTimestamps[i];
5998 aFlags.resize(bFlags.size());
5999 for (i = 0; i < bFlags.size(); ++i)
6000 aFlags[i] = Utf8Str(bFlags[i]);
6001
6002 return rc;
6003}
6004#endif // VBOX_WITH_GUEST_PROPS
6005HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6006 std::vector<com::Utf8Str> &aNames,
6007 std::vector<com::Utf8Str> &aValues,
6008 std::vector<LONG64> &aTimestamps,
6009 std::vector<com::Utf8Str> &aFlags)
6010{
6011#ifndef VBOX_WITH_GUEST_PROPS
6012 ReturnComNotImplemented();
6013#else // VBOX_WITH_GUEST_PROPS
6014
6015 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6016
6017 if (rc == E_ACCESSDENIED)
6018 /* The VM is not running or the service is not (yet) accessible */
6019 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6020 return rc;
6021#endif // VBOX_WITH_GUEST_PROPS
6022}
6023
6024HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6025 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6026{
6027 MediumAttachmentList atts;
6028
6029 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6030 if (FAILED(rc)) return rc;
6031
6032 aMediumAttachments.resize(atts.size());
6033 size_t i = 0;
6034 for (MediumAttachmentList::const_iterator
6035 it = atts.begin();
6036 it != atts.end();
6037 ++it, ++i)
6038 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6039
6040 return S_OK;
6041}
6042
6043HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6044 LONG aControllerPort,
6045 LONG aDevice,
6046 ComPtr<IMediumAttachment> &aAttachment)
6047{
6048 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6049 aName.c_str(), aControllerPort, aDevice));
6050
6051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6052
6053 aAttachment = NULL;
6054
6055 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6056 aName,
6057 aControllerPort,
6058 aDevice);
6059 if (pAttach.isNull())
6060 return setError(VBOX_E_OBJECT_NOT_FOUND,
6061 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6062 aDevice, aControllerPort, aName.c_str());
6063
6064 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6065
6066 return S_OK;
6067}
6068
6069
6070HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6071 StorageBus_T aConnectionType,
6072 ComPtr<IStorageController> &aController)
6073{
6074 if ( (aConnectionType <= StorageBus_Null)
6075 || (aConnectionType > StorageBus_PCIe))
6076 return setError(E_INVALIDARG,
6077 tr("Invalid connection type: %d"),
6078 aConnectionType);
6079
6080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6081
6082 HRESULT rc = i_checkStateDependency(MutableStateDep);
6083 if (FAILED(rc)) return rc;
6084
6085 /* try to find one with the name first. */
6086 ComObjPtr<StorageController> ctrl;
6087
6088 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6089 if (SUCCEEDED(rc))
6090 return setError(VBOX_E_OBJECT_IN_USE,
6091 tr("Storage controller named '%s' already exists"),
6092 aName.c_str());
6093
6094 ctrl.createObject();
6095
6096 /* get a new instance number for the storage controller */
6097 ULONG ulInstance = 0;
6098 bool fBootable = true;
6099 for (StorageControllerList::const_iterator
6100 it = mStorageControllers->begin();
6101 it != mStorageControllers->end();
6102 ++it)
6103 {
6104 if ((*it)->i_getStorageBus() == aConnectionType)
6105 {
6106 ULONG ulCurInst = (*it)->i_getInstance();
6107
6108 if (ulCurInst >= ulInstance)
6109 ulInstance = ulCurInst + 1;
6110
6111 /* Only one controller of each type can be marked as bootable. */
6112 if ((*it)->i_getBootable())
6113 fBootable = false;
6114 }
6115 }
6116
6117 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6118 if (FAILED(rc)) return rc;
6119
6120 i_setModified(IsModified_Storage);
6121 mStorageControllers.backup();
6122 mStorageControllers->push_back(ctrl);
6123
6124 ctrl.queryInterfaceTo(aController.asOutParam());
6125
6126 /* inform the direct session if any */
6127 alock.release();
6128 i_onStorageControllerChange();
6129
6130 return S_OK;
6131}
6132
6133HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6134 ComPtr<IStorageController> &aStorageController)
6135{
6136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6137
6138 ComObjPtr<StorageController> ctrl;
6139
6140 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6141 if (SUCCEEDED(rc))
6142 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6143
6144 return rc;
6145}
6146
6147HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6148 ULONG aInstance,
6149 ComPtr<IStorageController> &aStorageController)
6150{
6151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6152
6153 for (StorageControllerList::const_iterator
6154 it = mStorageControllers->begin();
6155 it != mStorageControllers->end();
6156 ++it)
6157 {
6158 if ( (*it)->i_getStorageBus() == aConnectionType
6159 && (*it)->i_getInstance() == aInstance)
6160 {
6161 (*it).queryInterfaceTo(aStorageController.asOutParam());
6162 return S_OK;
6163 }
6164 }
6165
6166 return setError(VBOX_E_OBJECT_NOT_FOUND,
6167 tr("Could not find a storage controller with instance number '%lu'"),
6168 aInstance);
6169}
6170
6171HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6172{
6173 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6174
6175 HRESULT rc = i_checkStateDependency(MutableStateDep);
6176 if (FAILED(rc)) return rc;
6177
6178 ComObjPtr<StorageController> ctrl;
6179
6180 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6181 if (SUCCEEDED(rc))
6182 {
6183 /* Ensure that only one controller of each type is marked as bootable. */
6184 if (aBootable == TRUE)
6185 {
6186 for (StorageControllerList::const_iterator
6187 it = mStorageControllers->begin();
6188 it != mStorageControllers->end();
6189 ++it)
6190 {
6191 ComObjPtr<StorageController> aCtrl = (*it);
6192
6193 if ( (aCtrl->i_getName() != aName)
6194 && aCtrl->i_getBootable() == TRUE
6195 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6196 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6197 {
6198 aCtrl->i_setBootable(FALSE);
6199 break;
6200 }
6201 }
6202 }
6203
6204 if (SUCCEEDED(rc))
6205 {
6206 ctrl->i_setBootable(aBootable);
6207 i_setModified(IsModified_Storage);
6208 }
6209 }
6210
6211 if (SUCCEEDED(rc))
6212 {
6213 /* inform the direct session if any */
6214 alock.release();
6215 i_onStorageControllerChange();
6216 }
6217
6218 return rc;
6219}
6220
6221HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6222{
6223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6224
6225 HRESULT rc = i_checkStateDependency(MutableStateDep);
6226 if (FAILED(rc)) return rc;
6227
6228 ComObjPtr<StorageController> ctrl;
6229 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6230 if (FAILED(rc)) return rc;
6231
6232 {
6233 /* find all attached devices to the appropriate storage controller and detach them all */
6234 // make a temporary list because detachDevice invalidates iterators into
6235 // mMediumAttachments
6236 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6237
6238 for (MediumAttachmentList::const_iterator
6239 it = llAttachments2.begin();
6240 it != llAttachments2.end();
6241 ++it)
6242 {
6243 MediumAttachment *pAttachTemp = *it;
6244
6245 AutoCaller localAutoCaller(pAttachTemp);
6246 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6247
6248 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6249
6250 if (pAttachTemp->i_getControllerName() == aName)
6251 {
6252 rc = i_detachDevice(pAttachTemp, alock, NULL);
6253 if (FAILED(rc)) return rc;
6254 }
6255 }
6256 }
6257
6258 /* We can remove it now. */
6259 i_setModified(IsModified_Storage);
6260 mStorageControllers.backup();
6261
6262 ctrl->i_unshare();
6263
6264 mStorageControllers->remove(ctrl);
6265
6266 /* inform the direct session if any */
6267 alock.release();
6268 i_onStorageControllerChange();
6269
6270 return S_OK;
6271}
6272
6273HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6274 ComPtr<IUSBController> &aController)
6275{
6276 if ( (aType <= USBControllerType_Null)
6277 || (aType >= USBControllerType_Last))
6278 return setError(E_INVALIDARG,
6279 tr("Invalid USB controller type: %d"),
6280 aType);
6281
6282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6283
6284 HRESULT rc = i_checkStateDependency(MutableStateDep);
6285 if (FAILED(rc)) return rc;
6286
6287 /* try to find one with the same type first. */
6288 ComObjPtr<USBController> ctrl;
6289
6290 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6291 if (SUCCEEDED(rc))
6292 return setError(VBOX_E_OBJECT_IN_USE,
6293 tr("USB controller named '%s' already exists"),
6294 aName.c_str());
6295
6296 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6297 ULONG maxInstances;
6298 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6299 if (FAILED(rc))
6300 return rc;
6301
6302 ULONG cInstances = i_getUSBControllerCountByType(aType);
6303 if (cInstances >= maxInstances)
6304 return setError(E_INVALIDARG,
6305 tr("Too many USB controllers of this type"));
6306
6307 ctrl.createObject();
6308
6309 rc = ctrl->init(this, aName, aType);
6310 if (FAILED(rc)) return rc;
6311
6312 i_setModified(IsModified_USB);
6313 mUSBControllers.backup();
6314 mUSBControllers->push_back(ctrl);
6315
6316 ctrl.queryInterfaceTo(aController.asOutParam());
6317
6318 /* inform the direct session if any */
6319 alock.release();
6320 i_onUSBControllerChange();
6321
6322 return S_OK;
6323}
6324
6325HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6326{
6327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6328
6329 ComObjPtr<USBController> ctrl;
6330
6331 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6332 if (SUCCEEDED(rc))
6333 ctrl.queryInterfaceTo(aController.asOutParam());
6334
6335 return rc;
6336}
6337
6338HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6339 ULONG *aControllers)
6340{
6341 if ( (aType <= USBControllerType_Null)
6342 || (aType >= USBControllerType_Last))
6343 return setError(E_INVALIDARG,
6344 tr("Invalid USB controller type: %d"),
6345 aType);
6346
6347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6348
6349 ComObjPtr<USBController> ctrl;
6350
6351 *aControllers = i_getUSBControllerCountByType(aType);
6352
6353 return S_OK;
6354}
6355
6356HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6357{
6358
6359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6360
6361 HRESULT rc = i_checkStateDependency(MutableStateDep);
6362 if (FAILED(rc)) return rc;
6363
6364 ComObjPtr<USBController> ctrl;
6365 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6366 if (FAILED(rc)) return rc;
6367
6368 i_setModified(IsModified_USB);
6369 mUSBControllers.backup();
6370
6371 ctrl->i_unshare();
6372
6373 mUSBControllers->remove(ctrl);
6374
6375 /* inform the direct session if any */
6376 alock.release();
6377 i_onUSBControllerChange();
6378
6379 return S_OK;
6380}
6381
6382HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6383 ULONG *aOriginX,
6384 ULONG *aOriginY,
6385 ULONG *aWidth,
6386 ULONG *aHeight,
6387 BOOL *aEnabled)
6388{
6389 uint32_t u32OriginX= 0;
6390 uint32_t u32OriginY= 0;
6391 uint32_t u32Width = 0;
6392 uint32_t u32Height = 0;
6393 uint16_t u16Flags = 0;
6394
6395 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6396 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6397 if (RT_FAILURE(vrc))
6398 {
6399#ifdef RT_OS_WINDOWS
6400 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6401 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6402 * So just assign fEnable to TRUE again.
6403 * The right fix would be to change GUI API wrappers to make sure that parameters
6404 * are changed only if API succeeds.
6405 */
6406 *aEnabled = TRUE;
6407#endif
6408 return setError(VBOX_E_IPRT_ERROR,
6409 tr("Saved guest size is not available (%Rrc)"),
6410 vrc);
6411 }
6412
6413 *aOriginX = u32OriginX;
6414 *aOriginY = u32OriginY;
6415 *aWidth = u32Width;
6416 *aHeight = u32Height;
6417 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6418
6419 return S_OK;
6420}
6421
6422HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6423 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6424{
6425 if (aScreenId != 0)
6426 return E_NOTIMPL;
6427
6428 if ( aBitmapFormat != BitmapFormat_BGR0
6429 && aBitmapFormat != BitmapFormat_BGRA
6430 && aBitmapFormat != BitmapFormat_RGBA
6431 && aBitmapFormat != BitmapFormat_PNG)
6432 return setError(E_NOTIMPL,
6433 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6434
6435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 uint8_t *pu8Data = NULL;
6438 uint32_t cbData = 0;
6439 uint32_t u32Width = 0;
6440 uint32_t u32Height = 0;
6441
6442 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6443
6444 if (RT_FAILURE(vrc))
6445 return setError(VBOX_E_IPRT_ERROR,
6446 tr("Saved thumbnail data is not available (%Rrc)"),
6447 vrc);
6448
6449 HRESULT hr = S_OK;
6450
6451 *aWidth = u32Width;
6452 *aHeight = u32Height;
6453
6454 if (cbData > 0)
6455 {
6456 /* Convert pixels to the format expected by the API caller. */
6457 if (aBitmapFormat == BitmapFormat_BGR0)
6458 {
6459 /* [0] B, [1] G, [2] R, [3] 0. */
6460 aData.resize(cbData);
6461 memcpy(&aData.front(), pu8Data, cbData);
6462 }
6463 else if (aBitmapFormat == BitmapFormat_BGRA)
6464 {
6465 /* [0] B, [1] G, [2] R, [3] A. */
6466 aData.resize(cbData);
6467 for (uint32_t i = 0; i < cbData; i += 4)
6468 {
6469 aData[i] = pu8Data[i];
6470 aData[i + 1] = pu8Data[i + 1];
6471 aData[i + 2] = pu8Data[i + 2];
6472 aData[i + 3] = 0xff;
6473 }
6474 }
6475 else if (aBitmapFormat == BitmapFormat_RGBA)
6476 {
6477 /* [0] R, [1] G, [2] B, [3] A. */
6478 aData.resize(cbData);
6479 for (uint32_t i = 0; i < cbData; i += 4)
6480 {
6481 aData[i] = pu8Data[i + 2];
6482 aData[i + 1] = pu8Data[i + 1];
6483 aData[i + 2] = pu8Data[i];
6484 aData[i + 3] = 0xff;
6485 }
6486 }
6487 else if (aBitmapFormat == BitmapFormat_PNG)
6488 {
6489 uint8_t *pu8PNG = NULL;
6490 uint32_t cbPNG = 0;
6491 uint32_t cxPNG = 0;
6492 uint32_t cyPNG = 0;
6493
6494 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6495
6496 if (RT_SUCCESS(vrc))
6497 {
6498 aData.resize(cbPNG);
6499 if (cbPNG)
6500 memcpy(&aData.front(), pu8PNG, cbPNG);
6501 }
6502 else
6503 hr = setError(VBOX_E_IPRT_ERROR,
6504 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6505 vrc);
6506
6507 RTMemFree(pu8PNG);
6508 }
6509 }
6510
6511 freeSavedDisplayScreenshot(pu8Data);
6512
6513 return hr;
6514}
6515
6516HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6517 ULONG *aWidth,
6518 ULONG *aHeight,
6519 std::vector<BitmapFormat_T> &aBitmapFormats)
6520{
6521 if (aScreenId != 0)
6522 return E_NOTIMPL;
6523
6524 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6525
6526 uint8_t *pu8Data = NULL;
6527 uint32_t cbData = 0;
6528 uint32_t u32Width = 0;
6529 uint32_t u32Height = 0;
6530
6531 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6532
6533 if (RT_FAILURE(vrc))
6534 return setError(VBOX_E_IPRT_ERROR,
6535 tr("Saved screenshot data is not available (%Rrc)"),
6536 vrc);
6537
6538 *aWidth = u32Width;
6539 *aHeight = u32Height;
6540 aBitmapFormats.resize(1);
6541 aBitmapFormats[0] = BitmapFormat_PNG;
6542
6543 freeSavedDisplayScreenshot(pu8Data);
6544
6545 return S_OK;
6546}
6547
6548HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6549 BitmapFormat_T aBitmapFormat,
6550 ULONG *aWidth,
6551 ULONG *aHeight,
6552 std::vector<BYTE> &aData)
6553{
6554 if (aScreenId != 0)
6555 return E_NOTIMPL;
6556
6557 if (aBitmapFormat != BitmapFormat_PNG)
6558 return E_NOTIMPL;
6559
6560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6561
6562 uint8_t *pu8Data = NULL;
6563 uint32_t cbData = 0;
6564 uint32_t u32Width = 0;
6565 uint32_t u32Height = 0;
6566
6567 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6568
6569 if (RT_FAILURE(vrc))
6570 return setError(VBOX_E_IPRT_ERROR,
6571 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6572 vrc);
6573
6574 *aWidth = u32Width;
6575 *aHeight = u32Height;
6576
6577 aData.resize(cbData);
6578 if (cbData)
6579 memcpy(&aData.front(), pu8Data, cbData);
6580
6581 freeSavedDisplayScreenshot(pu8Data);
6582
6583 return S_OK;
6584}
6585
6586HRESULT Machine::hotPlugCPU(ULONG aCpu)
6587{
6588 HRESULT rc = S_OK;
6589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6590
6591 if (!mHWData->mCPUHotPlugEnabled)
6592 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6593
6594 if (aCpu >= mHWData->mCPUCount)
6595 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6596
6597 if (mHWData->mCPUAttached[aCpu])
6598 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6599
6600 alock.release();
6601 rc = i_onCPUChange(aCpu, false);
6602 alock.acquire();
6603 if (FAILED(rc)) return rc;
6604
6605 i_setModified(IsModified_MachineData);
6606 mHWData.backup();
6607 mHWData->mCPUAttached[aCpu] = true;
6608
6609 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6610 if (Global::IsOnline(mData->mMachineState))
6611 i_saveSettings(NULL);
6612
6613 return S_OK;
6614}
6615
6616HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6617{
6618 HRESULT rc = S_OK;
6619
6620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6621
6622 if (!mHWData->mCPUHotPlugEnabled)
6623 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6624
6625 if (aCpu >= SchemaDefs::MaxCPUCount)
6626 return setError(E_INVALIDARG,
6627 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6628 SchemaDefs::MaxCPUCount);
6629
6630 if (!mHWData->mCPUAttached[aCpu])
6631 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6632
6633 /* CPU 0 can't be detached */
6634 if (aCpu == 0)
6635 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6636
6637 alock.release();
6638 rc = i_onCPUChange(aCpu, true);
6639 alock.acquire();
6640 if (FAILED(rc)) return rc;
6641
6642 i_setModified(IsModified_MachineData);
6643 mHWData.backup();
6644 mHWData->mCPUAttached[aCpu] = false;
6645
6646 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6647 if (Global::IsOnline(mData->mMachineState))
6648 i_saveSettings(NULL);
6649
6650 return S_OK;
6651}
6652
6653HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6654{
6655 *aAttached = false;
6656
6657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6658
6659 /* If hotplug is enabled the CPU is always enabled. */
6660 if (!mHWData->mCPUHotPlugEnabled)
6661 {
6662 if (aCpu < mHWData->mCPUCount)
6663 *aAttached = true;
6664 }
6665 else
6666 {
6667 if (aCpu < SchemaDefs::MaxCPUCount)
6668 *aAttached = mHWData->mCPUAttached[aCpu];
6669 }
6670
6671 return S_OK;
6672}
6673
6674HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6675{
6676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6677
6678 Utf8Str log = i_getLogFilename(aIdx);
6679 if (!RTFileExists(log.c_str()))
6680 log.setNull();
6681 aFilename = log;
6682
6683 return S_OK;
6684}
6685
6686HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6687{
6688 if (aSize < 0)
6689 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6690
6691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6692
6693 HRESULT rc = S_OK;
6694 Utf8Str log = i_getLogFilename(aIdx);
6695
6696 /* do not unnecessarily hold the lock while doing something which does
6697 * not need the lock and potentially takes a long time. */
6698 alock.release();
6699
6700 /* Limit the chunk size to 32K for now, as that gives better performance
6701 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6702 * One byte expands to approx. 25 bytes of breathtaking XML. */
6703 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6704 aData.resize(cbData);
6705
6706 RTFILE LogFile;
6707 int vrc = RTFileOpen(&LogFile, log.c_str(),
6708 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6709 if (RT_SUCCESS(vrc))
6710 {
6711 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6712 if (RT_SUCCESS(vrc))
6713 aData.resize(cbData);
6714 else
6715 rc = setError(VBOX_E_IPRT_ERROR,
6716 tr("Could not read log file '%s' (%Rrc)"),
6717 log.c_str(), vrc);
6718 RTFileClose(LogFile);
6719 }
6720 else
6721 rc = setError(VBOX_E_IPRT_ERROR,
6722 tr("Could not open log file '%s' (%Rrc)"),
6723 log.c_str(), vrc);
6724
6725 if (FAILED(rc))
6726 aData.resize(0);
6727
6728 return rc;
6729}
6730
6731
6732/**
6733 * Currently this method doesn't attach device to the running VM,
6734 * just makes sure it's plugged on next VM start.
6735 */
6736HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6737{
6738 // lock scope
6739 {
6740 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6741
6742 HRESULT rc = i_checkStateDependency(MutableStateDep);
6743 if (FAILED(rc)) return rc;
6744
6745 ChipsetType_T aChipset = ChipsetType_PIIX3;
6746 COMGETTER(ChipsetType)(&aChipset);
6747
6748 if (aChipset != ChipsetType_ICH9)
6749 {
6750 return setError(E_INVALIDARG,
6751 tr("Host PCI attachment only supported with ICH9 chipset"));
6752 }
6753
6754 // check if device with this host PCI address already attached
6755 for (HWData::PCIDeviceAssignmentList::const_iterator
6756 it = mHWData->mPCIDeviceAssignments.begin();
6757 it != mHWData->mPCIDeviceAssignments.end();
6758 ++it)
6759 {
6760 LONG iHostAddress = -1;
6761 ComPtr<PCIDeviceAttachment> pAttach;
6762 pAttach = *it;
6763 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6764 if (iHostAddress == aHostAddress)
6765 return setError(E_INVALIDARG,
6766 tr("Device with host PCI address already attached to this VM"));
6767 }
6768
6769 ComObjPtr<PCIDeviceAttachment> pda;
6770 char name[32];
6771
6772 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6773 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6774 pda.createObject();
6775 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6776 i_setModified(IsModified_MachineData);
6777 mHWData.backup();
6778 mHWData->mPCIDeviceAssignments.push_back(pda);
6779 }
6780
6781 return S_OK;
6782}
6783
6784/**
6785 * Currently this method doesn't detach device from the running VM,
6786 * just makes sure it's not plugged on next VM start.
6787 */
6788HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6789{
6790 ComObjPtr<PCIDeviceAttachment> pAttach;
6791 bool fRemoved = false;
6792 HRESULT rc;
6793
6794 // lock scope
6795 {
6796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6797
6798 rc = i_checkStateDependency(MutableStateDep);
6799 if (FAILED(rc)) return rc;
6800
6801 for (HWData::PCIDeviceAssignmentList::const_iterator
6802 it = mHWData->mPCIDeviceAssignments.begin();
6803 it != mHWData->mPCIDeviceAssignments.end();
6804 ++it)
6805 {
6806 LONG iHostAddress = -1;
6807 pAttach = *it;
6808 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6809 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6810 {
6811 i_setModified(IsModified_MachineData);
6812 mHWData.backup();
6813 mHWData->mPCIDeviceAssignments.remove(pAttach);
6814 fRemoved = true;
6815 break;
6816 }
6817 }
6818 }
6819
6820
6821 /* Fire event outside of the lock */
6822 if (fRemoved)
6823 {
6824 Assert(!pAttach.isNull());
6825 ComPtr<IEventSource> es;
6826 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6827 Assert(SUCCEEDED(rc));
6828 Bstr mid;
6829 rc = this->COMGETTER(Id)(mid.asOutParam());
6830 Assert(SUCCEEDED(rc));
6831 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6832 }
6833
6834 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6835 tr("No host PCI device %08x attached"),
6836 aHostAddress
6837 );
6838}
6839
6840HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6841{
6842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6843
6844 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6845 size_t i = 0;
6846 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6847 it = mHWData->mPCIDeviceAssignments.begin();
6848 it != mHWData->mPCIDeviceAssignments.end();
6849 ++it, ++i)
6850 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6851
6852 return S_OK;
6853}
6854
6855HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6856{
6857 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6858
6859 return S_OK;
6860}
6861
6862HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6863{
6864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6865
6866 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6867
6868 return S_OK;
6869}
6870
6871HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6872{
6873 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6874 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6875 if (SUCCEEDED(hrc))
6876 {
6877 hrc = mHWData.backupEx();
6878 if (SUCCEEDED(hrc))
6879 {
6880 i_setModified(IsModified_MachineData);
6881 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6882 }
6883 }
6884 return hrc;
6885}
6886
6887HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6888{
6889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6890 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6891 return S_OK;
6892}
6893
6894HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6895{
6896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6897 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6898 if (SUCCEEDED(hrc))
6899 {
6900 hrc = mHWData.backupEx();
6901 if (SUCCEEDED(hrc))
6902 {
6903 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6904 if (SUCCEEDED(hrc))
6905 i_setModified(IsModified_MachineData);
6906 }
6907 }
6908 return hrc;
6909}
6910
6911HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6912{
6913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6914
6915 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6916
6917 return S_OK;
6918}
6919
6920HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6921{
6922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6923 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6924 if (SUCCEEDED(hrc))
6925 {
6926 hrc = mHWData.backupEx();
6927 if (SUCCEEDED(hrc))
6928 {
6929 i_setModified(IsModified_MachineData);
6930 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6931 }
6932 }
6933 return hrc;
6934}
6935
6936HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6937{
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6941
6942 return S_OK;
6943}
6944
6945HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6946{
6947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6948
6949 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6950 if ( SUCCEEDED(hrc)
6951 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6952 {
6953 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6954 int vrc;
6955
6956 if (aAutostartEnabled)
6957 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6958 else
6959 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6960
6961 if (RT_SUCCESS(vrc))
6962 {
6963 hrc = mHWData.backupEx();
6964 if (SUCCEEDED(hrc))
6965 {
6966 i_setModified(IsModified_MachineData);
6967 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6968 }
6969 }
6970 else if (vrc == VERR_NOT_SUPPORTED)
6971 hrc = setError(VBOX_E_NOT_SUPPORTED,
6972 tr("The VM autostart feature is not supported on this platform"));
6973 else if (vrc == VERR_PATH_NOT_FOUND)
6974 hrc = setError(E_FAIL,
6975 tr("The path to the autostart database is not set"));
6976 else
6977 hrc = setError(E_UNEXPECTED,
6978 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6979 aAutostartEnabled ? "Adding" : "Removing",
6980 mUserData->s.strName.c_str(), vrc);
6981 }
6982 return hrc;
6983}
6984
6985HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6986{
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6990
6991 return S_OK;
6992}
6993
6994HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6995{
6996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6998 if (SUCCEEDED(hrc))
6999 {
7000 hrc = mHWData.backupEx();
7001 if (SUCCEEDED(hrc))
7002 {
7003 i_setModified(IsModified_MachineData);
7004 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7005 }
7006 }
7007 return hrc;
7008}
7009
7010HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7011{
7012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7013
7014 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7015
7016 return S_OK;
7017}
7018
7019HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7020{
7021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7022 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7023 if ( SUCCEEDED(hrc)
7024 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7025 {
7026 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7027 int vrc;
7028
7029 if (aAutostopType != AutostopType_Disabled)
7030 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7031 else
7032 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7033
7034 if (RT_SUCCESS(vrc))
7035 {
7036 hrc = mHWData.backupEx();
7037 if (SUCCEEDED(hrc))
7038 {
7039 i_setModified(IsModified_MachineData);
7040 mHWData->mAutostart.enmAutostopType = aAutostopType;
7041 }
7042 }
7043 else if (vrc == VERR_NOT_SUPPORTED)
7044 hrc = setError(VBOX_E_NOT_SUPPORTED,
7045 tr("The VM autostop feature is not supported on this platform"));
7046 else if (vrc == VERR_PATH_NOT_FOUND)
7047 hrc = setError(E_FAIL,
7048 tr("The path to the autostart database is not set"));
7049 else
7050 hrc = setError(E_UNEXPECTED,
7051 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7052 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7053 mUserData->s.strName.c_str(), vrc);
7054 }
7055 return hrc;
7056}
7057
7058HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7059{
7060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7061
7062 aDefaultFrontend = mHWData->mDefaultFrontend;
7063
7064 return S_OK;
7065}
7066
7067HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7068{
7069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7070 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7071 if (SUCCEEDED(hrc))
7072 {
7073 hrc = mHWData.backupEx();
7074 if (SUCCEEDED(hrc))
7075 {
7076 i_setModified(IsModified_MachineData);
7077 mHWData->mDefaultFrontend = aDefaultFrontend;
7078 }
7079 }
7080 return hrc;
7081}
7082
7083HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7084{
7085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7086 size_t cbIcon = mUserData->s.ovIcon.size();
7087 aIcon.resize(cbIcon);
7088 if (cbIcon)
7089 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7090 return S_OK;
7091}
7092
7093HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7094{
7095 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7096 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7097 if (SUCCEEDED(hrc))
7098 {
7099 i_setModified(IsModified_MachineData);
7100 mUserData.backup();
7101 size_t cbIcon = aIcon.size();
7102 mUserData->s.ovIcon.resize(cbIcon);
7103 if (cbIcon)
7104 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7105 }
7106 return hrc;
7107}
7108
7109HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7110{
7111#ifdef VBOX_WITH_USB
7112 *aUSBProxyAvailable = true;
7113#else
7114 *aUSBProxyAvailable = false;
7115#endif
7116 return S_OK;
7117}
7118
7119HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7120{
7121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7122
7123 aVMProcessPriority = mUserData->s.strVMPriority;
7124
7125 return S_OK;
7126}
7127
7128HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7129{
7130 RT_NOREF(aVMProcessPriority);
7131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7132 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7133 if (SUCCEEDED(hrc))
7134 {
7135 /** @todo r=klaus: currently this is marked as not implemented, as
7136 * the code for setting the priority of the process is not there
7137 * (neither when starting the VM nor at runtime). */
7138 ReturnComNotImplemented();
7139#if 0
7140 hrc = mUserData.backupEx();
7141 if (SUCCEEDED(hrc))
7142 {
7143 i_setModified(IsModified_MachineData);
7144 mUserData->s.strVMPriority = aVMProcessPriority;
7145 }
7146#endif
7147 }
7148 return hrc;
7149}
7150
7151HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7152 ComPtr<IProgress> &aProgress)
7153{
7154 ComObjPtr<Progress> pP;
7155 Progress *ppP = pP;
7156 IProgress *iP = static_cast<IProgress *>(ppP);
7157 IProgress **pProgress = &iP;
7158
7159 IMachine *pTarget = aTarget;
7160
7161 /* Convert the options. */
7162 RTCList<CloneOptions_T> optList;
7163 if (aOptions.size())
7164 for (size_t i = 0; i < aOptions.size(); ++i)
7165 optList.append(aOptions[i]);
7166
7167 if (optList.contains(CloneOptions_Link))
7168 {
7169 if (!i_isSnapshotMachine())
7170 return setError(E_INVALIDARG,
7171 tr("Linked clone can only be created from a snapshot"));
7172 if (aMode != CloneMode_MachineState)
7173 return setError(E_INVALIDARG,
7174 tr("Linked clone can only be created for a single machine state"));
7175 }
7176 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7177
7178 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7179
7180 HRESULT rc = pWorker->start(pProgress);
7181
7182 pP = static_cast<Progress *>(*pProgress);
7183 pP.queryInterfaceTo(aProgress.asOutParam());
7184
7185 return rc;
7186
7187}
7188
7189HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7190{
7191 NOREF(aProgress);
7192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7193
7194 // This check should always fail.
7195 HRESULT rc = i_checkStateDependency(MutableStateDep);
7196 if (FAILED(rc)) return rc;
7197
7198 AssertFailedReturn(E_NOTIMPL);
7199}
7200
7201HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7202{
7203 NOREF(aSavedStateFile);
7204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7205
7206 // This check should always fail.
7207 HRESULT rc = i_checkStateDependency(MutableStateDep);
7208 if (FAILED(rc)) return rc;
7209
7210 AssertFailedReturn(E_NOTIMPL);
7211}
7212
7213HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7214{
7215 NOREF(aFRemoveFile);
7216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7217
7218 // This check should always fail.
7219 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7220 if (FAILED(rc)) return rc;
7221
7222 AssertFailedReturn(E_NOTIMPL);
7223}
7224
7225// public methods for internal purposes
7226/////////////////////////////////////////////////////////////////////////////
7227
7228/**
7229 * Adds the given IsModified_* flag to the dirty flags of the machine.
7230 * This must be called either during i_loadSettings or under the machine write lock.
7231 * @param fl Flag
7232 * @param fAllowStateModification If state modifications are allowed.
7233 */
7234void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7235{
7236 mData->flModifications |= fl;
7237 if (fAllowStateModification && i_isStateModificationAllowed())
7238 mData->mCurrentStateModified = true;
7239}
7240
7241/**
7242 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7243 * care of the write locking.
7244 *
7245 * @param fModification The flag to add.
7246 * @param fAllowStateModification If state modifications are allowed.
7247 */
7248void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7249{
7250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7251 i_setModified(fModification, fAllowStateModification);
7252}
7253
7254/**
7255 * Saves the registry entry of this machine to the given configuration node.
7256 *
7257 * @param data Machine registry data.
7258 *
7259 * @note locks this object for reading.
7260 */
7261HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7262{
7263 AutoLimitedCaller autoCaller(this);
7264 AssertComRCReturnRC(autoCaller.rc());
7265
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267
7268 data.uuid = mData->mUuid;
7269 data.strSettingsFile = mData->m_strConfigFile;
7270
7271 return S_OK;
7272}
7273
7274/**
7275 * Calculates the absolute path of the given path taking the directory of the
7276 * machine settings file as the current directory.
7277 *
7278 * @param strPath Path to calculate the absolute path for.
7279 * @param aResult Where to put the result (used only on success, can be the
7280 * same Utf8Str instance as passed in @a aPath).
7281 * @return IPRT result.
7282 *
7283 * @note Locks this object for reading.
7284 */
7285int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7286{
7287 AutoCaller autoCaller(this);
7288 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7289
7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7291
7292 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7293
7294 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7295
7296 strSettingsDir.stripFilename();
7297 char folder[RTPATH_MAX];
7298 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7299 if (RT_SUCCESS(vrc))
7300 aResult = folder;
7301
7302 return vrc;
7303}
7304
7305/**
7306 * Copies strSource to strTarget, making it relative to the machine folder
7307 * if it is a subdirectory thereof, or simply copying it otherwise.
7308 *
7309 * @param strSource Path to evaluate and copy.
7310 * @param strTarget Buffer to receive target path.
7311 *
7312 * @note Locks this object for reading.
7313 */
7314void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7315 Utf8Str &strTarget)
7316{
7317 AutoCaller autoCaller(this);
7318 AssertComRCReturn(autoCaller.rc(), (void)0);
7319
7320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7321
7322 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7323 // use strTarget as a temporary buffer to hold the machine settings dir
7324 strTarget = mData->m_strConfigFileFull;
7325 strTarget.stripFilename();
7326 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7327 {
7328 // is relative: then append what's left
7329 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7330 // for empty paths (only possible for subdirs) use "." to avoid
7331 // triggering default settings for not present config attributes.
7332 if (strTarget.isEmpty())
7333 strTarget = ".";
7334 }
7335 else
7336 // is not relative: then overwrite
7337 strTarget = strSource;
7338}
7339
7340/**
7341 * Returns the full path to the machine's log folder in the
7342 * \a aLogFolder argument.
7343 */
7344void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturnVoid(autoCaller.rc());
7348
7349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7350
7351 char szTmp[RTPATH_MAX];
7352 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7353 if (RT_SUCCESS(vrc))
7354 {
7355 if (szTmp[0] && !mUserData.isNull())
7356 {
7357 char szTmp2[RTPATH_MAX];
7358 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7359 if (RT_SUCCESS(vrc))
7360 aLogFolder = Utf8StrFmt("%s%c%s",
7361 szTmp2,
7362 RTPATH_DELIMITER,
7363 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7364 }
7365 else
7366 vrc = VERR_PATH_IS_RELATIVE;
7367 }
7368
7369 if (RT_FAILURE(vrc))
7370 {
7371 // fallback if VBOX_USER_LOGHOME is not set or invalid
7372 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7373 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7374 aLogFolder.append(RTPATH_DELIMITER);
7375 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7376 }
7377}
7378
7379/**
7380 * Returns the full path to the machine's log file for an given index.
7381 */
7382Utf8Str Machine::i_getLogFilename(ULONG idx)
7383{
7384 Utf8Str logFolder;
7385 getLogFolder(logFolder);
7386 Assert(logFolder.length());
7387
7388 Utf8Str log;
7389 if (idx == 0)
7390 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7391#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7392 else if (idx == 1)
7393 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7394 else
7395 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7396#else
7397 else
7398 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7399#endif
7400 return log;
7401}
7402
7403/**
7404 * Returns the full path to the machine's hardened log file.
7405 */
7406Utf8Str Machine::i_getHardeningLogFilename(void)
7407{
7408 Utf8Str strFilename;
7409 getLogFolder(strFilename);
7410 Assert(strFilename.length());
7411 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7412 return strFilename;
7413}
7414
7415
7416/**
7417 * Composes a unique saved state filename based on the current system time. The filename is
7418 * granular to the second so this will work so long as no more than one snapshot is taken on
7419 * a machine per second.
7420 *
7421 * Before version 4.1, we used this formula for saved state files:
7422 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7423 * which no longer works because saved state files can now be shared between the saved state of the
7424 * "saved" machine and an online snapshot, and the following would cause problems:
7425 * 1) save machine
7426 * 2) create online snapshot from that machine state --> reusing saved state file
7427 * 3) save machine again --> filename would be reused, breaking the online snapshot
7428 *
7429 * So instead we now use a timestamp.
7430 *
7431 * @param strStateFilePath
7432 */
7433
7434void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7435{
7436 AutoCaller autoCaller(this);
7437 AssertComRCReturnVoid(autoCaller.rc());
7438
7439 {
7440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7441 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7442 }
7443
7444 RTTIMESPEC ts;
7445 RTTimeNow(&ts);
7446 RTTIME time;
7447 RTTimeExplode(&time, &ts);
7448
7449 strStateFilePath += RTPATH_DELIMITER;
7450 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7451 time.i32Year, time.u8Month, time.u8MonthDay,
7452 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7453}
7454
7455/**
7456 * Returns the full path to the default video capture file.
7457 */
7458void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7459{
7460 AutoCaller autoCaller(this);
7461 AssertComRCReturnVoid(autoCaller.rc());
7462
7463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7464
7465 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7466 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7467 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7468}
7469
7470/**
7471 * Returns whether at least one USB controller is present for the VM.
7472 */
7473bool Machine::i_isUSBControllerPresent()
7474{
7475 AutoCaller autoCaller(this);
7476 AssertComRCReturn(autoCaller.rc(), false);
7477
7478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7479
7480 return (mUSBControllers->size() > 0);
7481}
7482
7483/**
7484 * @note Locks this object for writing, calls the client process
7485 * (inside the lock).
7486 */
7487HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7488 const Utf8Str &strFrontend,
7489 const Utf8Str &strEnvironment,
7490 ProgressProxy *aProgress)
7491{
7492 LogFlowThisFuncEnter();
7493
7494 AssertReturn(aControl, E_FAIL);
7495 AssertReturn(aProgress, E_FAIL);
7496 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7497
7498 AutoCaller autoCaller(this);
7499 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7500
7501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7502
7503 if (!mData->mRegistered)
7504 return setError(E_UNEXPECTED,
7505 tr("The machine '%s' is not registered"),
7506 mUserData->s.strName.c_str());
7507
7508 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7509
7510 /* The process started when launching a VM with separate UI/VM processes is always
7511 * the UI process, i.e. needs special handling as it won't claim the session. */
7512 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7513
7514 if (fSeparate)
7515 {
7516 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7517 return setError(VBOX_E_INVALID_OBJECT_STATE,
7518 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7519 mUserData->s.strName.c_str());
7520 }
7521 else
7522 {
7523 if ( mData->mSession.mState == SessionState_Locked
7524 || mData->mSession.mState == SessionState_Spawning
7525 || mData->mSession.mState == SessionState_Unlocking)
7526 return setError(VBOX_E_INVALID_OBJECT_STATE,
7527 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7528 mUserData->s.strName.c_str());
7529
7530 /* may not be busy */
7531 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7532 }
7533
7534 /* get the path to the executable */
7535 char szPath[RTPATH_MAX];
7536 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7537 size_t cchBufLeft = strlen(szPath);
7538 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7539 szPath[cchBufLeft] = 0;
7540 char *pszNamePart = szPath + cchBufLeft;
7541 cchBufLeft = sizeof(szPath) - cchBufLeft;
7542
7543 int vrc = VINF_SUCCESS;
7544 RTPROCESS pid = NIL_RTPROCESS;
7545
7546 RTENV env = RTENV_DEFAULT;
7547
7548 if (!strEnvironment.isEmpty())
7549 {
7550 char *newEnvStr = NULL;
7551
7552 do
7553 {
7554 /* clone the current environment */
7555 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7556 AssertRCBreakStmt(vrc2, vrc = vrc2);
7557
7558 newEnvStr = RTStrDup(strEnvironment.c_str());
7559 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7560
7561 /* put new variables to the environment
7562 * (ignore empty variable names here since RTEnv API
7563 * intentionally doesn't do that) */
7564 char *var = newEnvStr;
7565 for (char *p = newEnvStr; *p; ++p)
7566 {
7567 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7568 {
7569 *p = '\0';
7570 if (*var)
7571 {
7572 char *val = strchr(var, '=');
7573 if (val)
7574 {
7575 *val++ = '\0';
7576 vrc2 = RTEnvSetEx(env, var, val);
7577 }
7578 else
7579 vrc2 = RTEnvUnsetEx(env, var);
7580 if (RT_FAILURE(vrc2))
7581 break;
7582 }
7583 var = p + 1;
7584 }
7585 }
7586 if (RT_SUCCESS(vrc2) && *var)
7587 vrc2 = RTEnvPutEx(env, var);
7588
7589 AssertRCBreakStmt(vrc2, vrc = vrc2);
7590 }
7591 while (0);
7592
7593 if (newEnvStr != NULL)
7594 RTStrFree(newEnvStr);
7595 }
7596
7597 /* Hardening logging */
7598#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7599 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7600 {
7601 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7602 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7603 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7604 {
7605 Utf8Str strStartupLogDir = strHardeningLogFile;
7606 strStartupLogDir.stripFilename();
7607 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7608 file without stripping the file. */
7609 }
7610 strSupHardeningLogArg.append(strHardeningLogFile);
7611
7612 /* Remove legacy log filename to avoid confusion. */
7613 Utf8Str strOldStartupLogFile;
7614 getLogFolder(strOldStartupLogFile);
7615 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7616 RTFileDelete(strOldStartupLogFile.c_str());
7617 }
7618 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7619#else
7620 const char *pszSupHardeningLogArg = NULL;
7621#endif
7622
7623 Utf8Str strCanonicalName;
7624
7625#ifdef VBOX_WITH_QTGUI
7626 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7627 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7628 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7629 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7630 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7631 {
7632 strCanonicalName = "GUI/Qt";
7633# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7634 /* Modify the base path so that we don't need to use ".." below. */
7635 RTPathStripTrailingSlash(szPath);
7636 RTPathStripFilename(szPath);
7637 cchBufLeft = strlen(szPath);
7638 pszNamePart = szPath + cchBufLeft;
7639 cchBufLeft = sizeof(szPath) - cchBufLeft;
7640
7641# define OSX_APP_NAME "VirtualBoxVM"
7642# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7643
7644 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7645 if ( strAppOverride.contains(".")
7646 || strAppOverride.contains("/")
7647 || strAppOverride.contains("\\")
7648 || strAppOverride.contains(":"))
7649 strAppOverride.setNull();
7650 Utf8Str strAppPath;
7651 if (!strAppOverride.isEmpty())
7652 {
7653 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7654 Utf8Str strFullPath(szPath);
7655 strFullPath.append(strAppPath);
7656 /* there is a race, but people using this deserve the failure */
7657 if (!RTFileExists(strFullPath.c_str()))
7658 strAppOverride.setNull();
7659 }
7660 if (strAppOverride.isEmpty())
7661 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7662 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7663 strcpy(pszNamePart, strAppPath.c_str());
7664# else
7665 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7666 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7667 strcpy(pszNamePart, s_szVirtualBox_exe);
7668# endif
7669
7670 Utf8Str idStr = mData->mUuid.toString();
7671 const char *apszArgs[] =
7672 {
7673 szPath,
7674 "--comment", mUserData->s.strName.c_str(),
7675 "--startvm", idStr.c_str(),
7676 "--no-startvm-errormsgbox",
7677 NULL, /* For "--separate". */
7678 NULL, /* For "--sup-startup-log". */
7679 NULL
7680 };
7681 unsigned iArg = 6;
7682 if (fSeparate)
7683 apszArgs[iArg++] = "--separate";
7684 apszArgs[iArg++] = pszSupHardeningLogArg;
7685
7686 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7687 }
7688#else /* !VBOX_WITH_QTGUI */
7689 if (0)
7690 ;
7691#endif /* VBOX_WITH_QTGUI */
7692
7693 else
7694
7695#ifdef VBOX_WITH_VBOXSDL
7696 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7697 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7698 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7699 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7700 {
7701 strCanonicalName = "GUI/SDL";
7702 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7703 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7704 strcpy(pszNamePart, s_szVBoxSDL_exe);
7705
7706 Utf8Str idStr = mData->mUuid.toString();
7707 const char *apszArgs[] =
7708 {
7709 szPath,
7710 "--comment", mUserData->s.strName.c_str(),
7711 "--startvm", idStr.c_str(),
7712 NULL, /* For "--separate". */
7713 NULL, /* For "--sup-startup-log". */
7714 NULL
7715 };
7716 unsigned iArg = 5;
7717 if (fSeparate)
7718 apszArgs[iArg++] = "--separate";
7719 apszArgs[iArg++] = pszSupHardeningLogArg;
7720
7721 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7722 }
7723#else /* !VBOX_WITH_VBOXSDL */
7724 if (0)
7725 ;
7726#endif /* !VBOX_WITH_VBOXSDL */
7727
7728 else
7729
7730#ifdef VBOX_WITH_HEADLESS
7731 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7732 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7733 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7734 )
7735 {
7736 strCanonicalName = "headless";
7737 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7738 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7739 * and a VM works even if the server has not been installed.
7740 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7741 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7742 * differently in 4.0 and 3.x.
7743 */
7744 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7745 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7746 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7747
7748 Utf8Str idStr = mData->mUuid.toString();
7749 const char *apszArgs[] =
7750 {
7751 szPath,
7752 "--comment", mUserData->s.strName.c_str(),
7753 "--startvm", idStr.c_str(),
7754 "--vrde", "config",
7755 NULL, /* For "--capture". */
7756 NULL, /* For "--sup-startup-log". */
7757 NULL
7758 };
7759 unsigned iArg = 7;
7760 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7761 apszArgs[iArg++] = "--capture";
7762 apszArgs[iArg++] = pszSupHardeningLogArg;
7763
7764# ifdef RT_OS_WINDOWS
7765 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7766# else
7767 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7768# endif
7769 }
7770#else /* !VBOX_WITH_HEADLESS */
7771 if (0)
7772 ;
7773#endif /* !VBOX_WITH_HEADLESS */
7774 else
7775 {
7776 RTEnvDestroy(env);
7777 return setError(E_INVALIDARG,
7778 tr("Invalid frontend name: '%s'"),
7779 strFrontend.c_str());
7780 }
7781
7782 RTEnvDestroy(env);
7783
7784 if (RT_FAILURE(vrc))
7785 return setError(VBOX_E_IPRT_ERROR,
7786 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7787 mUserData->s.strName.c_str(), vrc);
7788
7789 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7790
7791 if (!fSeparate)
7792 {
7793 /*
7794 * Note that we don't release the lock here before calling the client,
7795 * because it doesn't need to call us back if called with a NULL argument.
7796 * Releasing the lock here is dangerous because we didn't prepare the
7797 * launch data yet, but the client we've just started may happen to be
7798 * too fast and call LockMachine() that will fail (because of PID, etc.),
7799 * so that the Machine will never get out of the Spawning session state.
7800 */
7801
7802 /* inform the session that it will be a remote one */
7803 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7804#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7805 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7806#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7807 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7808#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7809 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7810
7811 if (FAILED(rc))
7812 {
7813 /* restore the session state */
7814 mData->mSession.mState = SessionState_Unlocked;
7815 alock.release();
7816 mParent->i_addProcessToReap(pid);
7817 /* The failure may occur w/o any error info (from RPC), so provide one */
7818 return setError(VBOX_E_VM_ERROR,
7819 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7820 }
7821
7822 /* attach launch data to the machine */
7823 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7824 mData->mSession.mRemoteControls.push_back(aControl);
7825 mData->mSession.mProgress = aProgress;
7826 mData->mSession.mPID = pid;
7827 mData->mSession.mState = SessionState_Spawning;
7828 Assert(strCanonicalName.isNotEmpty());
7829 mData->mSession.mName = strCanonicalName;
7830 }
7831 else
7832 {
7833 /* For separate UI process we declare the launch as completed instantly, as the
7834 * actual headless VM start may or may not come. No point in remembering anything
7835 * yet, as what matters for us is when the headless VM gets started. */
7836 aProgress->i_notifyComplete(S_OK);
7837 }
7838
7839 alock.release();
7840 mParent->i_addProcessToReap(pid);
7841
7842 LogFlowThisFuncLeave();
7843 return S_OK;
7844}
7845
7846/**
7847 * Returns @c true if the given session machine instance has an open direct
7848 * session (and optionally also for direct sessions which are closing) and
7849 * returns the session control machine instance if so.
7850 *
7851 * Note that when the method returns @c false, the arguments remain unchanged.
7852 *
7853 * @param aMachine Session machine object.
7854 * @param aControl Direct session control object (optional).
7855 * @param aRequireVM If true then only allow VM sessions.
7856 * @param aAllowClosing If true then additionally a session which is currently
7857 * being closed will also be allowed.
7858 *
7859 * @note locks this object for reading.
7860 */
7861bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7862 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7863 bool aRequireVM /*= false*/,
7864 bool aAllowClosing /*= false*/)
7865{
7866 AutoLimitedCaller autoCaller(this);
7867 AssertComRCReturn(autoCaller.rc(), false);
7868
7869 /* just return false for inaccessible machines */
7870 if (getObjectState().getState() != ObjectState::Ready)
7871 return false;
7872
7873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7874
7875 if ( ( mData->mSession.mState == SessionState_Locked
7876 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7877 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7878 )
7879 {
7880 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7881
7882 aMachine = mData->mSession.mMachine;
7883
7884 if (aControl != NULL)
7885 *aControl = mData->mSession.mDirectControl;
7886
7887 return true;
7888 }
7889
7890 return false;
7891}
7892
7893/**
7894 * Returns @c true if the given machine has an spawning direct session.
7895 *
7896 * @note locks this object for reading.
7897 */
7898bool Machine::i_isSessionSpawning()
7899{
7900 AutoLimitedCaller autoCaller(this);
7901 AssertComRCReturn(autoCaller.rc(), false);
7902
7903 /* just return false for inaccessible machines */
7904 if (getObjectState().getState() != ObjectState::Ready)
7905 return false;
7906
7907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7908
7909 if (mData->mSession.mState == SessionState_Spawning)
7910 return true;
7911
7912 return false;
7913}
7914
7915/**
7916 * Called from the client watcher thread to check for unexpected client process
7917 * death during Session_Spawning state (e.g. before it successfully opened a
7918 * direct session).
7919 *
7920 * On Win32 and on OS/2, this method is called only when we've got the
7921 * direct client's process termination notification, so it always returns @c
7922 * true.
7923 *
7924 * On other platforms, this method returns @c true if the client process is
7925 * terminated and @c false if it's still alive.
7926 *
7927 * @note Locks this object for writing.
7928 */
7929bool Machine::i_checkForSpawnFailure()
7930{
7931 AutoCaller autoCaller(this);
7932 if (!autoCaller.isOk())
7933 {
7934 /* nothing to do */
7935 LogFlowThisFunc(("Already uninitialized!\n"));
7936 return true;
7937 }
7938
7939 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7940
7941 if (mData->mSession.mState != SessionState_Spawning)
7942 {
7943 /* nothing to do */
7944 LogFlowThisFunc(("Not spawning any more!\n"));
7945 return true;
7946 }
7947
7948 HRESULT rc = S_OK;
7949
7950 /* PID not yet initialized, skip check. */
7951 if (mData->mSession.mPID == NIL_RTPROCESS)
7952 return false;
7953
7954 RTPROCSTATUS status;
7955 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7956
7957 if (vrc != VERR_PROCESS_RUNNING)
7958 {
7959 Utf8Str strExtraInfo;
7960
7961#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7962 /* If the startup logfile exists and is of non-zero length, tell the
7963 user to look there for more details to encourage them to attach it
7964 when reporting startup issues. */
7965 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7966 uint64_t cbStartupLogFile = 0;
7967 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7968 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7969 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7970#endif
7971
7972 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7973 rc = setError(E_FAIL,
7974 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7975 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7976 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7977 rc = setError(E_FAIL,
7978 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7979 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7980 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7981 rc = setError(E_FAIL,
7982 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7983 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7984 else
7985 rc = setError(E_FAIL,
7986 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7987 i_getName().c_str(), vrc, strExtraInfo.c_str());
7988 }
7989
7990 if (FAILED(rc))
7991 {
7992 /* Close the remote session, remove the remote control from the list
7993 * and reset session state to Closed (@note keep the code in sync with
7994 * the relevant part in LockMachine()). */
7995
7996 Assert(mData->mSession.mRemoteControls.size() == 1);
7997 if (mData->mSession.mRemoteControls.size() == 1)
7998 {
7999 ErrorInfoKeeper eik;
8000 mData->mSession.mRemoteControls.front()->Uninitialize();
8001 }
8002
8003 mData->mSession.mRemoteControls.clear();
8004 mData->mSession.mState = SessionState_Unlocked;
8005
8006 /* finalize the progress after setting the state */
8007 if (!mData->mSession.mProgress.isNull())
8008 {
8009 mData->mSession.mProgress->notifyComplete(rc);
8010 mData->mSession.mProgress.setNull();
8011 }
8012
8013 mData->mSession.mPID = NIL_RTPROCESS;
8014
8015 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8016 return true;
8017 }
8018
8019 return false;
8020}
8021
8022/**
8023 * Checks whether the machine can be registered. If so, commits and saves
8024 * all settings.
8025 *
8026 * @note Must be called from mParent's write lock. Locks this object and
8027 * children for writing.
8028 */
8029HRESULT Machine::i_prepareRegister()
8030{
8031 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8032
8033 AutoLimitedCaller autoCaller(this);
8034 AssertComRCReturnRC(autoCaller.rc());
8035
8036 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8037
8038 /* wait for state dependents to drop to zero */
8039 i_ensureNoStateDependencies();
8040
8041 if (!mData->mAccessible)
8042 return setError(VBOX_E_INVALID_OBJECT_STATE,
8043 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8044 mUserData->s.strName.c_str(),
8045 mData->mUuid.toString().c_str());
8046
8047 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8048
8049 if (mData->mRegistered)
8050 return setError(VBOX_E_INVALID_OBJECT_STATE,
8051 tr("The machine '%s' with UUID {%s} is already registered"),
8052 mUserData->s.strName.c_str(),
8053 mData->mUuid.toString().c_str());
8054
8055 HRESULT rc = S_OK;
8056
8057 // Ensure the settings are saved. If we are going to be registered and
8058 // no config file exists yet, create it by calling i_saveSettings() too.
8059 if ( (mData->flModifications)
8060 || (!mData->pMachineConfigFile->fileExists())
8061 )
8062 {
8063 rc = i_saveSettings(NULL);
8064 // no need to check whether VirtualBox.xml needs saving too since
8065 // we can't have a machine XML file rename pending
8066 if (FAILED(rc)) return rc;
8067 }
8068
8069 /* more config checking goes here */
8070
8071 if (SUCCEEDED(rc))
8072 {
8073 /* we may have had implicit modifications we want to fix on success */
8074 i_commit();
8075
8076 mData->mRegistered = true;
8077 }
8078 else
8079 {
8080 /* we may have had implicit modifications we want to cancel on failure*/
8081 i_rollback(false /* aNotify */);
8082 }
8083
8084 return rc;
8085}
8086
8087/**
8088 * Increases the number of objects dependent on the machine state or on the
8089 * registered state. Guarantees that these two states will not change at least
8090 * until #i_releaseStateDependency() is called.
8091 *
8092 * Depending on the @a aDepType value, additional state checks may be made.
8093 * These checks will set extended error info on failure. See
8094 * #i_checkStateDependency() for more info.
8095 *
8096 * If this method returns a failure, the dependency is not added and the caller
8097 * is not allowed to rely on any particular machine state or registration state
8098 * value and may return the failed result code to the upper level.
8099 *
8100 * @param aDepType Dependency type to add.
8101 * @param aState Current machine state (NULL if not interested).
8102 * @param aRegistered Current registered state (NULL if not interested).
8103 *
8104 * @note Locks this object for writing.
8105 */
8106HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8107 MachineState_T *aState /* = NULL */,
8108 BOOL *aRegistered /* = NULL */)
8109{
8110 AutoCaller autoCaller(this);
8111 AssertComRCReturnRC(autoCaller.rc());
8112
8113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8114
8115 HRESULT rc = i_checkStateDependency(aDepType);
8116 if (FAILED(rc)) return rc;
8117
8118 {
8119 if (mData->mMachineStateChangePending != 0)
8120 {
8121 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8122 * drop to zero so don't add more. It may make sense to wait a bit
8123 * and retry before reporting an error (since the pending state
8124 * transition should be really quick) but let's just assert for
8125 * now to see if it ever happens on practice. */
8126
8127 AssertFailed();
8128
8129 return setError(E_ACCESSDENIED,
8130 tr("Machine state change is in progress. Please retry the operation later."));
8131 }
8132
8133 ++mData->mMachineStateDeps;
8134 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8135 }
8136
8137 if (aState)
8138 *aState = mData->mMachineState;
8139 if (aRegistered)
8140 *aRegistered = mData->mRegistered;
8141
8142 return S_OK;
8143}
8144
8145/**
8146 * Decreases the number of objects dependent on the machine state.
8147 * Must always complete the #i_addStateDependency() call after the state
8148 * dependency is no more necessary.
8149 */
8150void Machine::i_releaseStateDependency()
8151{
8152 AutoCaller autoCaller(this);
8153 AssertComRCReturnVoid(autoCaller.rc());
8154
8155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8156
8157 /* releaseStateDependency() w/o addStateDependency()? */
8158 AssertReturnVoid(mData->mMachineStateDeps != 0);
8159 -- mData->mMachineStateDeps;
8160
8161 if (mData->mMachineStateDeps == 0)
8162 {
8163 /* inform i_ensureNoStateDependencies() that there are no more deps */
8164 if (mData->mMachineStateChangePending != 0)
8165 {
8166 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8167 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8168 }
8169 }
8170}
8171
8172Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8173{
8174 /* start with nothing found */
8175 Utf8Str strResult("");
8176
8177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8178
8179 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8180 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8181 // found:
8182 strResult = it->second; // source is a Utf8Str
8183
8184 return strResult;
8185}
8186
8187// protected methods
8188/////////////////////////////////////////////////////////////////////////////
8189
8190/**
8191 * Performs machine state checks based on the @a aDepType value. If a check
8192 * fails, this method will set extended error info, otherwise it will return
8193 * S_OK. It is supposed, that on failure, the caller will immediately return
8194 * the return value of this method to the upper level.
8195 *
8196 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8197 *
8198 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8199 * current state of this machine object allows to change settings of the
8200 * machine (i.e. the machine is not registered, or registered but not running
8201 * and not saved). It is useful to call this method from Machine setters
8202 * before performing any change.
8203 *
8204 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8205 * as for MutableStateDep except that if the machine is saved, S_OK is also
8206 * returned. This is useful in setters which allow changing machine
8207 * properties when it is in the saved state.
8208 *
8209 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8210 * if the current state of this machine object allows to change runtime
8211 * changeable settings of the machine (i.e. the machine is not registered, or
8212 * registered but either running or not running and not saved). It is useful
8213 * to call this method from Machine setters before performing any changes to
8214 * runtime changeable settings.
8215 *
8216 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8217 * the same as for MutableOrRunningStateDep except that if the machine is
8218 * saved, S_OK is also returned. This is useful in setters which allow
8219 * changing runtime and saved state changeable machine properties.
8220 *
8221 * @param aDepType Dependency type to check.
8222 *
8223 * @note Non Machine based classes should use #i_addStateDependency() and
8224 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8225 * template.
8226 *
8227 * @note This method must be called from under this object's read or write
8228 * lock.
8229 */
8230HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8231{
8232 switch (aDepType)
8233 {
8234 case AnyStateDep:
8235 {
8236 break;
8237 }
8238 case MutableStateDep:
8239 {
8240 if ( mData->mRegistered
8241 && ( !i_isSessionMachine()
8242 || ( mData->mMachineState != MachineState_Aborted
8243 && mData->mMachineState != MachineState_Teleported
8244 && mData->mMachineState != MachineState_PoweredOff
8245 )
8246 )
8247 )
8248 return setError(VBOX_E_INVALID_VM_STATE,
8249 tr("The machine is not mutable (state is %s)"),
8250 Global::stringifyMachineState(mData->mMachineState));
8251 break;
8252 }
8253 case MutableOrSavedStateDep:
8254 {
8255 if ( mData->mRegistered
8256 && ( !i_isSessionMachine()
8257 || ( mData->mMachineState != MachineState_Aborted
8258 && mData->mMachineState != MachineState_Teleported
8259 && mData->mMachineState != MachineState_Saved
8260 && mData->mMachineState != MachineState_PoweredOff
8261 )
8262 )
8263 )
8264 return setError(VBOX_E_INVALID_VM_STATE,
8265 tr("The machine is not mutable or saved (state is %s)"),
8266 Global::stringifyMachineState(mData->mMachineState));
8267 break;
8268 }
8269 case MutableOrRunningStateDep:
8270 {
8271 if ( mData->mRegistered
8272 && ( !i_isSessionMachine()
8273 || ( mData->mMachineState != MachineState_Aborted
8274 && mData->mMachineState != MachineState_Teleported
8275 && mData->mMachineState != MachineState_PoweredOff
8276 && !Global::IsOnline(mData->mMachineState)
8277 )
8278 )
8279 )
8280 return setError(VBOX_E_INVALID_VM_STATE,
8281 tr("The machine is not mutable or running (state is %s)"),
8282 Global::stringifyMachineState(mData->mMachineState));
8283 break;
8284 }
8285 case MutableOrSavedOrRunningStateDep:
8286 {
8287 if ( mData->mRegistered
8288 && ( !i_isSessionMachine()
8289 || ( mData->mMachineState != MachineState_Aborted
8290 && mData->mMachineState != MachineState_Teleported
8291 && mData->mMachineState != MachineState_Saved
8292 && mData->mMachineState != MachineState_PoweredOff
8293 && !Global::IsOnline(mData->mMachineState)
8294 )
8295 )
8296 )
8297 return setError(VBOX_E_INVALID_VM_STATE,
8298 tr("The machine is not mutable, saved or running (state is %s)"),
8299 Global::stringifyMachineState(mData->mMachineState));
8300 break;
8301 }
8302 }
8303
8304 return S_OK;
8305}
8306
8307/**
8308 * Helper to initialize all associated child objects and allocate data
8309 * structures.
8310 *
8311 * This method must be called as a part of the object's initialization procedure
8312 * (usually done in the #init() method).
8313 *
8314 * @note Must be called only from #init() or from #i_registeredInit().
8315 */
8316HRESULT Machine::initDataAndChildObjects()
8317{
8318 AutoCaller autoCaller(this);
8319 AssertComRCReturnRC(autoCaller.rc());
8320 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8321 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8322
8323 AssertReturn(!mData->mAccessible, E_FAIL);
8324
8325 /* allocate data structures */
8326 mSSData.allocate();
8327 mUserData.allocate();
8328 mHWData.allocate();
8329 mMediumAttachments.allocate();
8330 mStorageControllers.allocate();
8331 mUSBControllers.allocate();
8332
8333 /* initialize mOSTypeId */
8334 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8335
8336/** @todo r=bird: init() methods never fails, right? Why don't we make them
8337 * return void then! */
8338
8339 /* create associated BIOS settings object */
8340 unconst(mBIOSSettings).createObject();
8341 mBIOSSettings->init(this);
8342
8343 /* create an associated VRDE object (default is disabled) */
8344 unconst(mVRDEServer).createObject();
8345 mVRDEServer->init(this);
8346
8347 /* create associated serial port objects */
8348 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8349 {
8350 unconst(mSerialPorts[slot]).createObject();
8351 mSerialPorts[slot]->init(this, slot);
8352 }
8353
8354 /* create associated parallel port objects */
8355 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8356 {
8357 unconst(mParallelPorts[slot]).createObject();
8358 mParallelPorts[slot]->init(this, slot);
8359 }
8360
8361 /* create the audio adapter object (always present, default is disabled) */
8362 unconst(mAudioAdapter).createObject();
8363 mAudioAdapter->init(this);
8364
8365 /* create the USB device filters object (always present) */
8366 unconst(mUSBDeviceFilters).createObject();
8367 mUSBDeviceFilters->init(this);
8368
8369 /* create associated network adapter objects */
8370 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8371 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8372 {
8373 unconst(mNetworkAdapters[slot]).createObject();
8374 mNetworkAdapters[slot]->init(this, slot);
8375 }
8376
8377 /* create the bandwidth control */
8378 unconst(mBandwidthControl).createObject();
8379 mBandwidthControl->init(this);
8380
8381 return S_OK;
8382}
8383
8384/**
8385 * Helper to uninitialize all associated child objects and to free all data
8386 * structures.
8387 *
8388 * This method must be called as a part of the object's uninitialization
8389 * procedure (usually done in the #uninit() method).
8390 *
8391 * @note Must be called only from #uninit() or from #i_registeredInit().
8392 */
8393void Machine::uninitDataAndChildObjects()
8394{
8395 AutoCaller autoCaller(this);
8396 AssertComRCReturnVoid(autoCaller.rc());
8397 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8398 || getObjectState().getState() == ObjectState::Limited);
8399
8400 /* tell all our other child objects we've been uninitialized */
8401 if (mBandwidthControl)
8402 {
8403 mBandwidthControl->uninit();
8404 unconst(mBandwidthControl).setNull();
8405 }
8406
8407 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8408 {
8409 if (mNetworkAdapters[slot])
8410 {
8411 mNetworkAdapters[slot]->uninit();
8412 unconst(mNetworkAdapters[slot]).setNull();
8413 }
8414 }
8415
8416 if (mUSBDeviceFilters)
8417 {
8418 mUSBDeviceFilters->uninit();
8419 unconst(mUSBDeviceFilters).setNull();
8420 }
8421
8422 if (mAudioAdapter)
8423 {
8424 mAudioAdapter->uninit();
8425 unconst(mAudioAdapter).setNull();
8426 }
8427
8428 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8429 {
8430 if (mParallelPorts[slot])
8431 {
8432 mParallelPorts[slot]->uninit();
8433 unconst(mParallelPorts[slot]).setNull();
8434 }
8435 }
8436
8437 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8438 {
8439 if (mSerialPorts[slot])
8440 {
8441 mSerialPorts[slot]->uninit();
8442 unconst(mSerialPorts[slot]).setNull();
8443 }
8444 }
8445
8446 if (mVRDEServer)
8447 {
8448 mVRDEServer->uninit();
8449 unconst(mVRDEServer).setNull();
8450 }
8451
8452 if (mBIOSSettings)
8453 {
8454 mBIOSSettings->uninit();
8455 unconst(mBIOSSettings).setNull();
8456 }
8457
8458 /* Deassociate media (only when a real Machine or a SnapshotMachine
8459 * instance is uninitialized; SessionMachine instances refer to real
8460 * Machine media). This is necessary for a clean re-initialization of
8461 * the VM after successfully re-checking the accessibility state. Note
8462 * that in case of normal Machine or SnapshotMachine uninitialization (as
8463 * a result of unregistering or deleting the snapshot), outdated media
8464 * attachments will already be uninitialized and deleted, so this
8465 * code will not affect them. */
8466 if ( !mMediumAttachments.isNull()
8467 && !i_isSessionMachine()
8468 )
8469 {
8470 for (MediumAttachmentList::const_iterator
8471 it = mMediumAttachments->begin();
8472 it != mMediumAttachments->end();
8473 ++it)
8474 {
8475 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8476 if (pMedium.isNull())
8477 continue;
8478 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8479 AssertComRC(rc);
8480 }
8481 }
8482
8483 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8484 {
8485 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8486 if (mData->mFirstSnapshot)
8487 {
8488 // snapshots tree is protected by machine write lock; strictly
8489 // this isn't necessary here since we're deleting the entire
8490 // machine, but otherwise we assert in Snapshot::uninit()
8491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8492 mData->mFirstSnapshot->uninit();
8493 mData->mFirstSnapshot.setNull();
8494 }
8495
8496 mData->mCurrentSnapshot.setNull();
8497 }
8498
8499 /* free data structures (the essential mData structure is not freed here
8500 * since it may be still in use) */
8501 mMediumAttachments.free();
8502 mStorageControllers.free();
8503 mUSBControllers.free();
8504 mHWData.free();
8505 mUserData.free();
8506 mSSData.free();
8507}
8508
8509/**
8510 * Returns a pointer to the Machine object for this machine that acts like a
8511 * parent for complex machine data objects such as shared folders, etc.
8512 *
8513 * For primary Machine objects and for SnapshotMachine objects, returns this
8514 * object's pointer itself. For SessionMachine objects, returns the peer
8515 * (primary) machine pointer.
8516 */
8517Machine *Machine::i_getMachine()
8518{
8519 if (i_isSessionMachine())
8520 return (Machine*)mPeer;
8521 return this;
8522}
8523
8524/**
8525 * Makes sure that there are no machine state dependents. If necessary, waits
8526 * for the number of dependents to drop to zero.
8527 *
8528 * Make sure this method is called from under this object's write lock to
8529 * guarantee that no new dependents may be added when this method returns
8530 * control to the caller.
8531 *
8532 * @note Locks this object for writing. The lock will be released while waiting
8533 * (if necessary).
8534 *
8535 * @warning To be used only in methods that change the machine state!
8536 */
8537void Machine::i_ensureNoStateDependencies()
8538{
8539 AssertReturnVoid(isWriteLockOnCurrentThread());
8540
8541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8542
8543 /* Wait for all state dependents if necessary */
8544 if (mData->mMachineStateDeps != 0)
8545 {
8546 /* lazy semaphore creation */
8547 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8548 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8549
8550 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8551 mData->mMachineStateDeps));
8552
8553 ++mData->mMachineStateChangePending;
8554
8555 /* reset the semaphore before waiting, the last dependent will signal
8556 * it */
8557 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8558
8559 alock.release();
8560
8561 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8562
8563 alock.acquire();
8564
8565 -- mData->mMachineStateChangePending;
8566 }
8567}
8568
8569/**
8570 * Changes the machine state and informs callbacks.
8571 *
8572 * This method is not intended to fail so it either returns S_OK or asserts (and
8573 * returns a failure).
8574 *
8575 * @note Locks this object for writing.
8576 */
8577HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8578{
8579 LogFlowThisFuncEnter();
8580 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8581 Assert(aMachineState != MachineState_Null);
8582
8583 AutoCaller autoCaller(this);
8584 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8585
8586 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8587
8588 /* wait for state dependents to drop to zero */
8589 i_ensureNoStateDependencies();
8590
8591 MachineState_T const enmOldState = mData->mMachineState;
8592 if (enmOldState != aMachineState)
8593 {
8594 mData->mMachineState = aMachineState;
8595 RTTimeNow(&mData->mLastStateChange);
8596
8597#ifdef VBOX_WITH_DTRACE_R3_MAIN
8598 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8599#endif
8600 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8601 }
8602
8603 LogFlowThisFuncLeave();
8604 return S_OK;
8605}
8606
8607/**
8608 * Searches for a shared folder with the given logical name
8609 * in the collection of shared folders.
8610 *
8611 * @param aName logical name of the shared folder
8612 * @param aSharedFolder where to return the found object
8613 * @param aSetError whether to set the error info if the folder is
8614 * not found
8615 * @return
8616 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8617 *
8618 * @note
8619 * must be called from under the object's lock!
8620 */
8621HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8622 ComObjPtr<SharedFolder> &aSharedFolder,
8623 bool aSetError /* = false */)
8624{
8625 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8626 for (HWData::SharedFolderList::const_iterator
8627 it = mHWData->mSharedFolders.begin();
8628 it != mHWData->mSharedFolders.end();
8629 ++it)
8630 {
8631 SharedFolder *pSF = *it;
8632 AutoCaller autoCaller(pSF);
8633 if (pSF->i_getName() == aName)
8634 {
8635 aSharedFolder = pSF;
8636 rc = S_OK;
8637 break;
8638 }
8639 }
8640
8641 if (aSetError && FAILED(rc))
8642 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8643
8644 return rc;
8645}
8646
8647/**
8648 * Initializes all machine instance data from the given settings structures
8649 * from XML. The exception is the machine UUID which needs special handling
8650 * depending on the caller's use case, so the caller needs to set that herself.
8651 *
8652 * This gets called in several contexts during machine initialization:
8653 *
8654 * -- When machine XML exists on disk already and needs to be loaded into memory,
8655 * for example, from #i_registeredInit() to load all registered machines on
8656 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8657 * attached to the machine should be part of some media registry already.
8658 *
8659 * -- During OVF import, when a machine config has been constructed from an
8660 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8661 * ensure that the media listed as attachments in the config (which have
8662 * been imported from the OVF) receive the correct registry ID.
8663 *
8664 * -- During VM cloning.
8665 *
8666 * @param config Machine settings from XML.
8667 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8668 * for each attached medium in the config.
8669 * @return
8670 */
8671HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8672 const Guid *puuidRegistry)
8673{
8674 // copy name, description, OS type, teleporter, UTC etc.
8675 mUserData->s = config.machineUserData;
8676
8677 // look up the object by Id to check it is valid
8678 ComObjPtr<GuestOSType> pGuestOSType;
8679 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8680 pGuestOSType);
8681 if (FAILED(rc)) return rc;
8682 mUserData->s.strOsType = pGuestOSType->i_id();
8683
8684 // stateFile (optional)
8685 if (config.strStateFile.isEmpty())
8686 mSSData->strStateFilePath.setNull();
8687 else
8688 {
8689 Utf8Str stateFilePathFull(config.strStateFile);
8690 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8691 if (RT_FAILURE(vrc))
8692 return setError(E_FAIL,
8693 tr("Invalid saved state file path '%s' (%Rrc)"),
8694 config.strStateFile.c_str(),
8695 vrc);
8696 mSSData->strStateFilePath = stateFilePathFull;
8697 }
8698
8699 // snapshot folder needs special processing so set it again
8700 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8701 if (FAILED(rc)) return rc;
8702
8703 /* Copy the extra data items (config may or may not be the same as
8704 * mData->pMachineConfigFile) if necessary. When loading the XML files
8705 * from disk they are the same, but not for OVF import. */
8706 if (mData->pMachineConfigFile != &config)
8707 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8708
8709 /* currentStateModified (optional, default is true) */
8710 mData->mCurrentStateModified = config.fCurrentStateModified;
8711
8712 mData->mLastStateChange = config.timeLastStateChange;
8713
8714 /*
8715 * note: all mUserData members must be assigned prior this point because
8716 * we need to commit changes in order to let mUserData be shared by all
8717 * snapshot machine instances.
8718 */
8719 mUserData.commitCopy();
8720
8721 // machine registry, if present (must be loaded before snapshots)
8722 if (config.canHaveOwnMediaRegistry())
8723 {
8724 // determine machine folder
8725 Utf8Str strMachineFolder = i_getSettingsFileFull();
8726 strMachineFolder.stripFilename();
8727 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8728 config.mediaRegistry,
8729 strMachineFolder);
8730 if (FAILED(rc)) return rc;
8731 }
8732
8733 /* Snapshot node (optional) */
8734 size_t cRootSnapshots;
8735 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8736 {
8737 // there must be only one root snapshot
8738 Assert(cRootSnapshots == 1);
8739
8740 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8741
8742 rc = i_loadSnapshot(snap,
8743 config.uuidCurrentSnapshot,
8744 NULL); // no parent == first snapshot
8745 if (FAILED(rc)) return rc;
8746 }
8747
8748 // hardware data
8749 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8750 if (FAILED(rc)) return rc;
8751
8752 /*
8753 * NOTE: the assignment below must be the last thing to do,
8754 * otherwise it will be not possible to change the settings
8755 * somewhere in the code above because all setters will be
8756 * blocked by i_checkStateDependency(MutableStateDep).
8757 */
8758
8759 /* set the machine state to Aborted or Saved when appropriate */
8760 if (config.fAborted)
8761 {
8762 mSSData->strStateFilePath.setNull();
8763
8764 /* no need to use i_setMachineState() during init() */
8765 mData->mMachineState = MachineState_Aborted;
8766 }
8767 else if (!mSSData->strStateFilePath.isEmpty())
8768 {
8769 /* no need to use i_setMachineState() during init() */
8770 mData->mMachineState = MachineState_Saved;
8771 }
8772
8773 // after loading settings, we are no longer different from the XML on disk
8774 mData->flModifications = 0;
8775
8776 return S_OK;
8777}
8778
8779/**
8780 * Recursively loads all snapshots starting from the given.
8781 *
8782 * @param data snapshot settings.
8783 * @param aCurSnapshotId Current snapshot ID from the settings file.
8784 * @param aParentSnapshot Parent snapshot.
8785 */
8786HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8787 const Guid &aCurSnapshotId,
8788 Snapshot *aParentSnapshot)
8789{
8790 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8791 AssertReturn(!i_isSessionMachine(), E_FAIL);
8792
8793 HRESULT rc = S_OK;
8794
8795 Utf8Str strStateFile;
8796 if (!data.strStateFile.isEmpty())
8797 {
8798 /* optional */
8799 strStateFile = data.strStateFile;
8800 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8801 if (RT_FAILURE(vrc))
8802 return setError(E_FAIL,
8803 tr("Invalid saved state file path '%s' (%Rrc)"),
8804 strStateFile.c_str(),
8805 vrc);
8806 }
8807
8808 /* create a snapshot machine object */
8809 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8810 pSnapshotMachine.createObject();
8811 rc = pSnapshotMachine->initFromSettings(this,
8812 data.hardware,
8813 &data.debugging,
8814 &data.autostart,
8815 data.uuid.ref(),
8816 strStateFile);
8817 if (FAILED(rc)) return rc;
8818
8819 /* create a snapshot object */
8820 ComObjPtr<Snapshot> pSnapshot;
8821 pSnapshot.createObject();
8822 /* initialize the snapshot */
8823 rc = pSnapshot->init(mParent, // VirtualBox object
8824 data.uuid,
8825 data.strName,
8826 data.strDescription,
8827 data.timestamp,
8828 pSnapshotMachine,
8829 aParentSnapshot);
8830 if (FAILED(rc)) return rc;
8831
8832 /* memorize the first snapshot if necessary */
8833 if (!mData->mFirstSnapshot)
8834 mData->mFirstSnapshot = pSnapshot;
8835
8836 /* memorize the current snapshot when appropriate */
8837 if ( !mData->mCurrentSnapshot
8838 && pSnapshot->i_getId() == aCurSnapshotId
8839 )
8840 mData->mCurrentSnapshot = pSnapshot;
8841
8842 // now create the children
8843 for (settings::SnapshotsList::const_iterator
8844 it = data.llChildSnapshots.begin();
8845 it != data.llChildSnapshots.end();
8846 ++it)
8847 {
8848 const settings::Snapshot &childData = *it;
8849 // recurse
8850 rc = i_loadSnapshot(childData,
8851 aCurSnapshotId,
8852 pSnapshot); // parent = the one we created above
8853 if (FAILED(rc)) return rc;
8854 }
8855
8856 return rc;
8857}
8858
8859/**
8860 * Loads settings into mHWData.
8861 *
8862 * @param puuidRegistry Registry ID.
8863 * @param puuidSnapshot Snapshot ID
8864 * @param data Reference to the hardware settings.
8865 * @param pDbg Pointer to the debugging settings.
8866 * @param pAutostart Pointer to the autostart settings.
8867 */
8868HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8869 const Guid *puuidSnapshot,
8870 const settings::Hardware &data,
8871 const settings::Debugging *pDbg,
8872 const settings::Autostart *pAutostart)
8873{
8874 AssertReturn(!i_isSessionMachine(), E_FAIL);
8875
8876 HRESULT rc = S_OK;
8877
8878 try
8879 {
8880 ComObjPtr<GuestOSType> pGuestOSType;
8881 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8882 pGuestOSType);
8883 if (FAILED(rc))
8884 return rc;
8885
8886 /* The hardware version attribute (optional). */
8887 mHWData->mHWVersion = data.strVersion;
8888 mHWData->mHardwareUUID = data.uuid;
8889
8890 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8891 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8892 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8893 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8894 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8895 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8896 mHWData->mPAEEnabled = data.fPAE;
8897 mHWData->mLongMode = data.enmLongMode;
8898 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8899 mHWData->mAPIC = data.fAPIC;
8900 mHWData->mX2APIC = data.fX2APIC;
8901 mHWData->mCPUCount = data.cCPUs;
8902 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8903 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8904 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8905 mHWData->mCpuProfile = data.strCpuProfile;
8906
8907 // cpu
8908 if (mHWData->mCPUHotPlugEnabled)
8909 {
8910 for (settings::CpuList::const_iterator
8911 it = data.llCpus.begin();
8912 it != data.llCpus.end();
8913 ++it)
8914 {
8915 const settings::Cpu &cpu = *it;
8916
8917 mHWData->mCPUAttached[cpu.ulId] = true;
8918 }
8919 }
8920
8921 // cpuid leafs
8922 for (settings::CpuIdLeafsList::const_iterator
8923 it = data.llCpuIdLeafs.begin();
8924 it != data.llCpuIdLeafs.end();
8925 ++it)
8926 {
8927 const settings::CpuIdLeaf &rLeaf= *it;
8928 if ( rLeaf.idx < UINT32_C(0x20)
8929 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8930 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8931 mHWData->mCpuIdLeafList.push_back(rLeaf);
8932 /* else: just ignore */
8933 }
8934
8935 mHWData->mMemorySize = data.ulMemorySizeMB;
8936 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8937
8938 // boot order
8939 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8940 {
8941 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8942 if (it == data.mapBootOrder.end())
8943 mHWData->mBootOrder[i] = DeviceType_Null;
8944 else
8945 mHWData->mBootOrder[i] = it->second;
8946 }
8947
8948 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8949 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8950 mHWData->mMonitorCount = data.cMonitors;
8951 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8952 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8953 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8954 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8955 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8956 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8957 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8958 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8959 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8960 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8961 if (!data.strVideoCaptureFile.isEmpty())
8962 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8963 else
8964 mHWData->mVideoCaptureFile.setNull();
8965 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
8966 mHWData->mFirmwareType = data.firmwareType;
8967 mHWData->mPointingHIDType = data.pointingHIDType;
8968 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8969 mHWData->mChipsetType = data.chipsetType;
8970 mHWData->mParavirtProvider = data.paravirtProvider;
8971 mHWData->mParavirtDebug = data.strParavirtDebug;
8972 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8973 mHWData->mHPETEnabled = data.fHPETEnabled;
8974
8975 /* VRDEServer */
8976 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8977 if (FAILED(rc)) return rc;
8978
8979 /* BIOS */
8980 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8981 if (FAILED(rc)) return rc;
8982
8983 // Bandwidth control (must come before network adapters)
8984 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8985 if (FAILED(rc)) return rc;
8986
8987 /* Shared folders */
8988 for (settings::USBControllerList::const_iterator
8989 it = data.usbSettings.llUSBControllers.begin();
8990 it != data.usbSettings.llUSBControllers.end();
8991 ++it)
8992 {
8993 const settings::USBController &settingsCtrl = *it;
8994 ComObjPtr<USBController> newCtrl;
8995
8996 newCtrl.createObject();
8997 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8998 mUSBControllers->push_back(newCtrl);
8999 }
9000
9001 /* USB device filters */
9002 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9003 if (FAILED(rc)) return rc;
9004
9005 // network adapters (establish array size first and apply defaults, to
9006 // ensure reading the same settings as we saved, since the list skips
9007 // adapters having defaults)
9008 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9009 size_t oldCount = mNetworkAdapters.size();
9010 if (newCount > oldCount)
9011 {
9012 mNetworkAdapters.resize(newCount);
9013 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9014 {
9015 unconst(mNetworkAdapters[slot]).createObject();
9016 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9017 }
9018 }
9019 else if (newCount < oldCount)
9020 mNetworkAdapters.resize(newCount);
9021 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9022 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9023 for (settings::NetworkAdaptersList::const_iterator
9024 it = data.llNetworkAdapters.begin();
9025 it != data.llNetworkAdapters.end();
9026 ++it)
9027 {
9028 const settings::NetworkAdapter &nic = *it;
9029
9030 /* slot uniqueness is guaranteed by XML Schema */
9031 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9032 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9033 if (FAILED(rc)) return rc;
9034 }
9035
9036 // serial ports (establish defaults first, to ensure reading the same
9037 // settings as we saved, since the list skips ports having defaults)
9038 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9039 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9040 for (settings::SerialPortsList::const_iterator
9041 it = data.llSerialPorts.begin();
9042 it != data.llSerialPorts.end();
9043 ++it)
9044 {
9045 const settings::SerialPort &s = *it;
9046
9047 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9048 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9049 if (FAILED(rc)) return rc;
9050 }
9051
9052 // parallel ports (establish defaults first, to ensure reading the same
9053 // settings as we saved, since the list skips ports having defaults)
9054 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9055 mParallelPorts[i]->i_applyDefaults();
9056 for (settings::ParallelPortsList::const_iterator
9057 it = data.llParallelPorts.begin();
9058 it != data.llParallelPorts.end();
9059 ++it)
9060 {
9061 const settings::ParallelPort &p = *it;
9062
9063 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9064 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9065 if (FAILED(rc)) return rc;
9066 }
9067
9068 /* AudioAdapter */
9069 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9070 if (FAILED(rc)) return rc;
9071
9072 /* storage controllers */
9073 rc = i_loadStorageControllers(data.storage,
9074 puuidRegistry,
9075 puuidSnapshot);
9076 if (FAILED(rc)) return rc;
9077
9078 /* Shared folders */
9079 for (settings::SharedFoldersList::const_iterator
9080 it = data.llSharedFolders.begin();
9081 it != data.llSharedFolders.end();
9082 ++it)
9083 {
9084 const settings::SharedFolder &sf = *it;
9085
9086 ComObjPtr<SharedFolder> sharedFolder;
9087 /* Check for double entries. Not allowed! */
9088 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9089 if (SUCCEEDED(rc))
9090 return setError(VBOX_E_OBJECT_IN_USE,
9091 tr("Shared folder named '%s' already exists"),
9092 sf.strName.c_str());
9093
9094 /* Create the new shared folder. Don't break on error. This will be
9095 * reported when the machine starts. */
9096 sharedFolder.createObject();
9097 rc = sharedFolder->init(i_getMachine(),
9098 sf.strName,
9099 sf.strHostPath,
9100 RT_BOOL(sf.fWritable),
9101 RT_BOOL(sf.fAutoMount),
9102 false /* fFailOnError */);
9103 if (FAILED(rc)) return rc;
9104 mHWData->mSharedFolders.push_back(sharedFolder);
9105 }
9106
9107 // Clipboard
9108 mHWData->mClipboardMode = data.clipboardMode;
9109
9110 // drag'n'drop
9111 mHWData->mDnDMode = data.dndMode;
9112
9113 // guest settings
9114 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9115
9116 // IO settings
9117 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9118 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9119
9120 // Host PCI devices
9121 for (settings::HostPCIDeviceAttachmentList::const_iterator
9122 it = data.pciAttachments.begin();
9123 it != data.pciAttachments.end();
9124 ++it)
9125 {
9126 const settings::HostPCIDeviceAttachment &hpda = *it;
9127 ComObjPtr<PCIDeviceAttachment> pda;
9128
9129 pda.createObject();
9130 pda->i_loadSettings(this, hpda);
9131 mHWData->mPCIDeviceAssignments.push_back(pda);
9132 }
9133
9134 /*
9135 * (The following isn't really real hardware, but it lives in HWData
9136 * for reasons of convenience.)
9137 */
9138
9139#ifdef VBOX_WITH_GUEST_PROPS
9140 /* Guest properties (optional) */
9141
9142 /* Only load transient guest properties for configs which have saved
9143 * state, because there shouldn't be any for powered off VMs. The same
9144 * logic applies for snapshots, as offline snapshots shouldn't have
9145 * any such properties. They confuse the code in various places.
9146 * Note: can't rely on the machine state, as it isn't set yet. */
9147 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9148 /* apologies for the hacky unconst() usage, but this needs hacking
9149 * actually inconsistent settings into consistency, otherwise there
9150 * will be some corner cases where the inconsistency survives
9151 * surprisingly long without getting fixed, especially for snapshots
9152 * as there are no config changes. */
9153 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9154 for (settings::GuestPropertiesList::iterator
9155 it = llGuestProperties.begin();
9156 it != llGuestProperties.end();
9157 /*nothing*/)
9158 {
9159 const settings::GuestProperty &prop = *it;
9160 uint32_t fFlags = guestProp::NILFLAG;
9161 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9162 if ( fSkipTransientGuestProperties
9163 && ( fFlags & guestProp::TRANSIENT
9164 || fFlags & guestProp::TRANSRESET))
9165 {
9166 it = llGuestProperties.erase(it);
9167 continue;
9168 }
9169 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9170 mHWData->mGuestProperties[prop.strName] = property;
9171 ++it;
9172 }
9173#endif /* VBOX_WITH_GUEST_PROPS defined */
9174
9175 rc = i_loadDebugging(pDbg);
9176 if (FAILED(rc))
9177 return rc;
9178
9179 mHWData->mAutostart = *pAutostart;
9180
9181 /* default frontend */
9182 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9183 }
9184 catch (std::bad_alloc &)
9185 {
9186 return E_OUTOFMEMORY;
9187 }
9188
9189 AssertComRC(rc);
9190 return rc;
9191}
9192
9193/**
9194 * Called from i_loadHardware() to load the debugging settings of the
9195 * machine.
9196 *
9197 * @param pDbg Pointer to the settings.
9198 */
9199HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9200{
9201 mHWData->mDebugging = *pDbg;
9202 /* no more processing currently required, this will probably change. */
9203 return S_OK;
9204}
9205
9206/**
9207 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9208 *
9209 * @param data storage settings.
9210 * @param puuidRegistry media registry ID to set media to or NULL;
9211 * see Machine::i_loadMachineDataFromSettings()
9212 * @param puuidSnapshot snapshot ID
9213 * @return
9214 */
9215HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9216 const Guid *puuidRegistry,
9217 const Guid *puuidSnapshot)
9218{
9219 AssertReturn(!i_isSessionMachine(), E_FAIL);
9220
9221 HRESULT rc = S_OK;
9222
9223 for (settings::StorageControllersList::const_iterator
9224 it = data.llStorageControllers.begin();
9225 it != data.llStorageControllers.end();
9226 ++it)
9227 {
9228 const settings::StorageController &ctlData = *it;
9229
9230 ComObjPtr<StorageController> pCtl;
9231 /* Try to find one with the name first. */
9232 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9233 if (SUCCEEDED(rc))
9234 return setError(VBOX_E_OBJECT_IN_USE,
9235 tr("Storage controller named '%s' already exists"),
9236 ctlData.strName.c_str());
9237
9238 pCtl.createObject();
9239 rc = pCtl->init(this,
9240 ctlData.strName,
9241 ctlData.storageBus,
9242 ctlData.ulInstance,
9243 ctlData.fBootable);
9244 if (FAILED(rc)) return rc;
9245
9246 mStorageControllers->push_back(pCtl);
9247
9248 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9249 if (FAILED(rc)) return rc;
9250
9251 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9252 if (FAILED(rc)) return rc;
9253
9254 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9255 if (FAILED(rc)) return rc;
9256
9257 /* Load the attached devices now. */
9258 rc = i_loadStorageDevices(pCtl,
9259 ctlData,
9260 puuidRegistry,
9261 puuidSnapshot);
9262 if (FAILED(rc)) return rc;
9263 }
9264
9265 return S_OK;
9266}
9267
9268/**
9269 * Called from i_loadStorageControllers for a controller's devices.
9270 *
9271 * @param aStorageController
9272 * @param data
9273 * @param puuidRegistry media registry ID to set media to or NULL; see
9274 * Machine::i_loadMachineDataFromSettings()
9275 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9276 * @return
9277 */
9278HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9279 const settings::StorageController &data,
9280 const Guid *puuidRegistry,
9281 const Guid *puuidSnapshot)
9282{
9283 HRESULT rc = S_OK;
9284
9285 /* paranoia: detect duplicate attachments */
9286 for (settings::AttachedDevicesList::const_iterator
9287 it = data.llAttachedDevices.begin();
9288 it != data.llAttachedDevices.end();
9289 ++it)
9290 {
9291 const settings::AttachedDevice &ad = *it;
9292
9293 for (settings::AttachedDevicesList::const_iterator it2 = it;
9294 it2 != data.llAttachedDevices.end();
9295 ++it2)
9296 {
9297 if (it == it2)
9298 continue;
9299
9300 const settings::AttachedDevice &ad2 = *it2;
9301
9302 if ( ad.lPort == ad2.lPort
9303 && ad.lDevice == ad2.lDevice)
9304 {
9305 return setError(E_FAIL,
9306 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9307 aStorageController->i_getName().c_str(),
9308 ad.lPort,
9309 ad.lDevice,
9310 mUserData->s.strName.c_str());
9311 }
9312 }
9313 }
9314
9315 for (settings::AttachedDevicesList::const_iterator
9316 it = data.llAttachedDevices.begin();
9317 it != data.llAttachedDevices.end();
9318 ++it)
9319 {
9320 const settings::AttachedDevice &dev = *it;
9321 ComObjPtr<Medium> medium;
9322
9323 switch (dev.deviceType)
9324 {
9325 case DeviceType_Floppy:
9326 case DeviceType_DVD:
9327 if (dev.strHostDriveSrc.isNotEmpty())
9328 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9329 false /* fRefresh */, medium);
9330 else
9331 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9332 dev.uuid,
9333 false /* fRefresh */,
9334 false /* aSetError */,
9335 medium);
9336 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9337 // This is not an error. The host drive or UUID might have vanished, so just go
9338 // ahead without this removeable medium attachment
9339 rc = S_OK;
9340 break;
9341
9342 case DeviceType_HardDisk:
9343 {
9344 /* find a hard disk by UUID */
9345 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9346 if (FAILED(rc))
9347 {
9348 if (i_isSnapshotMachine())
9349 {
9350 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9351 // so the user knows that the bad disk is in a snapshot somewhere
9352 com::ErrorInfo info;
9353 return setError(E_FAIL,
9354 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9355 puuidSnapshot->raw(),
9356 info.getText().raw());
9357 }
9358 else
9359 return rc;
9360 }
9361
9362 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9363
9364 if (medium->i_getType() == MediumType_Immutable)
9365 {
9366 if (i_isSnapshotMachine())
9367 return setError(E_FAIL,
9368 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9369 "of the virtual machine '%s' ('%s')"),
9370 medium->i_getLocationFull().c_str(),
9371 dev.uuid.raw(),
9372 puuidSnapshot->raw(),
9373 mUserData->s.strName.c_str(),
9374 mData->m_strConfigFileFull.c_str());
9375
9376 return setError(E_FAIL,
9377 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9378 medium->i_getLocationFull().c_str(),
9379 dev.uuid.raw(),
9380 mUserData->s.strName.c_str(),
9381 mData->m_strConfigFileFull.c_str());
9382 }
9383
9384 if (medium->i_getType() == MediumType_MultiAttach)
9385 {
9386 if (i_isSnapshotMachine())
9387 return setError(E_FAIL,
9388 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9389 "of the virtual machine '%s' ('%s')"),
9390 medium->i_getLocationFull().c_str(),
9391 dev.uuid.raw(),
9392 puuidSnapshot->raw(),
9393 mUserData->s.strName.c_str(),
9394 mData->m_strConfigFileFull.c_str());
9395
9396 return setError(E_FAIL,
9397 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9398 medium->i_getLocationFull().c_str(),
9399 dev.uuid.raw(),
9400 mUserData->s.strName.c_str(),
9401 mData->m_strConfigFileFull.c_str());
9402 }
9403
9404 if ( !i_isSnapshotMachine()
9405 && medium->i_getChildren().size() != 0
9406 )
9407 return setError(E_FAIL,
9408 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9409 "because it has %d differencing child hard disks"),
9410 medium->i_getLocationFull().c_str(),
9411 dev.uuid.raw(),
9412 mUserData->s.strName.c_str(),
9413 mData->m_strConfigFileFull.c_str(),
9414 medium->i_getChildren().size());
9415
9416 if (i_findAttachment(*mMediumAttachments.data(),
9417 medium))
9418 return setError(E_FAIL,
9419 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9420 medium->i_getLocationFull().c_str(),
9421 dev.uuid.raw(),
9422 mUserData->s.strName.c_str(),
9423 mData->m_strConfigFileFull.c_str());
9424
9425 break;
9426 }
9427
9428 default:
9429 return setError(E_FAIL,
9430 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9431 medium->i_getLocationFull().c_str(),
9432 mUserData->s.strName.c_str(),
9433 mData->m_strConfigFileFull.c_str());
9434 }
9435
9436 if (FAILED(rc))
9437 break;
9438
9439 /* Bandwidth groups are loaded at this point. */
9440 ComObjPtr<BandwidthGroup> pBwGroup;
9441
9442 if (!dev.strBwGroup.isEmpty())
9443 {
9444 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9445 if (FAILED(rc))
9446 return setError(E_FAIL,
9447 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9448 medium->i_getLocationFull().c_str(),
9449 dev.strBwGroup.c_str(),
9450 mUserData->s.strName.c_str(),
9451 mData->m_strConfigFileFull.c_str());
9452 pBwGroup->i_reference();
9453 }
9454
9455 const Utf8Str controllerName = aStorageController->i_getName();
9456 ComObjPtr<MediumAttachment> pAttachment;
9457 pAttachment.createObject();
9458 rc = pAttachment->init(this,
9459 medium,
9460 controllerName,
9461 dev.lPort,
9462 dev.lDevice,
9463 dev.deviceType,
9464 false,
9465 dev.fPassThrough,
9466 dev.fTempEject,
9467 dev.fNonRotational,
9468 dev.fDiscard,
9469 dev.fHotPluggable,
9470 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9471 if (FAILED(rc)) break;
9472
9473 /* associate the medium with this machine and snapshot */
9474 if (!medium.isNull())
9475 {
9476 AutoCaller medCaller(medium);
9477 if (FAILED(medCaller.rc())) return medCaller.rc();
9478 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9479
9480 if (i_isSnapshotMachine())
9481 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9482 else
9483 rc = medium->i_addBackReference(mData->mUuid);
9484 /* If the medium->addBackReference fails it sets an appropriate
9485 * error message, so no need to do any guesswork here. */
9486
9487 if (puuidRegistry)
9488 // caller wants registry ID to be set on all attached media (OVF import case)
9489 medium->i_addRegistry(*puuidRegistry);
9490 }
9491
9492 if (FAILED(rc))
9493 break;
9494
9495 /* back up mMediumAttachments to let registeredInit() properly rollback
9496 * on failure (= limited accessibility) */
9497 i_setModified(IsModified_Storage);
9498 mMediumAttachments.backup();
9499 mMediumAttachments->push_back(pAttachment);
9500 }
9501
9502 return rc;
9503}
9504
9505/**
9506 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9507 *
9508 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9509 * @param aSnapshot where to return the found snapshot
9510 * @param aSetError true to set extended error info on failure
9511 */
9512HRESULT Machine::i_findSnapshotById(const Guid &aId,
9513 ComObjPtr<Snapshot> &aSnapshot,
9514 bool aSetError /* = false */)
9515{
9516 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9517
9518 if (!mData->mFirstSnapshot)
9519 {
9520 if (aSetError)
9521 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9522 return E_FAIL;
9523 }
9524
9525 if (aId.isZero())
9526 aSnapshot = mData->mFirstSnapshot;
9527 else
9528 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9529
9530 if (!aSnapshot)
9531 {
9532 if (aSetError)
9533 return setError(E_FAIL,
9534 tr("Could not find a snapshot with UUID {%s}"),
9535 aId.toString().c_str());
9536 return E_FAIL;
9537 }
9538
9539 return S_OK;
9540}
9541
9542/**
9543 * Returns the snapshot with the given name or fails of no such snapshot.
9544 *
9545 * @param strName snapshot name to find
9546 * @param aSnapshot where to return the found snapshot
9547 * @param aSetError true to set extended error info on failure
9548 */
9549HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9550 ComObjPtr<Snapshot> &aSnapshot,
9551 bool aSetError /* = false */)
9552{
9553 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9554
9555 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9556
9557 if (!mData->mFirstSnapshot)
9558 {
9559 if (aSetError)
9560 return setError(VBOX_E_OBJECT_NOT_FOUND,
9561 tr("This machine does not have any snapshots"));
9562 return VBOX_E_OBJECT_NOT_FOUND;
9563 }
9564
9565 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9566
9567 if (!aSnapshot)
9568 {
9569 if (aSetError)
9570 return setError(VBOX_E_OBJECT_NOT_FOUND,
9571 tr("Could not find a snapshot named '%s'"), strName.c_str());
9572 return VBOX_E_OBJECT_NOT_FOUND;
9573 }
9574
9575 return S_OK;
9576}
9577
9578/**
9579 * Returns a storage controller object with the given name.
9580 *
9581 * @param aName storage controller name to find
9582 * @param aStorageController where to return the found storage controller
9583 * @param aSetError true to set extended error info on failure
9584 */
9585HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9586 ComObjPtr<StorageController> &aStorageController,
9587 bool aSetError /* = false */)
9588{
9589 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9590
9591 for (StorageControllerList::const_iterator
9592 it = mStorageControllers->begin();
9593 it != mStorageControllers->end();
9594 ++it)
9595 {
9596 if ((*it)->i_getName() == aName)
9597 {
9598 aStorageController = (*it);
9599 return S_OK;
9600 }
9601 }
9602
9603 if (aSetError)
9604 return setError(VBOX_E_OBJECT_NOT_FOUND,
9605 tr("Could not find a storage controller named '%s'"),
9606 aName.c_str());
9607 return VBOX_E_OBJECT_NOT_FOUND;
9608}
9609
9610/**
9611 * Returns a USB controller object with the given name.
9612 *
9613 * @param aName USB controller name to find
9614 * @param aUSBController where to return the found USB controller
9615 * @param aSetError true to set extended error info on failure
9616 */
9617HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9618 ComObjPtr<USBController> &aUSBController,
9619 bool aSetError /* = false */)
9620{
9621 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9622
9623 for (USBControllerList::const_iterator
9624 it = mUSBControllers->begin();
9625 it != mUSBControllers->end();
9626 ++it)
9627 {
9628 if ((*it)->i_getName() == aName)
9629 {
9630 aUSBController = (*it);
9631 return S_OK;
9632 }
9633 }
9634
9635 if (aSetError)
9636 return setError(VBOX_E_OBJECT_NOT_FOUND,
9637 tr("Could not find a storage controller named '%s'"),
9638 aName.c_str());
9639 return VBOX_E_OBJECT_NOT_FOUND;
9640}
9641
9642/**
9643 * Returns the number of USB controller instance of the given type.
9644 *
9645 * @param enmType USB controller type.
9646 */
9647ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9648{
9649 ULONG cCtrls = 0;
9650
9651 for (USBControllerList::const_iterator
9652 it = mUSBControllers->begin();
9653 it != mUSBControllers->end();
9654 ++it)
9655 {
9656 if ((*it)->i_getControllerType() == enmType)
9657 cCtrls++;
9658 }
9659
9660 return cCtrls;
9661}
9662
9663HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9664 MediumAttachmentList &atts)
9665{
9666 AutoCaller autoCaller(this);
9667 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9668
9669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9670
9671 for (MediumAttachmentList::const_iterator
9672 it = mMediumAttachments->begin();
9673 it != mMediumAttachments->end();
9674 ++it)
9675 {
9676 const ComObjPtr<MediumAttachment> &pAtt = *it;
9677 // should never happen, but deal with NULL pointers in the list.
9678 AssertContinue(!pAtt.isNull());
9679
9680 // getControllerName() needs caller+read lock
9681 AutoCaller autoAttCaller(pAtt);
9682 if (FAILED(autoAttCaller.rc()))
9683 {
9684 atts.clear();
9685 return autoAttCaller.rc();
9686 }
9687 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9688
9689 if (pAtt->i_getControllerName() == aName)
9690 atts.push_back(pAtt);
9691 }
9692
9693 return S_OK;
9694}
9695
9696
9697/**
9698 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9699 * file if the machine name was changed and about creating a new settings file
9700 * if this is a new machine.
9701 *
9702 * @note Must be never called directly but only from #saveSettings().
9703 */
9704HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9705{
9706 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9707
9708 HRESULT rc = S_OK;
9709
9710 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9711
9712 /// @todo need to handle primary group change, too
9713
9714 /* attempt to rename the settings file if machine name is changed */
9715 if ( mUserData->s.fNameSync
9716 && mUserData.isBackedUp()
9717 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9718 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9719 )
9720 {
9721 bool dirRenamed = false;
9722 bool fileRenamed = false;
9723
9724 Utf8Str configFile, newConfigFile;
9725 Utf8Str configFilePrev, newConfigFilePrev;
9726 Utf8Str configDir, newConfigDir;
9727
9728 do
9729 {
9730 int vrc = VINF_SUCCESS;
9731
9732 Utf8Str name = mUserData.backedUpData()->s.strName;
9733 Utf8Str newName = mUserData->s.strName;
9734 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9735 if (group == "/")
9736 group.setNull();
9737 Utf8Str newGroup = mUserData->s.llGroups.front();
9738 if (newGroup == "/")
9739 newGroup.setNull();
9740
9741 configFile = mData->m_strConfigFileFull;
9742
9743 /* first, rename the directory if it matches the group and machine name */
9744 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9745 group.c_str(), RTPATH_DELIMITER, name.c_str());
9746 /** @todo hack, make somehow use of ComposeMachineFilename */
9747 if (mUserData->s.fDirectoryIncludesUUID)
9748 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9749 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9750 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9751 /** @todo hack, make somehow use of ComposeMachineFilename */
9752 if (mUserData->s.fDirectoryIncludesUUID)
9753 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9754 configDir = configFile;
9755 configDir.stripFilename();
9756 newConfigDir = configDir;
9757 if ( configDir.length() >= groupPlusName.length()
9758 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9759 groupPlusName.c_str()))
9760 {
9761 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9762 Utf8Str newConfigBaseDir(newConfigDir);
9763 newConfigDir.append(newGroupPlusName);
9764 /* consistency: use \ if appropriate on the platform */
9765 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9766 /* new dir and old dir cannot be equal here because of 'if'
9767 * above and because name != newName */
9768 Assert(configDir != newConfigDir);
9769 if (!fSettingsFileIsNew)
9770 {
9771 /* perform real rename only if the machine is not new */
9772 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9773 if ( vrc == VERR_FILE_NOT_FOUND
9774 || vrc == VERR_PATH_NOT_FOUND)
9775 {
9776 /* create the parent directory, then retry renaming */
9777 Utf8Str parent(newConfigDir);
9778 parent.stripFilename();
9779 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9780 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9781 }
9782 if (RT_FAILURE(vrc))
9783 {
9784 rc = setError(E_FAIL,
9785 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9786 configDir.c_str(),
9787 newConfigDir.c_str(),
9788 vrc);
9789 break;
9790 }
9791 /* delete subdirectories which are no longer needed */
9792 Utf8Str dir(configDir);
9793 dir.stripFilename();
9794 while (dir != newConfigBaseDir && dir != ".")
9795 {
9796 vrc = RTDirRemove(dir.c_str());
9797 if (RT_FAILURE(vrc))
9798 break;
9799 dir.stripFilename();
9800 }
9801 dirRenamed = true;
9802 }
9803 }
9804
9805 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9806 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9807
9808 /* then try to rename the settings file itself */
9809 if (newConfigFile != configFile)
9810 {
9811 /* get the path to old settings file in renamed directory */
9812 configFile = Utf8StrFmt("%s%c%s",
9813 newConfigDir.c_str(),
9814 RTPATH_DELIMITER,
9815 RTPathFilename(configFile.c_str()));
9816 if (!fSettingsFileIsNew)
9817 {
9818 /* perform real rename only if the machine is not new */
9819 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9820 if (RT_FAILURE(vrc))
9821 {
9822 rc = setError(E_FAIL,
9823 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9824 configFile.c_str(),
9825 newConfigFile.c_str(),
9826 vrc);
9827 break;
9828 }
9829 fileRenamed = true;
9830 configFilePrev = configFile;
9831 configFilePrev += "-prev";
9832 newConfigFilePrev = newConfigFile;
9833 newConfigFilePrev += "-prev";
9834 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9835 }
9836 }
9837
9838 // update m_strConfigFileFull amd mConfigFile
9839 mData->m_strConfigFileFull = newConfigFile;
9840 // compute the relative path too
9841 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9842
9843 // store the old and new so that VirtualBox::i_saveSettings() can update
9844 // the media registry
9845 if ( mData->mRegistered
9846 && (configDir != newConfigDir || configFile != newConfigFile))
9847 {
9848 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9849
9850 if (pfNeedsGlobalSaveSettings)
9851 *pfNeedsGlobalSaveSettings = true;
9852 }
9853
9854 // in the saved state file path, replace the old directory with the new directory
9855 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9856 {
9857 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9858 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9859 }
9860
9861 // and do the same thing for the saved state file paths of all the online snapshots
9862 if (mData->mFirstSnapshot)
9863 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9864 newConfigDir.c_str());
9865 }
9866 while (0);
9867
9868 if (FAILED(rc))
9869 {
9870 /* silently try to rename everything back */
9871 if (fileRenamed)
9872 {
9873 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9874 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9875 }
9876 if (dirRenamed)
9877 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9878 }
9879
9880 if (FAILED(rc)) return rc;
9881 }
9882
9883 if (fSettingsFileIsNew)
9884 {
9885 /* create a virgin config file */
9886 int vrc = VINF_SUCCESS;
9887
9888 /* ensure the settings directory exists */
9889 Utf8Str path(mData->m_strConfigFileFull);
9890 path.stripFilename();
9891 if (!RTDirExists(path.c_str()))
9892 {
9893 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9894 if (RT_FAILURE(vrc))
9895 {
9896 return setError(E_FAIL,
9897 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9898 path.c_str(),
9899 vrc);
9900 }
9901 }
9902
9903 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9904 path = Utf8Str(mData->m_strConfigFileFull);
9905 RTFILE f = NIL_RTFILE;
9906 vrc = RTFileOpen(&f, path.c_str(),
9907 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9908 if (RT_FAILURE(vrc))
9909 return setError(E_FAIL,
9910 tr("Could not create the settings file '%s' (%Rrc)"),
9911 path.c_str(),
9912 vrc);
9913 RTFileClose(f);
9914 }
9915
9916 return rc;
9917}
9918
9919/**
9920 * Saves and commits machine data, user data and hardware data.
9921 *
9922 * Note that on failure, the data remains uncommitted.
9923 *
9924 * @a aFlags may combine the following flags:
9925 *
9926 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9927 * Used when saving settings after an operation that makes them 100%
9928 * correspond to the settings from the current snapshot.
9929 * - SaveS_Force: settings will be saved without doing a deep compare of the
9930 * settings structures. This is used when this is called because snapshots
9931 * have changed to avoid the overhead of the deep compare.
9932 *
9933 * @note Must be called from under this object's write lock. Locks children for
9934 * writing.
9935 *
9936 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9937 * initialized to false and that will be set to true by this function if
9938 * the caller must invoke VirtualBox::i_saveSettings() because the global
9939 * settings have changed. This will happen if a machine rename has been
9940 * saved and the global machine and media registries will therefore need
9941 * updating.
9942 * @param aFlags Flags.
9943 */
9944HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9945 int aFlags /*= 0*/)
9946{
9947 LogFlowThisFuncEnter();
9948
9949 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9950
9951 /* make sure child objects are unable to modify the settings while we are
9952 * saving them */
9953 i_ensureNoStateDependencies();
9954
9955 AssertReturn(!i_isSnapshotMachine(),
9956 E_FAIL);
9957
9958 HRESULT rc = S_OK;
9959 bool fNeedsWrite = false;
9960
9961 /* First, prepare to save settings. It will care about renaming the
9962 * settings directory and file if the machine name was changed and about
9963 * creating a new settings file if this is a new machine. */
9964 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9965 if (FAILED(rc)) return rc;
9966
9967 // keep a pointer to the current settings structures
9968 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9969 settings::MachineConfigFile *pNewConfig = NULL;
9970
9971 try
9972 {
9973 // make a fresh one to have everyone write stuff into
9974 pNewConfig = new settings::MachineConfigFile(NULL);
9975 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9976
9977 // now go and copy all the settings data from COM to the settings structures
9978 // (this calls i_saveSettings() on all the COM objects in the machine)
9979 i_copyMachineDataToSettings(*pNewConfig);
9980
9981 if (aFlags & SaveS_ResetCurStateModified)
9982 {
9983 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9984 mData->mCurrentStateModified = FALSE;
9985 fNeedsWrite = true; // always, no need to compare
9986 }
9987 else if (aFlags & SaveS_Force)
9988 {
9989 fNeedsWrite = true; // always, no need to compare
9990 }
9991 else
9992 {
9993 if (!mData->mCurrentStateModified)
9994 {
9995 // do a deep compare of the settings that we just saved with the settings
9996 // previously stored in the config file; this invokes MachineConfigFile::operator==
9997 // which does a deep compare of all the settings, which is expensive but less expensive
9998 // than writing out XML in vain
9999 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10000
10001 // could still be modified if any settings changed
10002 mData->mCurrentStateModified = fAnySettingsChanged;
10003
10004 fNeedsWrite = fAnySettingsChanged;
10005 }
10006 else
10007 fNeedsWrite = true;
10008 }
10009
10010 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10011
10012 if (fNeedsWrite)
10013 // now spit it all out!
10014 pNewConfig->write(mData->m_strConfigFileFull);
10015
10016 mData->pMachineConfigFile = pNewConfig;
10017 delete pOldConfig;
10018 i_commit();
10019
10020 // after saving settings, we are no longer different from the XML on disk
10021 mData->flModifications = 0;
10022 }
10023 catch (HRESULT err)
10024 {
10025 // we assume that error info is set by the thrower
10026 rc = err;
10027
10028 // restore old config
10029 delete pNewConfig;
10030 mData->pMachineConfigFile = pOldConfig;
10031 }
10032 catch (...)
10033 {
10034 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10035 }
10036
10037 if (fNeedsWrite)
10038 {
10039 /* Fire the data change event, even on failure (since we've already
10040 * committed all data). This is done only for SessionMachines because
10041 * mutable Machine instances are always not registered (i.e. private
10042 * to the client process that creates them) and thus don't need to
10043 * inform callbacks. */
10044 if (i_isSessionMachine())
10045 mParent->i_onMachineDataChange(mData->mUuid);
10046 }
10047
10048 LogFlowThisFunc(("rc=%08X\n", rc));
10049 LogFlowThisFuncLeave();
10050 return rc;
10051}
10052
10053/**
10054 * Implementation for saving the machine settings into the given
10055 * settings::MachineConfigFile instance. This copies machine extradata
10056 * from the previous machine config file in the instance data, if any.
10057 *
10058 * This gets called from two locations:
10059 *
10060 * -- Machine::i_saveSettings(), during the regular XML writing;
10061 *
10062 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10063 * exported to OVF and we write the VirtualBox proprietary XML
10064 * into a <vbox:Machine> tag.
10065 *
10066 * This routine fills all the fields in there, including snapshots, *except*
10067 * for the following:
10068 *
10069 * -- fCurrentStateModified. There is some special logic associated with that.
10070 *
10071 * The caller can then call MachineConfigFile::write() or do something else
10072 * with it.
10073 *
10074 * Caller must hold the machine lock!
10075 *
10076 * This throws XML errors and HRESULT, so the caller must have a catch block!
10077 */
10078void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10079{
10080 // deep copy extradata, being extra careful with self assignment (the STL
10081 // map assignment on Mac OS X clang based Xcode isn't checking)
10082 if (&config != mData->pMachineConfigFile)
10083 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10084
10085 config.uuid = mData->mUuid;
10086
10087 // copy name, description, OS type, teleport, UTC etc.
10088 config.machineUserData = mUserData->s;
10089
10090 if ( mData->mMachineState == MachineState_Saved
10091 || mData->mMachineState == MachineState_Restoring
10092 // when doing certain snapshot operations we may or may not have
10093 // a saved state in the current state, so keep everything as is
10094 || ( ( mData->mMachineState == MachineState_Snapshotting
10095 || mData->mMachineState == MachineState_DeletingSnapshot
10096 || mData->mMachineState == MachineState_RestoringSnapshot)
10097 && (!mSSData->strStateFilePath.isEmpty())
10098 )
10099 )
10100 {
10101 Assert(!mSSData->strStateFilePath.isEmpty());
10102 /* try to make the file name relative to the settings file dir */
10103 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10104 }
10105 else
10106 {
10107 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10108 config.strStateFile.setNull();
10109 }
10110
10111 if (mData->mCurrentSnapshot)
10112 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10113 else
10114 config.uuidCurrentSnapshot.clear();
10115
10116 config.timeLastStateChange = mData->mLastStateChange;
10117 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10118 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10119
10120 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10121 if (FAILED(rc)) throw rc;
10122
10123 // save machine's media registry if this is VirtualBox 4.0 or later
10124 if (config.canHaveOwnMediaRegistry())
10125 {
10126 // determine machine folder
10127 Utf8Str strMachineFolder = i_getSettingsFileFull();
10128 strMachineFolder.stripFilename();
10129 mParent->i_saveMediaRegistry(config.mediaRegistry,
10130 i_getId(), // only media with registry ID == machine UUID
10131 strMachineFolder);
10132 // this throws HRESULT
10133 }
10134
10135 // save snapshots
10136 rc = i_saveAllSnapshots(config);
10137 if (FAILED(rc)) throw rc;
10138}
10139
10140/**
10141 * Saves all snapshots of the machine into the given machine config file. Called
10142 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10143 * @param config
10144 * @return
10145 */
10146HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10147{
10148 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10149
10150 HRESULT rc = S_OK;
10151
10152 try
10153 {
10154 config.llFirstSnapshot.clear();
10155
10156 if (mData->mFirstSnapshot)
10157 {
10158 // the settings use a list for "the first snapshot"
10159 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10160
10161 // get reference to the snapshot on the list and work on that
10162 // element straight in the list to avoid excessive copying later
10163 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10164 if (FAILED(rc)) throw rc;
10165 }
10166
10167// if (mType == IsSessionMachine)
10168// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10169
10170 }
10171 catch (HRESULT err)
10172 {
10173 /* we assume that error info is set by the thrower */
10174 rc = err;
10175 }
10176 catch (...)
10177 {
10178 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10179 }
10180
10181 return rc;
10182}
10183
10184/**
10185 * Saves the VM hardware configuration. It is assumed that the
10186 * given node is empty.
10187 *
10188 * @param data Reference to the settings object for the hardware config.
10189 * @param pDbg Pointer to the settings object for the debugging config
10190 * which happens to live in mHWData.
10191 * @param pAutostart Pointer to the settings object for the autostart config
10192 * which happens to live in mHWData.
10193 */
10194HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10195 settings::Autostart *pAutostart)
10196{
10197 HRESULT rc = S_OK;
10198
10199 try
10200 {
10201 /* The hardware version attribute (optional).
10202 Automatically upgrade from 1 to current default hardware version
10203 when there is no saved state. (ugly!) */
10204 if ( mHWData->mHWVersion == "1"
10205 && mSSData->strStateFilePath.isEmpty()
10206 )
10207 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10208
10209 data.strVersion = mHWData->mHWVersion;
10210 data.uuid = mHWData->mHardwareUUID;
10211
10212 // CPU
10213 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10214 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10215 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10216 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10217 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10218 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10219 data.fPAE = !!mHWData->mPAEEnabled;
10220 data.enmLongMode = mHWData->mLongMode;
10221 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10222 data.fAPIC = !!mHWData->mAPIC;
10223 data.fX2APIC = !!mHWData->mX2APIC;
10224 data.cCPUs = mHWData->mCPUCount;
10225 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10226 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10227 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10228 data.strCpuProfile = mHWData->mCpuProfile;
10229
10230 data.llCpus.clear();
10231 if (data.fCpuHotPlug)
10232 {
10233 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10234 {
10235 if (mHWData->mCPUAttached[idx])
10236 {
10237 settings::Cpu cpu;
10238 cpu.ulId = idx;
10239 data.llCpus.push_back(cpu);
10240 }
10241 }
10242 }
10243
10244 /* Standard and Extended CPUID leafs. */
10245 data.llCpuIdLeafs.clear();
10246 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10247
10248 // memory
10249 data.ulMemorySizeMB = mHWData->mMemorySize;
10250 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10251
10252 // firmware
10253 data.firmwareType = mHWData->mFirmwareType;
10254
10255 // HID
10256 data.pointingHIDType = mHWData->mPointingHIDType;
10257 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10258
10259 // chipset
10260 data.chipsetType = mHWData->mChipsetType;
10261
10262 // paravirt
10263 data.paravirtProvider = mHWData->mParavirtProvider;
10264 data.strParavirtDebug = mHWData->mParavirtDebug;
10265
10266 // emulated USB card reader
10267 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10268
10269 // HPET
10270 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10271
10272 // boot order
10273 data.mapBootOrder.clear();
10274 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10275 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10276
10277 // display
10278 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10279 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10280 data.cMonitors = mHWData->mMonitorCount;
10281 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10282 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10283 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10284 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10285 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10286 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10287 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10288 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10289 {
10290 if (mHWData->maVideoCaptureScreens[i])
10291 ASMBitSet(&data.u64VideoCaptureScreens, i);
10292 else
10293 ASMBitClear(&data.u64VideoCaptureScreens, i);
10294 }
10295 /* store relative video capture file if possible */
10296 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10297 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10298
10299 /* VRDEServer settings (optional) */
10300 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10301 if (FAILED(rc)) throw rc;
10302
10303 /* BIOS (required) */
10304 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10305 if (FAILED(rc)) throw rc;
10306
10307 /* USB Controller (required) */
10308 data.usbSettings.llUSBControllers.clear();
10309 for (USBControllerList::const_iterator
10310 it = mUSBControllers->begin();
10311 it != mUSBControllers->end();
10312 ++it)
10313 {
10314 ComObjPtr<USBController> ctrl = *it;
10315 settings::USBController settingsCtrl;
10316
10317 settingsCtrl.strName = ctrl->i_getName();
10318 settingsCtrl.enmType = ctrl->i_getControllerType();
10319
10320 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10321 }
10322
10323 /* USB device filters (required) */
10324 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10325 if (FAILED(rc)) throw rc;
10326
10327 /* Network adapters (required) */
10328 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10329 data.llNetworkAdapters.clear();
10330 /* Write out only the nominal number of network adapters for this
10331 * chipset type. Since Machine::commit() hasn't been called there
10332 * may be extra NIC settings in the vector. */
10333 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10334 {
10335 settings::NetworkAdapter nic;
10336 nic.ulSlot = (uint32_t)slot;
10337 /* paranoia check... must not be NULL, but must not crash either. */
10338 if (mNetworkAdapters[slot])
10339 {
10340 if (mNetworkAdapters[slot]->i_hasDefaults())
10341 continue;
10342
10343 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10344 if (FAILED(rc)) throw rc;
10345
10346 data.llNetworkAdapters.push_back(nic);
10347 }
10348 }
10349
10350 /* Serial ports */
10351 data.llSerialPorts.clear();
10352 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10353 {
10354 if (mSerialPorts[slot]->i_hasDefaults())
10355 continue;
10356
10357 settings::SerialPort s;
10358 s.ulSlot = slot;
10359 rc = mSerialPorts[slot]->i_saveSettings(s);
10360 if (FAILED(rc)) return rc;
10361
10362 data.llSerialPorts.push_back(s);
10363 }
10364
10365 /* Parallel ports */
10366 data.llParallelPorts.clear();
10367 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10368 {
10369 if (mParallelPorts[slot]->i_hasDefaults())
10370 continue;
10371
10372 settings::ParallelPort p;
10373 p.ulSlot = slot;
10374 rc = mParallelPorts[slot]->i_saveSettings(p);
10375 if (FAILED(rc)) return rc;
10376
10377 data.llParallelPorts.push_back(p);
10378 }
10379
10380 /* Audio adapter */
10381 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10382 if (FAILED(rc)) return rc;
10383
10384 rc = i_saveStorageControllers(data.storage);
10385 if (FAILED(rc)) return rc;
10386
10387 /* Shared folders */
10388 data.llSharedFolders.clear();
10389 for (HWData::SharedFolderList::const_iterator
10390 it = mHWData->mSharedFolders.begin();
10391 it != mHWData->mSharedFolders.end();
10392 ++it)
10393 {
10394 SharedFolder *pSF = *it;
10395 AutoCaller sfCaller(pSF);
10396 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10397 settings::SharedFolder sf;
10398 sf.strName = pSF->i_getName();
10399 sf.strHostPath = pSF->i_getHostPath();
10400 sf.fWritable = !!pSF->i_isWritable();
10401 sf.fAutoMount = !!pSF->i_isAutoMounted();
10402
10403 data.llSharedFolders.push_back(sf);
10404 }
10405
10406 // clipboard
10407 data.clipboardMode = mHWData->mClipboardMode;
10408
10409 // drag'n'drop
10410 data.dndMode = mHWData->mDnDMode;
10411
10412 /* Guest */
10413 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10414
10415 // IO settings
10416 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10417 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10418
10419 /* BandwidthControl (required) */
10420 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10421 if (FAILED(rc)) throw rc;
10422
10423 /* Host PCI devices */
10424 data.pciAttachments.clear();
10425 for (HWData::PCIDeviceAssignmentList::const_iterator
10426 it = mHWData->mPCIDeviceAssignments.begin();
10427 it != mHWData->mPCIDeviceAssignments.end();
10428 ++it)
10429 {
10430 ComObjPtr<PCIDeviceAttachment> pda = *it;
10431 settings::HostPCIDeviceAttachment hpda;
10432
10433 rc = pda->i_saveSettings(hpda);
10434 if (FAILED(rc)) throw rc;
10435
10436 data.pciAttachments.push_back(hpda);
10437 }
10438
10439 // guest properties
10440 data.llGuestProperties.clear();
10441#ifdef VBOX_WITH_GUEST_PROPS
10442 for (HWData::GuestPropertyMap::const_iterator
10443 it = mHWData->mGuestProperties.begin();
10444 it != mHWData->mGuestProperties.end();
10445 ++it)
10446 {
10447 HWData::GuestProperty property = it->second;
10448
10449 /* Remove transient guest properties at shutdown unless we
10450 * are saving state. Note that restoring snapshot intentionally
10451 * keeps them, they will be removed if appropriate once the final
10452 * machine state is set (as crashes etc. need to work). */
10453 if ( ( mData->mMachineState == MachineState_PoweredOff
10454 || mData->mMachineState == MachineState_Aborted
10455 || mData->mMachineState == MachineState_Teleported)
10456 && ( property.mFlags & guestProp::TRANSIENT
10457 || property.mFlags & guestProp::TRANSRESET))
10458 continue;
10459 settings::GuestProperty prop;
10460 prop.strName = it->first;
10461 prop.strValue = property.strValue;
10462 prop.timestamp = property.mTimestamp;
10463 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10464 guestProp::writeFlags(property.mFlags, szFlags);
10465 prop.strFlags = szFlags;
10466
10467 data.llGuestProperties.push_back(prop);
10468 }
10469
10470 /* I presume this doesn't require a backup(). */
10471 mData->mGuestPropertiesModified = FALSE;
10472#endif /* VBOX_WITH_GUEST_PROPS defined */
10473
10474 *pDbg = mHWData->mDebugging;
10475 *pAutostart = mHWData->mAutostart;
10476
10477 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10478 }
10479 catch (std::bad_alloc &)
10480 {
10481 return E_OUTOFMEMORY;
10482 }
10483
10484 AssertComRC(rc);
10485 return rc;
10486}
10487
10488/**
10489 * Saves the storage controller configuration.
10490 *
10491 * @param data storage settings.
10492 */
10493HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10494{
10495 data.llStorageControllers.clear();
10496
10497 for (StorageControllerList::const_iterator
10498 it = mStorageControllers->begin();
10499 it != mStorageControllers->end();
10500 ++it)
10501 {
10502 HRESULT rc;
10503 ComObjPtr<StorageController> pCtl = *it;
10504
10505 settings::StorageController ctl;
10506 ctl.strName = pCtl->i_getName();
10507 ctl.controllerType = pCtl->i_getControllerType();
10508 ctl.storageBus = pCtl->i_getStorageBus();
10509 ctl.ulInstance = pCtl->i_getInstance();
10510 ctl.fBootable = pCtl->i_getBootable();
10511
10512 /* Save the port count. */
10513 ULONG portCount;
10514 rc = pCtl->COMGETTER(PortCount)(&portCount);
10515 ComAssertComRCRet(rc, rc);
10516 ctl.ulPortCount = portCount;
10517
10518 /* Save fUseHostIOCache */
10519 BOOL fUseHostIOCache;
10520 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10521 ComAssertComRCRet(rc, rc);
10522 ctl.fUseHostIOCache = !!fUseHostIOCache;
10523
10524 /* save the devices now. */
10525 rc = i_saveStorageDevices(pCtl, ctl);
10526 ComAssertComRCRet(rc, rc);
10527
10528 data.llStorageControllers.push_back(ctl);
10529 }
10530
10531 return S_OK;
10532}
10533
10534/**
10535 * Saves the hard disk configuration.
10536 */
10537HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10538 settings::StorageController &data)
10539{
10540 MediumAttachmentList atts;
10541
10542 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10543 if (FAILED(rc)) return rc;
10544
10545 data.llAttachedDevices.clear();
10546 for (MediumAttachmentList::const_iterator
10547 it = atts.begin();
10548 it != atts.end();
10549 ++it)
10550 {
10551 settings::AttachedDevice dev;
10552 IMediumAttachment *iA = *it;
10553 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10554 Medium *pMedium = pAttach->i_getMedium();
10555
10556 dev.deviceType = pAttach->i_getType();
10557 dev.lPort = pAttach->i_getPort();
10558 dev.lDevice = pAttach->i_getDevice();
10559 dev.fPassThrough = pAttach->i_getPassthrough();
10560 dev.fHotPluggable = pAttach->i_getHotPluggable();
10561 if (pMedium)
10562 {
10563 if (pMedium->i_isHostDrive())
10564 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10565 else
10566 dev.uuid = pMedium->i_getId();
10567 dev.fTempEject = pAttach->i_getTempEject();
10568 dev.fNonRotational = pAttach->i_getNonRotational();
10569 dev.fDiscard = pAttach->i_getDiscard();
10570 }
10571
10572 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10573
10574 data.llAttachedDevices.push_back(dev);
10575 }
10576
10577 return S_OK;
10578}
10579
10580/**
10581 * Saves machine state settings as defined by aFlags
10582 * (SaveSTS_* values).
10583 *
10584 * @param aFlags Combination of SaveSTS_* flags.
10585 *
10586 * @note Locks objects for writing.
10587 */
10588HRESULT Machine::i_saveStateSettings(int aFlags)
10589{
10590 if (aFlags == 0)
10591 return S_OK;
10592
10593 AutoCaller autoCaller(this);
10594 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10595
10596 /* This object's write lock is also necessary to serialize file access
10597 * (prevent concurrent reads and writes) */
10598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10599
10600 HRESULT rc = S_OK;
10601
10602 Assert(mData->pMachineConfigFile);
10603
10604 try
10605 {
10606 if (aFlags & SaveSTS_CurStateModified)
10607 mData->pMachineConfigFile->fCurrentStateModified = true;
10608
10609 if (aFlags & SaveSTS_StateFilePath)
10610 {
10611 if (!mSSData->strStateFilePath.isEmpty())
10612 /* try to make the file name relative to the settings file dir */
10613 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10614 else
10615 mData->pMachineConfigFile->strStateFile.setNull();
10616 }
10617
10618 if (aFlags & SaveSTS_StateTimeStamp)
10619 {
10620 Assert( mData->mMachineState != MachineState_Aborted
10621 || mSSData->strStateFilePath.isEmpty());
10622
10623 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10624
10625 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10626/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10627 }
10628
10629 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10630 }
10631 catch (...)
10632 {
10633 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10634 }
10635
10636 return rc;
10637}
10638
10639/**
10640 * Ensures that the given medium is added to a media registry. If this machine
10641 * was created with 4.0 or later, then the machine registry is used. Otherwise
10642 * the global VirtualBox media registry is used.
10643 *
10644 * Caller must NOT hold machine lock, media tree or any medium locks!
10645 *
10646 * @param pMedium
10647 */
10648void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10649{
10650 /* Paranoia checks: do not hold machine or media tree locks. */
10651 AssertReturnVoid(!isWriteLockOnCurrentThread());
10652 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10653
10654 ComObjPtr<Medium> pBase;
10655 {
10656 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10657 pBase = pMedium->i_getBase();
10658 }
10659
10660 /* Paranoia checks: do not hold medium locks. */
10661 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10662 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10663
10664 // decide which medium registry to use now that the medium is attached:
10665 Guid uuid;
10666 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10667 // machine XML is VirtualBox 4.0 or higher:
10668 uuid = i_getId(); // machine UUID
10669 else
10670 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10671
10672 if (pMedium->i_addRegistry(uuid))
10673 mParent->i_markRegistryModified(uuid);
10674
10675 /* For more complex hard disk structures it can happen that the base
10676 * medium isn't yet associated with any medium registry. Do that now. */
10677 if (pMedium != pBase)
10678 {
10679 /* Tree lock needed by Medium::addRegistry when recursing. */
10680 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10681 if (pBase->i_addRegistryRecursive(uuid))
10682 {
10683 treeLock.release();
10684 mParent->i_markRegistryModified(uuid);
10685 }
10686 }
10687}
10688
10689/**
10690 * Creates differencing hard disks for all normal hard disks attached to this
10691 * machine and a new set of attachments to refer to created disks.
10692 *
10693 * Used when taking a snapshot or when deleting the current state. Gets called
10694 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10695 *
10696 * This method assumes that mMediumAttachments contains the original hard disk
10697 * attachments it needs to create diffs for. On success, these attachments will
10698 * be replaced with the created diffs.
10699 *
10700 * Attachments with non-normal hard disks are left as is.
10701 *
10702 * If @a aOnline is @c false then the original hard disks that require implicit
10703 * diffs will be locked for reading. Otherwise it is assumed that they are
10704 * already locked for writing (when the VM was started). Note that in the latter
10705 * case it is responsibility of the caller to lock the newly created diffs for
10706 * writing if this method succeeds.
10707 *
10708 * @param aProgress Progress object to run (must contain at least as
10709 * many operations left as the number of hard disks
10710 * attached).
10711 * @param aWeight Weight of this operation.
10712 * @param aOnline Whether the VM was online prior to this operation.
10713 *
10714 * @note The progress object is not marked as completed, neither on success nor
10715 * on failure. This is a responsibility of the caller.
10716 *
10717 * @note Locks this object and the media tree for writing.
10718 */
10719HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10720 ULONG aWeight,
10721 bool aOnline)
10722{
10723 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10724
10725 AutoCaller autoCaller(this);
10726 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10727
10728 AutoMultiWriteLock2 alock(this->lockHandle(),
10729 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10730
10731 /* must be in a protective state because we release the lock below */
10732 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10733 || mData->mMachineState == MachineState_OnlineSnapshotting
10734 || mData->mMachineState == MachineState_LiveSnapshotting
10735 || mData->mMachineState == MachineState_RestoringSnapshot
10736 || mData->mMachineState == MachineState_DeletingSnapshot
10737 , E_FAIL);
10738
10739 HRESULT rc = S_OK;
10740
10741 // use appropriate locked media map (online or offline)
10742 MediumLockListMap lockedMediaOffline;
10743 MediumLockListMap *lockedMediaMap;
10744 if (aOnline)
10745 lockedMediaMap = &mData->mSession.mLockedMedia;
10746 else
10747 lockedMediaMap = &lockedMediaOffline;
10748
10749 try
10750 {
10751 if (!aOnline)
10752 {
10753 /* lock all attached hard disks early to detect "in use"
10754 * situations before creating actual diffs */
10755 for (MediumAttachmentList::const_iterator
10756 it = mMediumAttachments->begin();
10757 it != mMediumAttachments->end();
10758 ++it)
10759 {
10760 MediumAttachment *pAtt = *it;
10761 if (pAtt->i_getType() == DeviceType_HardDisk)
10762 {
10763 Medium *pMedium = pAtt->i_getMedium();
10764 Assert(pMedium);
10765
10766 MediumLockList *pMediumLockList(new MediumLockList());
10767 alock.release();
10768 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10769 NULL /* pToLockWrite */,
10770 false /* fMediumLockWriteAll */,
10771 NULL,
10772 *pMediumLockList);
10773 alock.acquire();
10774 if (FAILED(rc))
10775 {
10776 delete pMediumLockList;
10777 throw rc;
10778 }
10779 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10780 if (FAILED(rc))
10781 {
10782 throw setError(rc,
10783 tr("Collecting locking information for all attached media failed"));
10784 }
10785 }
10786 }
10787
10788 /* Now lock all media. If this fails, nothing is locked. */
10789 alock.release();
10790 rc = lockedMediaMap->Lock();
10791 alock.acquire();
10792 if (FAILED(rc))
10793 {
10794 throw setError(rc,
10795 tr("Locking of attached media failed"));
10796 }
10797 }
10798
10799 /* remember the current list (note that we don't use backup() since
10800 * mMediumAttachments may be already backed up) */
10801 MediumAttachmentList atts = *mMediumAttachments.data();
10802
10803 /* start from scratch */
10804 mMediumAttachments->clear();
10805
10806 /* go through remembered attachments and create diffs for normal hard
10807 * disks and attach them */
10808 for (MediumAttachmentList::const_iterator
10809 it = atts.begin();
10810 it != atts.end();
10811 ++it)
10812 {
10813 MediumAttachment *pAtt = *it;
10814
10815 DeviceType_T devType = pAtt->i_getType();
10816 Medium *pMedium = pAtt->i_getMedium();
10817
10818 if ( devType != DeviceType_HardDisk
10819 || pMedium == NULL
10820 || pMedium->i_getType() != MediumType_Normal)
10821 {
10822 /* copy the attachment as is */
10823
10824 /** @todo the progress object created in SessionMachine::TakeSnaphot
10825 * only expects operations for hard disks. Later other
10826 * device types need to show up in the progress as well. */
10827 if (devType == DeviceType_HardDisk)
10828 {
10829 if (pMedium == NULL)
10830 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10831 aWeight); // weight
10832 else
10833 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10834 pMedium->i_getBase()->i_getName().c_str()).raw(),
10835 aWeight); // weight
10836 }
10837
10838 mMediumAttachments->push_back(pAtt);
10839 continue;
10840 }
10841
10842 /* need a diff */
10843 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10844 pMedium->i_getBase()->i_getName().c_str()).raw(),
10845 aWeight); // weight
10846
10847 Utf8Str strFullSnapshotFolder;
10848 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10849
10850 ComObjPtr<Medium> diff;
10851 diff.createObject();
10852 // store the diff in the same registry as the parent
10853 // (this cannot fail here because we can't create implicit diffs for
10854 // unregistered images)
10855 Guid uuidRegistryParent;
10856 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10857 Assert(fInRegistry); NOREF(fInRegistry);
10858 rc = diff->init(mParent,
10859 pMedium->i_getPreferredDiffFormat(),
10860 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10861 uuidRegistryParent,
10862 DeviceType_HardDisk);
10863 if (FAILED(rc)) throw rc;
10864
10865 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10866 * the push_back? Looks like we're going to release medium with the
10867 * wrong kind of lock (general issue with if we fail anywhere at all)
10868 * and an orphaned VDI in the snapshots folder. */
10869
10870 /* update the appropriate lock list */
10871 MediumLockList *pMediumLockList;
10872 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10873 AssertComRCThrowRC(rc);
10874 if (aOnline)
10875 {
10876 alock.release();
10877 /* The currently attached medium will be read-only, change
10878 * the lock type to read. */
10879 rc = pMediumLockList->Update(pMedium, false);
10880 alock.acquire();
10881 AssertComRCThrowRC(rc);
10882 }
10883
10884 /* release the locks before the potentially lengthy operation */
10885 alock.release();
10886 rc = pMedium->i_createDiffStorage(diff,
10887 pMedium->i_getPreferredDiffVariant(),
10888 pMediumLockList,
10889 NULL /* aProgress */,
10890 true /* aWait */);
10891 alock.acquire();
10892 if (FAILED(rc)) throw rc;
10893
10894 /* actual lock list update is done in Machine::i_commitMedia */
10895
10896 rc = diff->i_addBackReference(mData->mUuid);
10897 AssertComRCThrowRC(rc);
10898
10899 /* add a new attachment */
10900 ComObjPtr<MediumAttachment> attachment;
10901 attachment.createObject();
10902 rc = attachment->init(this,
10903 diff,
10904 pAtt->i_getControllerName(),
10905 pAtt->i_getPort(),
10906 pAtt->i_getDevice(),
10907 DeviceType_HardDisk,
10908 true /* aImplicit */,
10909 false /* aPassthrough */,
10910 false /* aTempEject */,
10911 pAtt->i_getNonRotational(),
10912 pAtt->i_getDiscard(),
10913 pAtt->i_getHotPluggable(),
10914 pAtt->i_getBandwidthGroup());
10915 if (FAILED(rc)) throw rc;
10916
10917 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10918 AssertComRCThrowRC(rc);
10919 mMediumAttachments->push_back(attachment);
10920 }
10921 }
10922 catch (HRESULT aRC) { rc = aRC; }
10923
10924 /* unlock all hard disks we locked when there is no VM */
10925 if (!aOnline)
10926 {
10927 ErrorInfoKeeper eik;
10928
10929 HRESULT rc1 = lockedMediaMap->Clear();
10930 AssertComRC(rc1);
10931 }
10932
10933 return rc;
10934}
10935
10936/**
10937 * Deletes implicit differencing hard disks created either by
10938 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10939 * mMediumAttachments.
10940 *
10941 * Note that to delete hard disks created by #attachDevice() this method is
10942 * called from #i_rollbackMedia() when the changes are rolled back.
10943 *
10944 * @note Locks this object and the media tree for writing.
10945 */
10946HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10947{
10948 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10949
10950 AutoCaller autoCaller(this);
10951 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10952
10953 AutoMultiWriteLock2 alock(this->lockHandle(),
10954 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10955
10956 /* We absolutely must have backed up state. */
10957 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10958
10959 /* Check if there are any implicitly created diff images. */
10960 bool fImplicitDiffs = false;
10961 for (MediumAttachmentList::const_iterator
10962 it = mMediumAttachments->begin();
10963 it != mMediumAttachments->end();
10964 ++it)
10965 {
10966 const ComObjPtr<MediumAttachment> &pAtt = *it;
10967 if (pAtt->i_isImplicit())
10968 {
10969 fImplicitDiffs = true;
10970 break;
10971 }
10972 }
10973 /* If there is nothing to do, leave early. This saves lots of image locking
10974 * effort. It also avoids a MachineStateChanged event without real reason.
10975 * This is important e.g. when loading a VM config, because there should be
10976 * no events. Otherwise API clients can become thoroughly confused for
10977 * inaccessible VMs (the code for loading VM configs uses this method for
10978 * cleanup if the config makes no sense), as they take such events as an
10979 * indication that the VM is alive, and they would force the VM config to
10980 * be reread, leading to an endless loop. */
10981 if (!fImplicitDiffs)
10982 return S_OK;
10983
10984 HRESULT rc = S_OK;
10985 MachineState_T oldState = mData->mMachineState;
10986
10987 /* will release the lock before the potentially lengthy operation,
10988 * so protect with the special state (unless already protected) */
10989 if ( oldState != MachineState_Snapshotting
10990 && oldState != MachineState_OnlineSnapshotting
10991 && oldState != MachineState_LiveSnapshotting
10992 && oldState != MachineState_RestoringSnapshot
10993 && oldState != MachineState_DeletingSnapshot
10994 && oldState != MachineState_DeletingSnapshotOnline
10995 && oldState != MachineState_DeletingSnapshotPaused
10996 )
10997 i_setMachineState(MachineState_SettingUp);
10998
10999 // use appropriate locked media map (online or offline)
11000 MediumLockListMap lockedMediaOffline;
11001 MediumLockListMap *lockedMediaMap;
11002 if (aOnline)
11003 lockedMediaMap = &mData->mSession.mLockedMedia;
11004 else
11005 lockedMediaMap = &lockedMediaOffline;
11006
11007 try
11008 {
11009 if (!aOnline)
11010 {
11011 /* lock all attached hard disks early to detect "in use"
11012 * situations before deleting actual diffs */
11013 for (MediumAttachmentList::const_iterator
11014 it = mMediumAttachments->begin();
11015 it != mMediumAttachments->end();
11016 ++it)
11017 {
11018 MediumAttachment *pAtt = *it;
11019 if (pAtt->i_getType() == DeviceType_HardDisk)
11020 {
11021 Medium *pMedium = pAtt->i_getMedium();
11022 Assert(pMedium);
11023
11024 MediumLockList *pMediumLockList(new MediumLockList());
11025 alock.release();
11026 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11027 NULL /* pToLockWrite */,
11028 false /* fMediumLockWriteAll */,
11029 NULL,
11030 *pMediumLockList);
11031 alock.acquire();
11032
11033 if (FAILED(rc))
11034 {
11035 delete pMediumLockList;
11036 throw rc;
11037 }
11038
11039 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11040 if (FAILED(rc))
11041 throw rc;
11042 }
11043 }
11044
11045 if (FAILED(rc))
11046 throw rc;
11047 } // end of offline
11048
11049 /* Lock lists are now up to date and include implicitly created media */
11050
11051 /* Go through remembered attachments and delete all implicitly created
11052 * diffs and fix up the attachment information */
11053 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11054 MediumAttachmentList implicitAtts;
11055 for (MediumAttachmentList::const_iterator
11056 it = mMediumAttachments->begin();
11057 it != mMediumAttachments->end();
11058 ++it)
11059 {
11060 ComObjPtr<MediumAttachment> pAtt = *it;
11061 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11062 if (pMedium.isNull())
11063 continue;
11064
11065 // Implicit attachments go on the list for deletion and back references are removed.
11066 if (pAtt->i_isImplicit())
11067 {
11068 /* Deassociate and mark for deletion */
11069 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11070 rc = pMedium->i_removeBackReference(mData->mUuid);
11071 if (FAILED(rc))
11072 throw rc;
11073 implicitAtts.push_back(pAtt);
11074 continue;
11075 }
11076
11077 /* Was this medium attached before? */
11078 if (!i_findAttachment(oldAtts, pMedium))
11079 {
11080 /* no: de-associate */
11081 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11082 rc = pMedium->i_removeBackReference(mData->mUuid);
11083 if (FAILED(rc))
11084 throw rc;
11085 continue;
11086 }
11087 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11088 }
11089
11090 /* If there are implicit attachments to delete, throw away the lock
11091 * map contents (which will unlock all media) since the medium
11092 * attachments will be rolled back. Below we need to completely
11093 * recreate the lock map anyway since it is infinitely complex to
11094 * do this incrementally (would need reconstructing each attachment
11095 * change, which would be extremely hairy). */
11096 if (implicitAtts.size() != 0)
11097 {
11098 ErrorInfoKeeper eik;
11099
11100 HRESULT rc1 = lockedMediaMap->Clear();
11101 AssertComRC(rc1);
11102 }
11103
11104 /* rollback hard disk changes */
11105 mMediumAttachments.rollback();
11106
11107 MultiResult mrc(S_OK);
11108
11109 // Delete unused implicit diffs.
11110 if (implicitAtts.size() != 0)
11111 {
11112 alock.release();
11113
11114 for (MediumAttachmentList::const_iterator
11115 it = implicitAtts.begin();
11116 it != implicitAtts.end();
11117 ++it)
11118 {
11119 // Remove medium associated with this attachment.
11120 ComObjPtr<MediumAttachment> pAtt = *it;
11121 Assert(pAtt);
11122 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11123 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11124 Assert(pMedium);
11125
11126 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11127 // continue on delete failure, just collect error messages
11128 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11129 pMedium->i_getLocationFull().c_str() ));
11130 mrc = rc;
11131 }
11132 // Clear the list of deleted implicit attachments now, while not
11133 // holding the lock, as it will ultimately trigger Medium::uninit()
11134 // calls which assume that the media tree lock isn't held.
11135 implicitAtts.clear();
11136
11137 alock.acquire();
11138
11139 /* if there is a VM recreate media lock map as mentioned above,
11140 * otherwise it is a waste of time and we leave things unlocked */
11141 if (aOnline)
11142 {
11143 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11144 /* must never be NULL, but better safe than sorry */
11145 if (!pMachine.isNull())
11146 {
11147 alock.release();
11148 rc = mData->mSession.mMachine->i_lockMedia();
11149 alock.acquire();
11150 if (FAILED(rc))
11151 throw rc;
11152 }
11153 }
11154 }
11155 }
11156 catch (HRESULT aRC) {rc = aRC;}
11157
11158 if (mData->mMachineState == MachineState_SettingUp)
11159 i_setMachineState(oldState);
11160
11161 /* unlock all hard disks we locked when there is no VM */
11162 if (!aOnline)
11163 {
11164 ErrorInfoKeeper eik;
11165
11166 HRESULT rc1 = lockedMediaMap->Clear();
11167 AssertComRC(rc1);
11168 }
11169
11170 return rc;
11171}
11172
11173
11174/**
11175 * Looks through the given list of media attachments for one with the given parameters
11176 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11177 * can be searched as well if needed.
11178 *
11179 * @param ll
11180 * @param aControllerName
11181 * @param aControllerPort
11182 * @param aDevice
11183 * @return
11184 */
11185MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11186 const Utf8Str &aControllerName,
11187 LONG aControllerPort,
11188 LONG aDevice)
11189{
11190 for (MediumAttachmentList::const_iterator
11191 it = ll.begin();
11192 it != ll.end();
11193 ++it)
11194 {
11195 MediumAttachment *pAttach = *it;
11196 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11197 return pAttach;
11198 }
11199
11200 return NULL;
11201}
11202
11203/**
11204 * Looks through the given list of media attachments for one with the given parameters
11205 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11206 * can be searched as well if needed.
11207 *
11208 * @param ll
11209 * @param pMedium
11210 * @return
11211 */
11212MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11213 ComObjPtr<Medium> pMedium)
11214{
11215 for (MediumAttachmentList::const_iterator
11216 it = ll.begin();
11217 it != ll.end();
11218 ++it)
11219 {
11220 MediumAttachment *pAttach = *it;
11221 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11222 if (pMediumThis == pMedium)
11223 return pAttach;
11224 }
11225
11226 return NULL;
11227}
11228
11229/**
11230 * Looks through the given list of media attachments for one with the given parameters
11231 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11232 * can be searched as well if needed.
11233 *
11234 * @param ll
11235 * @param id
11236 * @return
11237 */
11238MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11239 Guid &id)
11240{
11241 for (MediumAttachmentList::const_iterator
11242 it = ll.begin();
11243 it != ll.end();
11244 ++it)
11245 {
11246 MediumAttachment *pAttach = *it;
11247 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11248 if (pMediumThis->i_getId() == id)
11249 return pAttach;
11250 }
11251
11252 return NULL;
11253}
11254
11255/**
11256 * Main implementation for Machine::DetachDevice. This also gets called
11257 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11258 *
11259 * @param pAttach Medium attachment to detach.
11260 * @param writeLock Machine write lock which the caller must have locked once.
11261 * This may be released temporarily in here.
11262 * @param pSnapshot If NULL, then the detachment is for the current machine.
11263 * Otherwise this is for a SnapshotMachine, and this must be
11264 * its snapshot.
11265 * @return
11266 */
11267HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11268 AutoWriteLock &writeLock,
11269 Snapshot *pSnapshot)
11270{
11271 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11272 DeviceType_T mediumType = pAttach->i_getType();
11273
11274 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11275
11276 if (pAttach->i_isImplicit())
11277 {
11278 /* attempt to implicitly delete the implicitly created diff */
11279
11280 /// @todo move the implicit flag from MediumAttachment to Medium
11281 /// and forbid any hard disk operation when it is implicit. Or maybe
11282 /// a special media state for it to make it even more simple.
11283
11284 Assert(mMediumAttachments.isBackedUp());
11285
11286 /* will release the lock before the potentially lengthy operation, so
11287 * protect with the special state */
11288 MachineState_T oldState = mData->mMachineState;
11289 i_setMachineState(MachineState_SettingUp);
11290
11291 writeLock.release();
11292
11293 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11294 true /*aWait*/);
11295
11296 writeLock.acquire();
11297
11298 i_setMachineState(oldState);
11299
11300 if (FAILED(rc)) return rc;
11301 }
11302
11303 i_setModified(IsModified_Storage);
11304 mMediumAttachments.backup();
11305 mMediumAttachments->remove(pAttach);
11306
11307 if (!oldmedium.isNull())
11308 {
11309 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11310 if (pSnapshot)
11311 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11312 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11313 else if (mediumType != DeviceType_HardDisk)
11314 oldmedium->i_removeBackReference(mData->mUuid);
11315 }
11316
11317 return S_OK;
11318}
11319
11320/**
11321 * Goes thru all media of the given list and
11322 *
11323 * 1) calls i_detachDevice() on each of them for this machine and
11324 * 2) adds all Medium objects found in the process to the given list,
11325 * depending on cleanupMode.
11326 *
11327 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11328 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11329 * media to the list.
11330 *
11331 * This gets called from Machine::Unregister, both for the actual Machine and
11332 * the SnapshotMachine objects that might be found in the snapshots.
11333 *
11334 * Requires caller and locking. The machine lock must be passed in because it
11335 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11336 *
11337 * @param writeLock Machine lock from top-level caller; this gets passed to
11338 * i_detachDevice.
11339 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11340 * object if called for a SnapshotMachine.
11341 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11342 * added to llMedia; if Full, then all media get added;
11343 * otherwise no media get added.
11344 * @param llMedia Caller's list to receive Medium objects which got detached so
11345 * caller can close() them, depending on cleanupMode.
11346 * @return
11347 */
11348HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11349 Snapshot *pSnapshot,
11350 CleanupMode_T cleanupMode,
11351 MediaList &llMedia)
11352{
11353 Assert(isWriteLockOnCurrentThread());
11354
11355 HRESULT rc;
11356
11357 // make a temporary list because i_detachDevice invalidates iterators into
11358 // mMediumAttachments
11359 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11360
11361 for (MediumAttachmentList::iterator
11362 it = llAttachments2.begin();
11363 it != llAttachments2.end();
11364 ++it)
11365 {
11366 ComObjPtr<MediumAttachment> &pAttach = *it;
11367 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11368
11369 if (!pMedium.isNull())
11370 {
11371 AutoCaller mac(pMedium);
11372 if (FAILED(mac.rc())) return mac.rc();
11373 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11374 DeviceType_T devType = pMedium->i_getDeviceType();
11375 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11376 && devType == DeviceType_HardDisk)
11377 || (cleanupMode == CleanupMode_Full)
11378 )
11379 {
11380 llMedia.push_back(pMedium);
11381 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11382 /* Not allowed to keep this lock as below we need the parent
11383 * medium lock, and the lock order is parent to child. */
11384 lock.release();
11385 /*
11386 * Search for medias which are not attached to any machine, but
11387 * in the chain to an attached disk. Mediums are only consided
11388 * if they are:
11389 * - have only one child
11390 * - no references to any machines
11391 * - are of normal medium type
11392 */
11393 while (!pParent.isNull())
11394 {
11395 AutoCaller mac1(pParent);
11396 if (FAILED(mac1.rc())) return mac1.rc();
11397 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11398 if (pParent->i_getChildren().size() == 1)
11399 {
11400 if ( pParent->i_getMachineBackRefCount() == 0
11401 && pParent->i_getType() == MediumType_Normal
11402 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11403 llMedia.push_back(pParent);
11404 }
11405 else
11406 break;
11407 pParent = pParent->i_getParent();
11408 }
11409 }
11410 }
11411
11412 // real machine: then we need to use the proper method
11413 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11414
11415 if (FAILED(rc))
11416 return rc;
11417 }
11418
11419 return S_OK;
11420}
11421
11422/**
11423 * Perform deferred hard disk detachments.
11424 *
11425 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11426 * changed (not backed up).
11427 *
11428 * If @a aOnline is @c true then this method will also unlock the old hard
11429 * disks for which the new implicit diffs were created and will lock these new
11430 * diffs for writing.
11431 *
11432 * @param aOnline Whether the VM was online prior to this operation.
11433 *
11434 * @note Locks this object for writing!
11435 */
11436void Machine::i_commitMedia(bool aOnline /*= false*/)
11437{
11438 AutoCaller autoCaller(this);
11439 AssertComRCReturnVoid(autoCaller.rc());
11440
11441 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11442
11443 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11444
11445 HRESULT rc = S_OK;
11446
11447 /* no attach/detach operations -- nothing to do */
11448 if (!mMediumAttachments.isBackedUp())
11449 return;
11450
11451 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11452 bool fMediaNeedsLocking = false;
11453
11454 /* enumerate new attachments */
11455 for (MediumAttachmentList::const_iterator
11456 it = mMediumAttachments->begin();
11457 it != mMediumAttachments->end();
11458 ++it)
11459 {
11460 MediumAttachment *pAttach = *it;
11461
11462 pAttach->i_commit();
11463
11464 Medium *pMedium = pAttach->i_getMedium();
11465 bool fImplicit = pAttach->i_isImplicit();
11466
11467 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11468 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11469 fImplicit));
11470
11471 /** @todo convert all this Machine-based voodoo to MediumAttachment
11472 * based commit logic. */
11473 if (fImplicit)
11474 {
11475 /* convert implicit attachment to normal */
11476 pAttach->i_setImplicit(false);
11477
11478 if ( aOnline
11479 && pMedium
11480 && pAttach->i_getType() == DeviceType_HardDisk
11481 )
11482 {
11483 /* update the appropriate lock list */
11484 MediumLockList *pMediumLockList;
11485 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11486 AssertComRC(rc);
11487 if (pMediumLockList)
11488 {
11489 /* unlock if there's a need to change the locking */
11490 if (!fMediaNeedsLocking)
11491 {
11492 rc = mData->mSession.mLockedMedia.Unlock();
11493 AssertComRC(rc);
11494 fMediaNeedsLocking = true;
11495 }
11496 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11497 AssertComRC(rc);
11498 rc = pMediumLockList->Append(pMedium, true);
11499 AssertComRC(rc);
11500 }
11501 }
11502
11503 continue;
11504 }
11505
11506 if (pMedium)
11507 {
11508 /* was this medium attached before? */
11509 for (MediumAttachmentList::iterator
11510 oldIt = oldAtts.begin();
11511 oldIt != oldAtts.end();
11512 ++oldIt)
11513 {
11514 MediumAttachment *pOldAttach = *oldIt;
11515 if (pOldAttach->i_getMedium() == pMedium)
11516 {
11517 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11518
11519 /* yes: remove from old to avoid de-association */
11520 oldAtts.erase(oldIt);
11521 break;
11522 }
11523 }
11524 }
11525 }
11526
11527 /* enumerate remaining old attachments and de-associate from the
11528 * current machine state */
11529 for (MediumAttachmentList::const_iterator
11530 it = oldAtts.begin();
11531 it != oldAtts.end();
11532 ++it)
11533 {
11534 MediumAttachment *pAttach = *it;
11535 Medium *pMedium = pAttach->i_getMedium();
11536
11537 /* Detach only hard disks, since DVD/floppy media is detached
11538 * instantly in MountMedium. */
11539 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11540 {
11541 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11542
11543 /* now de-associate from the current machine state */
11544 rc = pMedium->i_removeBackReference(mData->mUuid);
11545 AssertComRC(rc);
11546
11547 if (aOnline)
11548 {
11549 /* unlock since medium is not used anymore */
11550 MediumLockList *pMediumLockList;
11551 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11552 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11553 {
11554 /* this happens for online snapshots, there the attachment
11555 * is changing, but only to a diff image created under
11556 * the old one, so there is no separate lock list */
11557 Assert(!pMediumLockList);
11558 }
11559 else
11560 {
11561 AssertComRC(rc);
11562 if (pMediumLockList)
11563 {
11564 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11565 AssertComRC(rc);
11566 }
11567 }
11568 }
11569 }
11570 }
11571
11572 /* take media locks again so that the locking state is consistent */
11573 if (fMediaNeedsLocking)
11574 {
11575 Assert(aOnline);
11576 rc = mData->mSession.mLockedMedia.Lock();
11577 AssertComRC(rc);
11578 }
11579
11580 /* commit the hard disk changes */
11581 mMediumAttachments.commit();
11582
11583 if (i_isSessionMachine())
11584 {
11585 /*
11586 * Update the parent machine to point to the new owner.
11587 * This is necessary because the stored parent will point to the
11588 * session machine otherwise and cause crashes or errors later
11589 * when the session machine gets invalid.
11590 */
11591 /** @todo Change the MediumAttachment class to behave like any other
11592 * class in this regard by creating peer MediumAttachment
11593 * objects for session machines and share the data with the peer
11594 * machine.
11595 */
11596 for (MediumAttachmentList::const_iterator
11597 it = mMediumAttachments->begin();
11598 it != mMediumAttachments->end();
11599 ++it)
11600 (*it)->i_updateParentMachine(mPeer);
11601
11602 /* attach new data to the primary machine and reshare it */
11603 mPeer->mMediumAttachments.attach(mMediumAttachments);
11604 }
11605
11606 return;
11607}
11608
11609/**
11610 * Perform deferred deletion of implicitly created diffs.
11611 *
11612 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11613 * changed (not backed up).
11614 *
11615 * @note Locks this object for writing!
11616 */
11617void Machine::i_rollbackMedia()
11618{
11619 AutoCaller autoCaller(this);
11620 AssertComRCReturnVoid(autoCaller.rc());
11621
11622 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11623 LogFlowThisFunc(("Entering rollbackMedia\n"));
11624
11625 HRESULT rc = S_OK;
11626
11627 /* no attach/detach operations -- nothing to do */
11628 if (!mMediumAttachments.isBackedUp())
11629 return;
11630
11631 /* enumerate new attachments */
11632 for (MediumAttachmentList::const_iterator
11633 it = mMediumAttachments->begin();
11634 it != mMediumAttachments->end();
11635 ++it)
11636 {
11637 MediumAttachment *pAttach = *it;
11638 /* Fix up the backrefs for DVD/floppy media. */
11639 if (pAttach->i_getType() != DeviceType_HardDisk)
11640 {
11641 Medium *pMedium = pAttach->i_getMedium();
11642 if (pMedium)
11643 {
11644 rc = pMedium->i_removeBackReference(mData->mUuid);
11645 AssertComRC(rc);
11646 }
11647 }
11648
11649 (*it)->i_rollback();
11650
11651 pAttach = *it;
11652 /* Fix up the backrefs for DVD/floppy media. */
11653 if (pAttach->i_getType() != DeviceType_HardDisk)
11654 {
11655 Medium *pMedium = pAttach->i_getMedium();
11656 if (pMedium)
11657 {
11658 rc = pMedium->i_addBackReference(mData->mUuid);
11659 AssertComRC(rc);
11660 }
11661 }
11662 }
11663
11664 /** @todo convert all this Machine-based voodoo to MediumAttachment
11665 * based rollback logic. */
11666 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11667
11668 return;
11669}
11670
11671/**
11672 * Returns true if the settings file is located in the directory named exactly
11673 * as the machine; this means, among other things, that the machine directory
11674 * should be auto-renamed.
11675 *
11676 * @param aSettingsDir if not NULL, the full machine settings file directory
11677 * name will be assigned there.
11678 *
11679 * @note Doesn't lock anything.
11680 * @note Not thread safe (must be called from this object's lock).
11681 */
11682bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11683{
11684 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11685 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11686 if (aSettingsDir)
11687 *aSettingsDir = strMachineDirName;
11688 strMachineDirName.stripPath(); // vmname
11689 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11690 strConfigFileOnly.stripPath() // vmname.vbox
11691 .stripSuffix(); // vmname
11692 /** @todo hack, make somehow use of ComposeMachineFilename */
11693 if (mUserData->s.fDirectoryIncludesUUID)
11694 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11695
11696 AssertReturn(!strMachineDirName.isEmpty(), false);
11697 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11698
11699 return strMachineDirName == strConfigFileOnly;
11700}
11701
11702/**
11703 * Discards all changes to machine settings.
11704 *
11705 * @param aNotify Whether to notify the direct session about changes or not.
11706 *
11707 * @note Locks objects for writing!
11708 */
11709void Machine::i_rollback(bool aNotify)
11710{
11711 AutoCaller autoCaller(this);
11712 AssertComRCReturn(autoCaller.rc(), (void)0);
11713
11714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11715
11716 if (!mStorageControllers.isNull())
11717 {
11718 if (mStorageControllers.isBackedUp())
11719 {
11720 /* unitialize all new devices (absent in the backed up list). */
11721 StorageControllerList *backedList = mStorageControllers.backedUpData();
11722 for (StorageControllerList::const_iterator
11723 it = mStorageControllers->begin();
11724 it != mStorageControllers->end();
11725 ++it)
11726 {
11727 if ( std::find(backedList->begin(), backedList->end(), *it)
11728 == backedList->end()
11729 )
11730 {
11731 (*it)->uninit();
11732 }
11733 }
11734
11735 /* restore the list */
11736 mStorageControllers.rollback();
11737 }
11738
11739 /* rollback any changes to devices after restoring the list */
11740 if (mData->flModifications & IsModified_Storage)
11741 {
11742 for (StorageControllerList::const_iterator
11743 it = mStorageControllers->begin();
11744 it != mStorageControllers->end();
11745 ++it)
11746 {
11747 (*it)->i_rollback();
11748 }
11749 }
11750 }
11751
11752 if (!mUSBControllers.isNull())
11753 {
11754 if (mUSBControllers.isBackedUp())
11755 {
11756 /* unitialize all new devices (absent in the backed up list). */
11757 USBControllerList *backedList = mUSBControllers.backedUpData();
11758 for (USBControllerList::const_iterator
11759 it = mUSBControllers->begin();
11760 it != mUSBControllers->end();
11761 ++it)
11762 {
11763 if ( std::find(backedList->begin(), backedList->end(), *it)
11764 == backedList->end()
11765 )
11766 {
11767 (*it)->uninit();
11768 }
11769 }
11770
11771 /* restore the list */
11772 mUSBControllers.rollback();
11773 }
11774
11775 /* rollback any changes to devices after restoring the list */
11776 if (mData->flModifications & IsModified_USB)
11777 {
11778 for (USBControllerList::const_iterator
11779 it = mUSBControllers->begin();
11780 it != mUSBControllers->end();
11781 ++it)
11782 {
11783 (*it)->i_rollback();
11784 }
11785 }
11786 }
11787
11788 mUserData.rollback();
11789
11790 mHWData.rollback();
11791
11792 if (mData->flModifications & IsModified_Storage)
11793 i_rollbackMedia();
11794
11795 if (mBIOSSettings)
11796 mBIOSSettings->i_rollback();
11797
11798 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11799 mVRDEServer->i_rollback();
11800
11801 if (mAudioAdapter)
11802 mAudioAdapter->i_rollback();
11803
11804 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11805 mUSBDeviceFilters->i_rollback();
11806
11807 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11808 mBandwidthControl->i_rollback();
11809
11810 if (!mHWData.isNull())
11811 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11812 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11813 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11814 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11815
11816 if (mData->flModifications & IsModified_NetworkAdapters)
11817 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11818 if ( mNetworkAdapters[slot]
11819 && mNetworkAdapters[slot]->i_isModified())
11820 {
11821 mNetworkAdapters[slot]->i_rollback();
11822 networkAdapters[slot] = mNetworkAdapters[slot];
11823 }
11824
11825 if (mData->flModifications & IsModified_SerialPorts)
11826 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11827 if ( mSerialPorts[slot]
11828 && mSerialPorts[slot]->i_isModified())
11829 {
11830 mSerialPorts[slot]->i_rollback();
11831 serialPorts[slot] = mSerialPorts[slot];
11832 }
11833
11834 if (mData->flModifications & IsModified_ParallelPorts)
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11836 if ( mParallelPorts[slot]
11837 && mParallelPorts[slot]->i_isModified())
11838 {
11839 mParallelPorts[slot]->i_rollback();
11840 parallelPorts[slot] = mParallelPorts[slot];
11841 }
11842
11843 if (aNotify)
11844 {
11845 /* inform the direct session about changes */
11846
11847 ComObjPtr<Machine> that = this;
11848 uint32_t flModifications = mData->flModifications;
11849 alock.release();
11850
11851 if (flModifications & IsModified_SharedFolders)
11852 that->i_onSharedFolderChange();
11853
11854 if (flModifications & IsModified_VRDEServer)
11855 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11856 if (flModifications & IsModified_USB)
11857 that->i_onUSBControllerChange();
11858
11859 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11860 if (networkAdapters[slot])
11861 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11862 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11863 if (serialPorts[slot])
11864 that->i_onSerialPortChange(serialPorts[slot]);
11865 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11866 if (parallelPorts[slot])
11867 that->i_onParallelPortChange(parallelPorts[slot]);
11868
11869 if (flModifications & IsModified_Storage)
11870 that->i_onStorageControllerChange();
11871
11872#if 0
11873 if (flModifications & IsModified_BandwidthControl)
11874 that->onBandwidthControlChange();
11875#endif
11876 }
11877}
11878
11879/**
11880 * Commits all the changes to machine settings.
11881 *
11882 * Note that this operation is supposed to never fail.
11883 *
11884 * @note Locks this object and children for writing.
11885 */
11886void Machine::i_commit()
11887{
11888 AutoCaller autoCaller(this);
11889 AssertComRCReturnVoid(autoCaller.rc());
11890
11891 AutoCaller peerCaller(mPeer);
11892 AssertComRCReturnVoid(peerCaller.rc());
11893
11894 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11895
11896 /*
11897 * use safe commit to ensure Snapshot machines (that share mUserData)
11898 * will still refer to a valid memory location
11899 */
11900 mUserData.commitCopy();
11901
11902 mHWData.commit();
11903
11904 if (mMediumAttachments.isBackedUp())
11905 i_commitMedia(Global::IsOnline(mData->mMachineState));
11906
11907 mBIOSSettings->i_commit();
11908 mVRDEServer->i_commit();
11909 mAudioAdapter->i_commit();
11910 mUSBDeviceFilters->i_commit();
11911 mBandwidthControl->i_commit();
11912
11913 /* Since mNetworkAdapters is a list which might have been changed (resized)
11914 * without using the Backupable<> template we need to handle the copying
11915 * of the list entries manually, including the creation of peers for the
11916 * new objects. */
11917 bool commitNetworkAdapters = false;
11918 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11919 if (mPeer)
11920 {
11921 /* commit everything, even the ones which will go away */
11922 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11923 mNetworkAdapters[slot]->i_commit();
11924 /* copy over the new entries, creating a peer and uninit the original */
11925 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11926 for (size_t slot = 0; slot < newSize; slot++)
11927 {
11928 /* look if this adapter has a peer device */
11929 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11930 if (!peer)
11931 {
11932 /* no peer means the adapter is a newly created one;
11933 * create a peer owning data this data share it with */
11934 peer.createObject();
11935 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11936 }
11937 mPeer->mNetworkAdapters[slot] = peer;
11938 }
11939 /* uninit any no longer needed network adapters */
11940 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11941 mNetworkAdapters[slot]->uninit();
11942 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11943 {
11944 if (mPeer->mNetworkAdapters[slot])
11945 mPeer->mNetworkAdapters[slot]->uninit();
11946 }
11947 /* Keep the original network adapter count until this point, so that
11948 * discarding a chipset type change will not lose settings. */
11949 mNetworkAdapters.resize(newSize);
11950 mPeer->mNetworkAdapters.resize(newSize);
11951 }
11952 else
11953 {
11954 /* we have no peer (our parent is the newly created machine);
11955 * just commit changes to the network adapters */
11956 commitNetworkAdapters = true;
11957 }
11958 if (commitNetworkAdapters)
11959 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11960 mNetworkAdapters[slot]->i_commit();
11961
11962 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11963 mSerialPorts[slot]->i_commit();
11964 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11965 mParallelPorts[slot]->i_commit();
11966
11967 bool commitStorageControllers = false;
11968
11969 if (mStorageControllers.isBackedUp())
11970 {
11971 mStorageControllers.commit();
11972
11973 if (mPeer)
11974 {
11975 /* Commit all changes to new controllers (this will reshare data with
11976 * peers for those who have peers) */
11977 StorageControllerList *newList = new StorageControllerList();
11978 for (StorageControllerList::const_iterator
11979 it = mStorageControllers->begin();
11980 it != mStorageControllers->end();
11981 ++it)
11982 {
11983 (*it)->i_commit();
11984
11985 /* look if this controller has a peer device */
11986 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11987 if (!peer)
11988 {
11989 /* no peer means the device is a newly created one;
11990 * create a peer owning data this device share it with */
11991 peer.createObject();
11992 peer->init(mPeer, *it, true /* aReshare */);
11993 }
11994 else
11995 {
11996 /* remove peer from the old list */
11997 mPeer->mStorageControllers->remove(peer);
11998 }
11999 /* and add it to the new list */
12000 newList->push_back(peer);
12001 }
12002
12003 /* uninit old peer's controllers that are left */
12004 for (StorageControllerList::const_iterator
12005 it = mPeer->mStorageControllers->begin();
12006 it != mPeer->mStorageControllers->end();
12007 ++it)
12008 {
12009 (*it)->uninit();
12010 }
12011
12012 /* attach new list of controllers to our peer */
12013 mPeer->mStorageControllers.attach(newList);
12014 }
12015 else
12016 {
12017 /* we have no peer (our parent is the newly created machine);
12018 * just commit changes to devices */
12019 commitStorageControllers = true;
12020 }
12021 }
12022 else
12023 {
12024 /* the list of controllers itself is not changed,
12025 * just commit changes to controllers themselves */
12026 commitStorageControllers = true;
12027 }
12028
12029 if (commitStorageControllers)
12030 {
12031 for (StorageControllerList::const_iterator
12032 it = mStorageControllers->begin();
12033 it != mStorageControllers->end();
12034 ++it)
12035 {
12036 (*it)->i_commit();
12037 }
12038 }
12039
12040 bool commitUSBControllers = false;
12041
12042 if (mUSBControllers.isBackedUp())
12043 {
12044 mUSBControllers.commit();
12045
12046 if (mPeer)
12047 {
12048 /* Commit all changes to new controllers (this will reshare data with
12049 * peers for those who have peers) */
12050 USBControllerList *newList = new USBControllerList();
12051 for (USBControllerList::const_iterator
12052 it = mUSBControllers->begin();
12053 it != mUSBControllers->end();
12054 ++it)
12055 {
12056 (*it)->i_commit();
12057
12058 /* look if this controller has a peer device */
12059 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12060 if (!peer)
12061 {
12062 /* no peer means the device is a newly created one;
12063 * create a peer owning data this device share it with */
12064 peer.createObject();
12065 peer->init(mPeer, *it, true /* aReshare */);
12066 }
12067 else
12068 {
12069 /* remove peer from the old list */
12070 mPeer->mUSBControllers->remove(peer);
12071 }
12072 /* and add it to the new list */
12073 newList->push_back(peer);
12074 }
12075
12076 /* uninit old peer's controllers that are left */
12077 for (USBControllerList::const_iterator
12078 it = mPeer->mUSBControllers->begin();
12079 it != mPeer->mUSBControllers->end();
12080 ++it)
12081 {
12082 (*it)->uninit();
12083 }
12084
12085 /* attach new list of controllers to our peer */
12086 mPeer->mUSBControllers.attach(newList);
12087 }
12088 else
12089 {
12090 /* we have no peer (our parent is the newly created machine);
12091 * just commit changes to devices */
12092 commitUSBControllers = true;
12093 }
12094 }
12095 else
12096 {
12097 /* the list of controllers itself is not changed,
12098 * just commit changes to controllers themselves */
12099 commitUSBControllers = true;
12100 }
12101
12102 if (commitUSBControllers)
12103 {
12104 for (USBControllerList::const_iterator
12105 it = mUSBControllers->begin();
12106 it != mUSBControllers->end();
12107 ++it)
12108 {
12109 (*it)->i_commit();
12110 }
12111 }
12112
12113 if (i_isSessionMachine())
12114 {
12115 /* attach new data to the primary machine and reshare it */
12116 mPeer->mUserData.attach(mUserData);
12117 mPeer->mHWData.attach(mHWData);
12118 /* mmMediumAttachments is reshared by fixupMedia */
12119 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12120 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12121 }
12122}
12123
12124/**
12125 * Copies all the hardware data from the given machine.
12126 *
12127 * Currently, only called when the VM is being restored from a snapshot. In
12128 * particular, this implies that the VM is not running during this method's
12129 * call.
12130 *
12131 * @note This method must be called from under this object's lock.
12132 *
12133 * @note This method doesn't call #i_commit(), so all data remains backed up and
12134 * unsaved.
12135 */
12136void Machine::i_copyFrom(Machine *aThat)
12137{
12138 AssertReturnVoid(!i_isSnapshotMachine());
12139 AssertReturnVoid(aThat->i_isSnapshotMachine());
12140
12141 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12142
12143 mHWData.assignCopy(aThat->mHWData);
12144
12145 // create copies of all shared folders (mHWData after attaching a copy
12146 // contains just references to original objects)
12147 for (HWData::SharedFolderList::iterator
12148 it = mHWData->mSharedFolders.begin();
12149 it != mHWData->mSharedFolders.end();
12150 ++it)
12151 {
12152 ComObjPtr<SharedFolder> folder;
12153 folder.createObject();
12154 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12155 AssertComRC(rc);
12156 *it = folder;
12157 }
12158
12159 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12160 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12161 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12162 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12163 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12164
12165 /* create private copies of all controllers */
12166 mStorageControllers.backup();
12167 mStorageControllers->clear();
12168 for (StorageControllerList::const_iterator
12169 it = aThat->mStorageControllers->begin();
12170 it != aThat->mStorageControllers->end();
12171 ++it)
12172 {
12173 ComObjPtr<StorageController> ctrl;
12174 ctrl.createObject();
12175 ctrl->initCopy(this, *it);
12176 mStorageControllers->push_back(ctrl);
12177 }
12178
12179 /* create private copies of all USB controllers */
12180 mUSBControllers.backup();
12181 mUSBControllers->clear();
12182 for (USBControllerList::const_iterator
12183 it = aThat->mUSBControllers->begin();
12184 it != aThat->mUSBControllers->end();
12185 ++it)
12186 {
12187 ComObjPtr<USBController> ctrl;
12188 ctrl.createObject();
12189 ctrl->initCopy(this, *it);
12190 mUSBControllers->push_back(ctrl);
12191 }
12192
12193 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12194 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12195 {
12196 if (mNetworkAdapters[slot].isNotNull())
12197 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12198 else
12199 {
12200 unconst(mNetworkAdapters[slot]).createObject();
12201 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12202 }
12203 }
12204 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12205 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12206 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12207 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12208}
12209
12210/**
12211 * Returns whether the given storage controller is hotplug capable.
12212 *
12213 * @returns true if the controller supports hotplugging
12214 * false otherwise.
12215 * @param enmCtrlType The controller type to check for.
12216 */
12217bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12218{
12219 ComPtr<ISystemProperties> systemProperties;
12220 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12221 if (FAILED(rc))
12222 return false;
12223
12224 BOOL aHotplugCapable = FALSE;
12225 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12226
12227 return RT_BOOL(aHotplugCapable);
12228}
12229
12230#ifdef VBOX_WITH_RESOURCE_USAGE_API
12231
12232void Machine::i_getDiskList(MediaList &list)
12233{
12234 for (MediumAttachmentList::const_iterator
12235 it = mMediumAttachments->begin();
12236 it != mMediumAttachments->end();
12237 ++it)
12238 {
12239 MediumAttachment *pAttach = *it;
12240 /* just in case */
12241 AssertContinue(pAttach);
12242
12243 AutoCaller localAutoCallerA(pAttach);
12244 if (FAILED(localAutoCallerA.rc())) continue;
12245
12246 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12247
12248 if (pAttach->i_getType() == DeviceType_HardDisk)
12249 list.push_back(pAttach->i_getMedium());
12250 }
12251}
12252
12253void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12254{
12255 AssertReturnVoid(isWriteLockOnCurrentThread());
12256 AssertPtrReturnVoid(aCollector);
12257
12258 pm::CollectorHAL *hal = aCollector->getHAL();
12259 /* Create sub metrics */
12260 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12261 "Percentage of processor time spent in user mode by the VM process.");
12262 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12263 "Percentage of processor time spent in kernel mode by the VM process.");
12264 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12265 "Size of resident portion of VM process in memory.");
12266 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12267 "Actual size of all VM disks combined.");
12268 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12269 "Network receive rate.");
12270 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12271 "Network transmit rate.");
12272 /* Create and register base metrics */
12273 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12274 cpuLoadUser, cpuLoadKernel);
12275 aCollector->registerBaseMetric(cpuLoad);
12276 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12277 ramUsageUsed);
12278 aCollector->registerBaseMetric(ramUsage);
12279 MediaList disks;
12280 i_getDiskList(disks);
12281 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12282 diskUsageUsed);
12283 aCollector->registerBaseMetric(diskUsage);
12284
12285 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12286 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12287 new pm::AggregateAvg()));
12288 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12289 new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12291 new pm::AggregateMax()));
12292 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12293 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12294 new pm::AggregateAvg()));
12295 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12296 new pm::AggregateMin()));
12297 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12298 new pm::AggregateMax()));
12299
12300 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12301 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12302 new pm::AggregateAvg()));
12303 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12304 new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12306 new pm::AggregateMax()));
12307
12308 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12309 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12310 new pm::AggregateAvg()));
12311 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12312 new pm::AggregateMin()));
12313 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12314 new pm::AggregateMax()));
12315
12316
12317 /* Guest metrics collector */
12318 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12319 aCollector->registerGuest(mCollectorGuest);
12320 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12321
12322 /* Create sub metrics */
12323 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12324 "Percentage of processor time spent in user mode as seen by the guest.");
12325 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12326 "Percentage of processor time spent in kernel mode as seen by the guest.");
12327 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12328 "Percentage of processor time spent idling as seen by the guest.");
12329
12330 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12331 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12332 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12333 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12334 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12335 pm::SubMetric *guestMemCache = new pm::SubMetric(
12336 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12337
12338 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12339 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12340
12341 /* Create and register base metrics */
12342 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12343 machineNetRx, machineNetTx);
12344 aCollector->registerBaseMetric(machineNetRate);
12345
12346 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12347 guestLoadUser, guestLoadKernel, guestLoadIdle);
12348 aCollector->registerBaseMetric(guestCpuLoad);
12349
12350 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12351 guestMemTotal, guestMemFree,
12352 guestMemBalloon, guestMemShared,
12353 guestMemCache, guestPagedTotal);
12354 aCollector->registerBaseMetric(guestCpuMem);
12355
12356 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12357 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12358 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12359 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12360
12361 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12362 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12363 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12364 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12365
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12370
12371 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12372 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12375
12376 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12377 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12380
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12385
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12390
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12395
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12400
12401 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12402 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12403 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12404 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12405
12406 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12407 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12408 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12409 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12410}
12411
12412void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12413{
12414 AssertReturnVoid(isWriteLockOnCurrentThread());
12415
12416 if (aCollector)
12417 {
12418 aCollector->unregisterMetricsFor(aMachine);
12419 aCollector->unregisterBaseMetricsFor(aMachine);
12420 }
12421}
12422
12423#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12424
12425
12426////////////////////////////////////////////////////////////////////////////////
12427
12428DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12429
12430HRESULT SessionMachine::FinalConstruct()
12431{
12432 LogFlowThisFunc(("\n"));
12433
12434 mClientToken = NULL;
12435
12436 return BaseFinalConstruct();
12437}
12438
12439void SessionMachine::FinalRelease()
12440{
12441 LogFlowThisFunc(("\n"));
12442
12443 Assert(!mClientToken);
12444 /* paranoia, should not hang around any more */
12445 if (mClientToken)
12446 {
12447 delete mClientToken;
12448 mClientToken = NULL;
12449 }
12450
12451 uninit(Uninit::Unexpected);
12452
12453 BaseFinalRelease();
12454}
12455
12456/**
12457 * @note Must be called only by Machine::LockMachine() from its own write lock.
12458 */
12459HRESULT SessionMachine::init(Machine *aMachine)
12460{
12461 LogFlowThisFuncEnter();
12462 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12463
12464 AssertReturn(aMachine, E_INVALIDARG);
12465
12466 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12467
12468 /* Enclose the state transition NotReady->InInit->Ready */
12469 AutoInitSpan autoInitSpan(this);
12470 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12471
12472 HRESULT rc = S_OK;
12473
12474 RT_ZERO(mAuthLibCtx);
12475
12476 /* create the machine client token */
12477 try
12478 {
12479 mClientToken = new ClientToken(aMachine, this);
12480 if (!mClientToken->isReady())
12481 {
12482 delete mClientToken;
12483 mClientToken = NULL;
12484 rc = E_FAIL;
12485 }
12486 }
12487 catch (std::bad_alloc &)
12488 {
12489 rc = E_OUTOFMEMORY;
12490 }
12491 if (FAILED(rc))
12492 return rc;
12493
12494 /* memorize the peer Machine */
12495 unconst(mPeer) = aMachine;
12496 /* share the parent pointer */
12497 unconst(mParent) = aMachine->mParent;
12498
12499 /* take the pointers to data to share */
12500 mData.share(aMachine->mData);
12501 mSSData.share(aMachine->mSSData);
12502
12503 mUserData.share(aMachine->mUserData);
12504 mHWData.share(aMachine->mHWData);
12505 mMediumAttachments.share(aMachine->mMediumAttachments);
12506
12507 mStorageControllers.allocate();
12508 for (StorageControllerList::const_iterator
12509 it = aMachine->mStorageControllers->begin();
12510 it != aMachine->mStorageControllers->end();
12511 ++it)
12512 {
12513 ComObjPtr<StorageController> ctl;
12514 ctl.createObject();
12515 ctl->init(this, *it);
12516 mStorageControllers->push_back(ctl);
12517 }
12518
12519 mUSBControllers.allocate();
12520 for (USBControllerList::const_iterator
12521 it = aMachine->mUSBControllers->begin();
12522 it != aMachine->mUSBControllers->end();
12523 ++it)
12524 {
12525 ComObjPtr<USBController> ctl;
12526 ctl.createObject();
12527 ctl->init(this, *it);
12528 mUSBControllers->push_back(ctl);
12529 }
12530
12531 unconst(mBIOSSettings).createObject();
12532 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12533 /* create another VRDEServer object that will be mutable */
12534 unconst(mVRDEServer).createObject();
12535 mVRDEServer->init(this, aMachine->mVRDEServer);
12536 /* create another audio adapter object that will be mutable */
12537 unconst(mAudioAdapter).createObject();
12538 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12539 /* create a list of serial ports that will be mutable */
12540 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12541 {
12542 unconst(mSerialPorts[slot]).createObject();
12543 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12544 }
12545 /* create a list of parallel ports that will be mutable */
12546 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12547 {
12548 unconst(mParallelPorts[slot]).createObject();
12549 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12550 }
12551
12552 /* create another USB device filters object that will be mutable */
12553 unconst(mUSBDeviceFilters).createObject();
12554 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12555
12556 /* create a list of network adapters that will be mutable */
12557 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12558 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12559 {
12560 unconst(mNetworkAdapters[slot]).createObject();
12561 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12562 }
12563
12564 /* create another bandwidth control object that will be mutable */
12565 unconst(mBandwidthControl).createObject();
12566 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12567
12568 /* default is to delete saved state on Saved -> PoweredOff transition */
12569 mRemoveSavedState = true;
12570
12571 /* Confirm a successful initialization when it's the case */
12572 autoInitSpan.setSucceeded();
12573
12574 miNATNetworksStarted = 0;
12575
12576 LogFlowThisFuncLeave();
12577 return rc;
12578}
12579
12580/**
12581 * Uninitializes this session object. If the reason is other than
12582 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12583 * or the client watcher code.
12584 *
12585 * @param aReason uninitialization reason
12586 *
12587 * @note Locks mParent + this object for writing.
12588 */
12589void SessionMachine::uninit(Uninit::Reason aReason)
12590{
12591 LogFlowThisFuncEnter();
12592 LogFlowThisFunc(("reason=%d\n", aReason));
12593
12594 /*
12595 * Strongly reference ourselves to prevent this object deletion after
12596 * mData->mSession.mMachine.setNull() below (which can release the last
12597 * reference and call the destructor). Important: this must be done before
12598 * accessing any members (and before AutoUninitSpan that does it as well).
12599 * This self reference will be released as the very last step on return.
12600 */
12601 ComObjPtr<SessionMachine> selfRef;
12602 if (aReason != Uninit::Unexpected)
12603 selfRef = this;
12604
12605 /* Enclose the state transition Ready->InUninit->NotReady */
12606 AutoUninitSpan autoUninitSpan(this);
12607 if (autoUninitSpan.uninitDone())
12608 {
12609 LogFlowThisFunc(("Already uninitialized\n"));
12610 LogFlowThisFuncLeave();
12611 return;
12612 }
12613
12614 if (autoUninitSpan.initFailed())
12615 {
12616 /* We've been called by init() because it's failed. It's not really
12617 * necessary (nor it's safe) to perform the regular uninit sequence
12618 * below, the following is enough.
12619 */
12620 LogFlowThisFunc(("Initialization failed.\n"));
12621 /* destroy the machine client token */
12622 if (mClientToken)
12623 {
12624 delete mClientToken;
12625 mClientToken = NULL;
12626 }
12627 uninitDataAndChildObjects();
12628 mData.free();
12629 unconst(mParent) = NULL;
12630 unconst(mPeer) = NULL;
12631 LogFlowThisFuncLeave();
12632 return;
12633 }
12634
12635 MachineState_T lastState;
12636 {
12637 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12638 lastState = mData->mMachineState;
12639 }
12640 NOREF(lastState);
12641
12642#ifdef VBOX_WITH_USB
12643 // release all captured USB devices, but do this before requesting the locks below
12644 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12645 {
12646 /* Console::captureUSBDevices() is called in the VM process only after
12647 * setting the machine state to Starting or Restoring.
12648 * Console::detachAllUSBDevices() will be called upon successful
12649 * termination. So, we need to release USB devices only if there was
12650 * an abnormal termination of a running VM.
12651 *
12652 * This is identical to SessionMachine::DetachAllUSBDevices except
12653 * for the aAbnormal argument. */
12654 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12655 AssertComRC(rc);
12656 NOREF(rc);
12657
12658 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12659 if (service)
12660 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12661 }
12662#endif /* VBOX_WITH_USB */
12663
12664 // we need to lock this object in uninit() because the lock is shared
12665 // with mPeer (as well as data we modify below). mParent lock is needed
12666 // by several calls to it.
12667 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12668
12669#ifdef VBOX_WITH_RESOURCE_USAGE_API
12670 /*
12671 * It is safe to call Machine::i_unregisterMetrics() here because
12672 * PerformanceCollector::samplerCallback no longer accesses guest methods
12673 * holding the lock.
12674 */
12675 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12676 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12677 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12678 if (mCollectorGuest)
12679 {
12680 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12681 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12682 mCollectorGuest = NULL;
12683 }
12684#endif
12685
12686 if (aReason == Uninit::Abnormal)
12687 {
12688 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12689
12690 /* reset the state to Aborted */
12691 if (mData->mMachineState != MachineState_Aborted)
12692 i_setMachineState(MachineState_Aborted);
12693 }
12694
12695 // any machine settings modified?
12696 if (mData->flModifications)
12697 {
12698 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12699 i_rollback(false /* aNotify */);
12700 }
12701
12702 mData->mSession.mPID = NIL_RTPROCESS;
12703
12704 if (aReason == Uninit::Unexpected)
12705 {
12706 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12707 * client watcher thread to update the set of machines that have open
12708 * sessions. */
12709 mParent->i_updateClientWatcher();
12710 }
12711
12712 /* uninitialize all remote controls */
12713 if (mData->mSession.mRemoteControls.size())
12714 {
12715 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12716 mData->mSession.mRemoteControls.size()));
12717
12718 /* Always restart a the beginning, since the iterator is invalidated
12719 * by using erase(). */
12720 for (Data::Session::RemoteControlList::iterator
12721 it = mData->mSession.mRemoteControls.begin();
12722 it != mData->mSession.mRemoteControls.end();
12723 it = mData->mSession.mRemoteControls.begin())
12724 {
12725 ComPtr<IInternalSessionControl> pControl = *it;
12726 mData->mSession.mRemoteControls.erase(it);
12727 multilock.release();
12728 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12729 HRESULT rc = pControl->Uninitialize();
12730 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12731 if (FAILED(rc))
12732 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12733 multilock.acquire();
12734 }
12735 mData->mSession.mRemoteControls.clear();
12736 }
12737
12738 /* Remove all references to the NAT network service. The service will stop
12739 * if all references (also from other VMs) are removed. */
12740 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12741 {
12742 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12743 {
12744 BOOL enabled;
12745 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12746 if ( FAILED(hrc)
12747 || !enabled)
12748 continue;
12749
12750 NetworkAttachmentType_T type;
12751 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12752 if ( SUCCEEDED(hrc)
12753 && type == NetworkAttachmentType_NATNetwork)
12754 {
12755 Bstr name;
12756 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12757 if (SUCCEEDED(hrc))
12758 {
12759 multilock.release();
12760 Utf8Str strName(name);
12761 LogRel(("VM '%s' stops using NAT network '%s'\n",
12762 mUserData->s.strName.c_str(), strName.c_str()));
12763 mParent->i_natNetworkRefDec(strName);
12764 multilock.acquire();
12765 }
12766 }
12767 }
12768 }
12769
12770 /*
12771 * An expected uninitialization can come only from #i_checkForDeath().
12772 * Otherwise it means that something's gone really wrong (for example,
12773 * the Session implementation has released the VirtualBox reference
12774 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12775 * etc). However, it's also possible, that the client releases the IPC
12776 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12777 * but the VirtualBox release event comes first to the server process.
12778 * This case is practically possible, so we should not assert on an
12779 * unexpected uninit, just log a warning.
12780 */
12781
12782 if (aReason == Uninit::Unexpected)
12783 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12784
12785 if (aReason != Uninit::Normal)
12786 {
12787 mData->mSession.mDirectControl.setNull();
12788 }
12789 else
12790 {
12791 /* this must be null here (see #OnSessionEnd()) */
12792 Assert(mData->mSession.mDirectControl.isNull());
12793 Assert(mData->mSession.mState == SessionState_Unlocking);
12794 Assert(!mData->mSession.mProgress.isNull());
12795 }
12796 if (mData->mSession.mProgress)
12797 {
12798 if (aReason == Uninit::Normal)
12799 mData->mSession.mProgress->i_notifyComplete(S_OK);
12800 else
12801 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12802 COM_IIDOF(ISession),
12803 getComponentName(),
12804 tr("The VM session was aborted"));
12805 mData->mSession.mProgress.setNull();
12806 }
12807
12808 if (mConsoleTaskData.mProgress)
12809 {
12810 Assert(aReason == Uninit::Abnormal);
12811 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12812 COM_IIDOF(ISession),
12813 getComponentName(),
12814 tr("The VM session was aborted"));
12815 mConsoleTaskData.mProgress.setNull();
12816 }
12817
12818 /* remove the association between the peer machine and this session machine */
12819 Assert( (SessionMachine*)mData->mSession.mMachine == this
12820 || aReason == Uninit::Unexpected);
12821
12822 /* reset the rest of session data */
12823 mData->mSession.mLockType = LockType_Null;
12824 mData->mSession.mMachine.setNull();
12825 mData->mSession.mState = SessionState_Unlocked;
12826 mData->mSession.mName.setNull();
12827
12828 /* destroy the machine client token before leaving the exclusive lock */
12829 if (mClientToken)
12830 {
12831 delete mClientToken;
12832 mClientToken = NULL;
12833 }
12834
12835 /* fire an event */
12836 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12837
12838 uninitDataAndChildObjects();
12839
12840 /* free the essential data structure last */
12841 mData.free();
12842
12843 /* release the exclusive lock before setting the below two to NULL */
12844 multilock.release();
12845
12846 unconst(mParent) = NULL;
12847 unconst(mPeer) = NULL;
12848
12849 AuthLibUnload(&mAuthLibCtx);
12850
12851 LogFlowThisFuncLeave();
12852}
12853
12854// util::Lockable interface
12855////////////////////////////////////////////////////////////////////////////////
12856
12857/**
12858 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12859 * with the primary Machine instance (mPeer).
12860 */
12861RWLockHandle *SessionMachine::lockHandle() const
12862{
12863 AssertReturn(mPeer != NULL, NULL);
12864 return mPeer->lockHandle();
12865}
12866
12867// IInternalMachineControl methods
12868////////////////////////////////////////////////////////////////////////////////
12869
12870/**
12871 * Passes collected guest statistics to performance collector object
12872 */
12873HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12874 ULONG aCpuKernel, ULONG aCpuIdle,
12875 ULONG aMemTotal, ULONG aMemFree,
12876 ULONG aMemBalloon, ULONG aMemShared,
12877 ULONG aMemCache, ULONG aPageTotal,
12878 ULONG aAllocVMM, ULONG aFreeVMM,
12879 ULONG aBalloonedVMM, ULONG aSharedVMM,
12880 ULONG aVmNetRx, ULONG aVmNetTx)
12881{
12882#ifdef VBOX_WITH_RESOURCE_USAGE_API
12883 if (mCollectorGuest)
12884 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12885 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12886 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12887 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12888
12889 return S_OK;
12890#else
12891 NOREF(aValidStats);
12892 NOREF(aCpuUser);
12893 NOREF(aCpuKernel);
12894 NOREF(aCpuIdle);
12895 NOREF(aMemTotal);
12896 NOREF(aMemFree);
12897 NOREF(aMemBalloon);
12898 NOREF(aMemShared);
12899 NOREF(aMemCache);
12900 NOREF(aPageTotal);
12901 NOREF(aAllocVMM);
12902 NOREF(aFreeVMM);
12903 NOREF(aBalloonedVMM);
12904 NOREF(aSharedVMM);
12905 NOREF(aVmNetRx);
12906 NOREF(aVmNetTx);
12907 return E_NOTIMPL;
12908#endif
12909}
12910
12911////////////////////////////////////////////////////////////////////////////////
12912//
12913// SessionMachine task records
12914//
12915////////////////////////////////////////////////////////////////////////////////
12916
12917/**
12918 * Task record for saving the machine state.
12919 */
12920class SessionMachine::SaveStateTask
12921 : public Machine::Task
12922{
12923public:
12924 SaveStateTask(SessionMachine *m,
12925 Progress *p,
12926 const Utf8Str &t,
12927 Reason_T enmReason,
12928 const Utf8Str &strStateFilePath)
12929 : Task(m, p, t),
12930 m_enmReason(enmReason),
12931 m_strStateFilePath(strStateFilePath)
12932 {}
12933
12934private:
12935 void handler()
12936 {
12937 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12938 }
12939
12940 Reason_T m_enmReason;
12941 Utf8Str m_strStateFilePath;
12942
12943 friend class SessionMachine;
12944};
12945
12946/**
12947 * Task thread implementation for SessionMachine::SaveState(), called from
12948 * SessionMachine::taskHandler().
12949 *
12950 * @note Locks this object for writing.
12951 *
12952 * @param task
12953 * @return
12954 */
12955void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12956{
12957 LogFlowThisFuncEnter();
12958
12959 AutoCaller autoCaller(this);
12960 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12961 if (FAILED(autoCaller.rc()))
12962 {
12963 /* we might have been uninitialized because the session was accidentally
12964 * closed by the client, so don't assert */
12965 HRESULT rc = setError(E_FAIL,
12966 tr("The session has been accidentally closed"));
12967 task.m_pProgress->i_notifyComplete(rc);
12968 LogFlowThisFuncLeave();
12969 return;
12970 }
12971
12972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12973
12974 HRESULT rc = S_OK;
12975
12976 try
12977 {
12978 ComPtr<IInternalSessionControl> directControl;
12979 if (mData->mSession.mLockType == LockType_VM)
12980 directControl = mData->mSession.mDirectControl;
12981 if (directControl.isNull())
12982 throw setError(VBOX_E_INVALID_VM_STATE,
12983 tr("Trying to save state without a running VM"));
12984 alock.release();
12985 BOOL fSuspendedBySave;
12986 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12987 Assert(!fSuspendedBySave);
12988 alock.acquire();
12989
12990 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12991 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12992 throw E_FAIL);
12993
12994 if (SUCCEEDED(rc))
12995 {
12996 mSSData->strStateFilePath = task.m_strStateFilePath;
12997
12998 /* save all VM settings */
12999 rc = i_saveSettings(NULL);
13000 // no need to check whether VirtualBox.xml needs saving also since
13001 // we can't have a name change pending at this point
13002 }
13003 else
13004 {
13005 // On failure, set the state to the state we had at the beginning.
13006 i_setMachineState(task.m_machineStateBackup);
13007 i_updateMachineStateOnClient();
13008
13009 // Delete the saved state file (might have been already created).
13010 // No need to check whether this is shared with a snapshot here
13011 // because we certainly created a fresh saved state file here.
13012 RTFileDelete(task.m_strStateFilePath.c_str());
13013 }
13014 }
13015 catch (HRESULT aRC) { rc = aRC; }
13016
13017 task.m_pProgress->i_notifyComplete(rc);
13018
13019 LogFlowThisFuncLeave();
13020}
13021
13022/**
13023 * @note Locks this object for writing.
13024 */
13025HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13026{
13027 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13028}
13029
13030HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13031{
13032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13033
13034 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13035 if (FAILED(rc)) return rc;
13036
13037 if ( mData->mMachineState != MachineState_Running
13038 && mData->mMachineState != MachineState_Paused
13039 )
13040 return setError(VBOX_E_INVALID_VM_STATE,
13041 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13042 Global::stringifyMachineState(mData->mMachineState));
13043
13044 ComObjPtr<Progress> pProgress;
13045 pProgress.createObject();
13046 rc = pProgress->init(i_getVirtualBox(),
13047 static_cast<IMachine *>(this) /* aInitiator */,
13048 tr("Saving the execution state of the virtual machine"),
13049 FALSE /* aCancelable */);
13050 if (FAILED(rc))
13051 return rc;
13052
13053 Utf8Str strStateFilePath;
13054 i_composeSavedStateFilename(strStateFilePath);
13055
13056 /* create and start the task on a separate thread (note that it will not
13057 * start working until we release alock) */
13058 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13059 rc = pTask->createThread();
13060 if (FAILED(rc))
13061 return rc;
13062
13063 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13064 i_setMachineState(MachineState_Saving);
13065 i_updateMachineStateOnClient();
13066
13067 pProgress.queryInterfaceTo(aProgress.asOutParam());
13068
13069 return S_OK;
13070}
13071
13072/**
13073 * @note Locks this object for writing.
13074 */
13075HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13076{
13077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13078
13079 HRESULT rc = i_checkStateDependency(MutableStateDep);
13080 if (FAILED(rc)) return rc;
13081
13082 if ( mData->mMachineState != MachineState_PoweredOff
13083 && mData->mMachineState != MachineState_Teleported
13084 && mData->mMachineState != MachineState_Aborted
13085 )
13086 return setError(VBOX_E_INVALID_VM_STATE,
13087 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13088 Global::stringifyMachineState(mData->mMachineState));
13089
13090 com::Utf8Str stateFilePathFull;
13091 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13092 if (RT_FAILURE(vrc))
13093 return setError(VBOX_E_FILE_ERROR,
13094 tr("Invalid saved state file path '%s' (%Rrc)"),
13095 aSavedStateFile.c_str(),
13096 vrc);
13097
13098 mSSData->strStateFilePath = stateFilePathFull;
13099
13100 /* The below i_setMachineState() will detect the state transition and will
13101 * update the settings file */
13102
13103 return i_setMachineState(MachineState_Saved);
13104}
13105
13106/**
13107 * @note Locks this object for writing.
13108 */
13109HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13110{
13111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13112
13113 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13114 if (FAILED(rc)) return rc;
13115
13116 if (mData->mMachineState != MachineState_Saved)
13117 return setError(VBOX_E_INVALID_VM_STATE,
13118 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13119 Global::stringifyMachineState(mData->mMachineState));
13120
13121 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13122
13123 /*
13124 * Saved -> PoweredOff transition will be detected in the SessionMachine
13125 * and properly handled.
13126 */
13127 rc = i_setMachineState(MachineState_PoweredOff);
13128 return rc;
13129}
13130
13131
13132/**
13133 * @note Locks the same as #i_setMachineState() does.
13134 */
13135HRESULT SessionMachine::updateState(MachineState_T aState)
13136{
13137 return i_setMachineState(aState);
13138}
13139
13140/**
13141 * @note Locks this object for writing.
13142 */
13143HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13144{
13145 IProgress *pProgress(aProgress);
13146
13147 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13148
13149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13150
13151 if (mData->mSession.mState != SessionState_Locked)
13152 return VBOX_E_INVALID_OBJECT_STATE;
13153
13154 if (!mData->mSession.mProgress.isNull())
13155 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13156
13157 /* If we didn't reference the NAT network service yet, add a reference to
13158 * force a start */
13159 if (miNATNetworksStarted < 1)
13160 {
13161 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13162 {
13163 BOOL enabled;
13164 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13165 if ( FAILED(hrc)
13166 || !enabled)
13167 continue;
13168
13169 NetworkAttachmentType_T type;
13170 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13171 if ( SUCCEEDED(hrc)
13172 && type == NetworkAttachmentType_NATNetwork)
13173 {
13174 Bstr name;
13175 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13176 if (SUCCEEDED(hrc))
13177 {
13178 Utf8Str strName(name);
13179 LogRel(("VM '%s' starts using NAT network '%s'\n",
13180 mUserData->s.strName.c_str(), strName.c_str()));
13181 mPeer->lockHandle()->unlockWrite();
13182 mParent->i_natNetworkRefInc(strName);
13183#ifdef RT_LOCK_STRICT
13184 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13185#else
13186 mPeer->lockHandle()->lockWrite();
13187#endif
13188 }
13189 }
13190 }
13191 miNATNetworksStarted++;
13192 }
13193
13194 LogFlowThisFunc(("returns S_OK.\n"));
13195 return S_OK;
13196}
13197
13198/**
13199 * @note Locks this object for writing.
13200 */
13201HRESULT SessionMachine::endPowerUp(LONG aResult)
13202{
13203 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13204
13205 if (mData->mSession.mState != SessionState_Locked)
13206 return VBOX_E_INVALID_OBJECT_STATE;
13207
13208 /* Finalize the LaunchVMProcess progress object. */
13209 if (mData->mSession.mProgress)
13210 {
13211 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13212 mData->mSession.mProgress.setNull();
13213 }
13214
13215 if (SUCCEEDED((HRESULT)aResult))
13216 {
13217#ifdef VBOX_WITH_RESOURCE_USAGE_API
13218 /* The VM has been powered up successfully, so it makes sense
13219 * now to offer the performance metrics for a running machine
13220 * object. Doing it earlier wouldn't be safe. */
13221 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13222 mData->mSession.mPID);
13223#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13224 }
13225
13226 return S_OK;
13227}
13228
13229/**
13230 * @note Locks this object for writing.
13231 */
13232HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13233{
13234 LogFlowThisFuncEnter();
13235
13236 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13237
13238 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13239 E_FAIL);
13240
13241 /* create a progress object to track operation completion */
13242 ComObjPtr<Progress> pProgress;
13243 pProgress.createObject();
13244 pProgress->init(i_getVirtualBox(),
13245 static_cast<IMachine *>(this) /* aInitiator */,
13246 tr("Stopping the virtual machine"),
13247 FALSE /* aCancelable */);
13248
13249 /* fill in the console task data */
13250 mConsoleTaskData.mLastState = mData->mMachineState;
13251 mConsoleTaskData.mProgress = pProgress;
13252
13253 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13254 i_setMachineState(MachineState_Stopping);
13255
13256 pProgress.queryInterfaceTo(aProgress.asOutParam());
13257
13258 return S_OK;
13259}
13260
13261/**
13262 * @note Locks this object for writing.
13263 */
13264HRESULT SessionMachine::endPoweringDown(LONG aResult,
13265 const com::Utf8Str &aErrMsg)
13266{
13267 LogFlowThisFuncEnter();
13268
13269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13270
13271 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13272 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13273 && mConsoleTaskData.mLastState != MachineState_Null,
13274 E_FAIL);
13275
13276 /*
13277 * On failure, set the state to the state we had when BeginPoweringDown()
13278 * was called (this is expected by Console::PowerDown() and the associated
13279 * task). On success the VM process already changed the state to
13280 * MachineState_PoweredOff, so no need to do anything.
13281 */
13282 if (FAILED(aResult))
13283 i_setMachineState(mConsoleTaskData.mLastState);
13284
13285 /* notify the progress object about operation completion */
13286 Assert(mConsoleTaskData.mProgress);
13287 if (SUCCEEDED(aResult))
13288 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13289 else
13290 {
13291 if (aErrMsg.length())
13292 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13293 COM_IIDOF(ISession),
13294 getComponentName(),
13295 aErrMsg.c_str());
13296 else
13297 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13298 }
13299
13300 /* clear out the temporary saved state data */
13301 mConsoleTaskData.mLastState = MachineState_Null;
13302 mConsoleTaskData.mProgress.setNull();
13303
13304 LogFlowThisFuncLeave();
13305 return S_OK;
13306}
13307
13308
13309/**
13310 * Goes through the USB filters of the given machine to see if the given
13311 * device matches any filter or not.
13312 *
13313 * @note Locks the same as USBController::hasMatchingFilter() does.
13314 */
13315HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13316 BOOL *aMatched,
13317 ULONG *aMaskedInterfaces)
13318{
13319 LogFlowThisFunc(("\n"));
13320
13321#ifdef VBOX_WITH_USB
13322 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13323#else
13324 NOREF(aDevice);
13325 NOREF(aMaskedInterfaces);
13326 *aMatched = FALSE;
13327#endif
13328
13329 return S_OK;
13330}
13331
13332/**
13333 * @note Locks the same as Host::captureUSBDevice() does.
13334 */
13335HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13336{
13337 LogFlowThisFunc(("\n"));
13338
13339#ifdef VBOX_WITH_USB
13340 /* if captureDeviceForVM() fails, it must have set extended error info */
13341 clearError();
13342 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13343 if (FAILED(rc)) return rc;
13344
13345 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13346 AssertReturn(service, E_FAIL);
13347 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13348#else
13349 NOREF(aId);
13350 return E_NOTIMPL;
13351#endif
13352}
13353
13354/**
13355 * @note Locks the same as Host::detachUSBDevice() does.
13356 */
13357HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13358 BOOL aDone)
13359{
13360 LogFlowThisFunc(("\n"));
13361
13362#ifdef VBOX_WITH_USB
13363 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13364 AssertReturn(service, E_FAIL);
13365 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13366#else
13367 NOREF(aId);
13368 NOREF(aDone);
13369 return E_NOTIMPL;
13370#endif
13371}
13372
13373/**
13374 * Inserts all machine filters to the USB proxy service and then calls
13375 * Host::autoCaptureUSBDevices().
13376 *
13377 * Called by Console from the VM process upon VM startup.
13378 *
13379 * @note Locks what called methods lock.
13380 */
13381HRESULT SessionMachine::autoCaptureUSBDevices()
13382{
13383 LogFlowThisFunc(("\n"));
13384
13385#ifdef VBOX_WITH_USB
13386 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13387 AssertComRC(rc);
13388 NOREF(rc);
13389
13390 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13391 AssertReturn(service, E_FAIL);
13392 return service->autoCaptureDevicesForVM(this);
13393#else
13394 return S_OK;
13395#endif
13396}
13397
13398/**
13399 * Removes all machine filters from the USB proxy service and then calls
13400 * Host::detachAllUSBDevices().
13401 *
13402 * Called by Console from the VM process upon normal VM termination or by
13403 * SessionMachine::uninit() upon abnormal VM termination (from under the
13404 * Machine/SessionMachine lock).
13405 *
13406 * @note Locks what called methods lock.
13407 */
13408HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13409{
13410 LogFlowThisFunc(("\n"));
13411
13412#ifdef VBOX_WITH_USB
13413 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13414 AssertComRC(rc);
13415 NOREF(rc);
13416
13417 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13418 AssertReturn(service, E_FAIL);
13419 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13420#else
13421 NOREF(aDone);
13422 return S_OK;
13423#endif
13424}
13425
13426/**
13427 * @note Locks this object for writing.
13428 */
13429HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13430 ComPtr<IProgress> &aProgress)
13431{
13432 LogFlowThisFuncEnter();
13433
13434 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13435 /*
13436 * We don't assert below because it might happen that a non-direct session
13437 * informs us it is closed right after we've been uninitialized -- it's ok.
13438 */
13439
13440 /* get IInternalSessionControl interface */
13441 ComPtr<IInternalSessionControl> control(aSession);
13442
13443 ComAssertRet(!control.isNull(), E_INVALIDARG);
13444
13445 /* Creating a Progress object requires the VirtualBox lock, and
13446 * thus locking it here is required by the lock order rules. */
13447 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13448
13449 if (control == mData->mSession.mDirectControl)
13450 {
13451 /* The direct session is being normally closed by the client process
13452 * ----------------------------------------------------------------- */
13453
13454 /* go to the closing state (essential for all open*Session() calls and
13455 * for #i_checkForDeath()) */
13456 Assert(mData->mSession.mState == SessionState_Locked);
13457 mData->mSession.mState = SessionState_Unlocking;
13458
13459 /* set direct control to NULL to release the remote instance */
13460 mData->mSession.mDirectControl.setNull();
13461 LogFlowThisFunc(("Direct control is set to NULL\n"));
13462
13463 if (mData->mSession.mProgress)
13464 {
13465 /* finalize the progress, someone might wait if a frontend
13466 * closes the session before powering on the VM. */
13467 mData->mSession.mProgress->notifyComplete(E_FAIL,
13468 COM_IIDOF(ISession),
13469 getComponentName(),
13470 tr("The VM session was closed before any attempt to power it on"));
13471 mData->mSession.mProgress.setNull();
13472 }
13473
13474 /* Create the progress object the client will use to wait until
13475 * #i_checkForDeath() is called to uninitialize this session object after
13476 * it releases the IPC semaphore.
13477 * Note! Because we're "reusing" mProgress here, this must be a proxy
13478 * object just like for LaunchVMProcess. */
13479 Assert(mData->mSession.mProgress.isNull());
13480 ComObjPtr<ProgressProxy> progress;
13481 progress.createObject();
13482 ComPtr<IUnknown> pPeer(mPeer);
13483 progress->init(mParent, pPeer,
13484 Bstr(tr("Closing session")).raw(),
13485 FALSE /* aCancelable */);
13486 progress.queryInterfaceTo(aProgress.asOutParam());
13487 mData->mSession.mProgress = progress;
13488 }
13489 else
13490 {
13491 /* the remote session is being normally closed */
13492 bool found = false;
13493 for (Data::Session::RemoteControlList::iterator
13494 it = mData->mSession.mRemoteControls.begin();
13495 it != mData->mSession.mRemoteControls.end();
13496 ++it)
13497 {
13498 if (control == *it)
13499 {
13500 found = true;
13501 // This MUST be erase(it), not remove(*it) as the latter
13502 // triggers a very nasty use after free due to the place where
13503 // the value "lives".
13504 mData->mSession.mRemoteControls.erase(it);
13505 break;
13506 }
13507 }
13508 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13509 E_INVALIDARG);
13510 }
13511
13512 /* signal the client watcher thread, because the client is going away */
13513 mParent->i_updateClientWatcher();
13514
13515 LogFlowThisFuncLeave();
13516 return S_OK;
13517}
13518
13519HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13520 std::vector<com::Utf8Str> &aValues,
13521 std::vector<LONG64> &aTimestamps,
13522 std::vector<com::Utf8Str> &aFlags)
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526#ifdef VBOX_WITH_GUEST_PROPS
13527 using namespace guestProp;
13528
13529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13530
13531 size_t cEntries = mHWData->mGuestProperties.size();
13532 aNames.resize(cEntries);
13533 aValues.resize(cEntries);
13534 aTimestamps.resize(cEntries);
13535 aFlags.resize(cEntries);
13536
13537 size_t i = 0;
13538 for (HWData::GuestPropertyMap::const_iterator
13539 it = mHWData->mGuestProperties.begin();
13540 it != mHWData->mGuestProperties.end();
13541 ++it, ++i)
13542 {
13543 char szFlags[MAX_FLAGS_LEN + 1];
13544 aNames[i] = it->first;
13545 aValues[i] = it->second.strValue;
13546 aTimestamps[i] = it->second.mTimestamp;
13547
13548 /* If it is NULL, keep it NULL. */
13549 if (it->second.mFlags)
13550 {
13551 writeFlags(it->second.mFlags, szFlags);
13552 aFlags[i] = szFlags;
13553 }
13554 else
13555 aFlags[i] = "";
13556 }
13557 return S_OK;
13558#else
13559 ReturnComNotImplemented();
13560#endif
13561}
13562
13563HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13564 const com::Utf8Str &aValue,
13565 LONG64 aTimestamp,
13566 const com::Utf8Str &aFlags)
13567{
13568 LogFlowThisFunc(("\n"));
13569
13570#ifdef VBOX_WITH_GUEST_PROPS
13571 using namespace guestProp;
13572
13573 try
13574 {
13575 /*
13576 * Convert input up front.
13577 */
13578 uint32_t fFlags = NILFLAG;
13579 if (aFlags.length())
13580 {
13581 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13582 AssertRCReturn(vrc, E_INVALIDARG);
13583 }
13584
13585 /*
13586 * Now grab the object lock, validate the state and do the update.
13587 */
13588
13589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13590
13591 if (!Global::IsOnline(mData->mMachineState))
13592 {
13593 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13594 VBOX_E_INVALID_VM_STATE);
13595 }
13596
13597 i_setModified(IsModified_MachineData);
13598 mHWData.backup();
13599
13600 bool fDelete = !aValue.length();
13601 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13602 if (it != mHWData->mGuestProperties.end())
13603 {
13604 if (!fDelete)
13605 {
13606 it->second.strValue = aValue;
13607 it->second.mTimestamp = aTimestamp;
13608 it->second.mFlags = fFlags;
13609 }
13610 else
13611 mHWData->mGuestProperties.erase(it);
13612
13613 mData->mGuestPropertiesModified = TRUE;
13614 }
13615 else if (!fDelete)
13616 {
13617 HWData::GuestProperty prop;
13618 prop.strValue = aValue;
13619 prop.mTimestamp = aTimestamp;
13620 prop.mFlags = fFlags;
13621
13622 mHWData->mGuestProperties[aName] = prop;
13623 mData->mGuestPropertiesModified = TRUE;
13624 }
13625
13626 alock.release();
13627
13628 mParent->i_onGuestPropertyChange(mData->mUuid,
13629 Bstr(aName).raw(),
13630 Bstr(aValue).raw(),
13631 Bstr(aFlags).raw());
13632 }
13633 catch (...)
13634 {
13635 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13636 }
13637 return S_OK;
13638#else
13639 ReturnComNotImplemented();
13640#endif
13641}
13642
13643
13644HRESULT SessionMachine::lockMedia()
13645{
13646 AutoMultiWriteLock2 alock(this->lockHandle(),
13647 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13648
13649 AssertReturn( mData->mMachineState == MachineState_Starting
13650 || mData->mMachineState == MachineState_Restoring
13651 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13652
13653 clearError();
13654 alock.release();
13655 return i_lockMedia();
13656}
13657
13658HRESULT SessionMachine::unlockMedia()
13659{
13660 HRESULT hrc = i_unlockMedia();
13661 return hrc;
13662}
13663
13664HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13665 ComPtr<IMediumAttachment> &aNewAttachment)
13666{
13667 // request the host lock first, since might be calling Host methods for getting host drives;
13668 // next, protect the media tree all the while we're in here, as well as our member variables
13669 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13670 this->lockHandle(),
13671 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13672
13673 IMediumAttachment *iAttach = aAttachment;
13674 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13675
13676 Utf8Str ctrlName;
13677 LONG lPort;
13678 LONG lDevice;
13679 bool fTempEject;
13680 {
13681 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13682
13683 /* Need to query the details first, as the IMediumAttachment reference
13684 * might be to the original settings, which we are going to change. */
13685 ctrlName = pAttach->i_getControllerName();
13686 lPort = pAttach->i_getPort();
13687 lDevice = pAttach->i_getDevice();
13688 fTempEject = pAttach->i_getTempEject();
13689 }
13690
13691 if (!fTempEject)
13692 {
13693 /* Remember previously mounted medium. The medium before taking the
13694 * backup is not necessarily the same thing. */
13695 ComObjPtr<Medium> oldmedium;
13696 oldmedium = pAttach->i_getMedium();
13697
13698 i_setModified(IsModified_Storage);
13699 mMediumAttachments.backup();
13700
13701 // The backup operation makes the pAttach reference point to the
13702 // old settings. Re-get the correct reference.
13703 pAttach = i_findAttachment(*mMediumAttachments.data(),
13704 ctrlName,
13705 lPort,
13706 lDevice);
13707
13708 {
13709 AutoCaller autoAttachCaller(this);
13710 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13711
13712 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13713 if (!oldmedium.isNull())
13714 oldmedium->i_removeBackReference(mData->mUuid);
13715
13716 pAttach->i_updateMedium(NULL);
13717 pAttach->i_updateEjected();
13718 }
13719
13720 i_setModified(IsModified_Storage);
13721 }
13722 else
13723 {
13724 {
13725 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13726 pAttach->i_updateEjected();
13727 }
13728 }
13729
13730 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13731
13732 return S_OK;
13733}
13734
13735HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13736 com::Utf8Str &aResult)
13737{
13738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13739
13740 HRESULT hr = S_OK;
13741
13742 if (!mAuthLibCtx.hAuthLibrary)
13743 {
13744 /* Load the external authentication library. */
13745 Bstr authLibrary;
13746 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13747
13748 Utf8Str filename = authLibrary;
13749
13750 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13751 if (RT_FAILURE(rc))
13752 {
13753 hr = setError(E_FAIL,
13754 tr("Could not load the external authentication library '%s' (%Rrc)"),
13755 filename.c_str(), rc);
13756 }
13757 }
13758
13759 /* The auth library might need the machine lock. */
13760 alock.release();
13761
13762 if (FAILED(hr))
13763 return hr;
13764
13765 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13766 {
13767 enum VRDEAuthParams
13768 {
13769 parmUuid = 1,
13770 parmGuestJudgement,
13771 parmUser,
13772 parmPassword,
13773 parmDomain,
13774 parmClientId
13775 };
13776
13777 AuthResult result = AuthResultAccessDenied;
13778
13779 Guid uuid(aAuthParams[parmUuid]);
13780 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13781 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13782
13783 result = AuthLibAuthenticate(&mAuthLibCtx,
13784 uuid.raw(), guestJudgement,
13785 aAuthParams[parmUser].c_str(),
13786 aAuthParams[parmPassword].c_str(),
13787 aAuthParams[parmDomain].c_str(),
13788 u32ClientId);
13789
13790 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13791 size_t cbPassword = aAuthParams[parmPassword].length();
13792 if (cbPassword)
13793 {
13794 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13795 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13796 }
13797
13798 if (result == AuthResultAccessGranted)
13799 aResult = "granted";
13800 else
13801 aResult = "denied";
13802
13803 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13804 aAuthParams[parmUser].c_str(), aResult.c_str()));
13805 }
13806 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13807 {
13808 enum VRDEAuthDisconnectParams
13809 {
13810 parmUuid = 1,
13811 parmClientId
13812 };
13813
13814 Guid uuid(aAuthParams[parmUuid]);
13815 uint32_t u32ClientId = 0;
13816 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13817 }
13818 else
13819 {
13820 hr = E_INVALIDARG;
13821 }
13822
13823 return hr;
13824}
13825
13826// public methods only for internal purposes
13827/////////////////////////////////////////////////////////////////////////////
13828
13829#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13830/**
13831 * Called from the client watcher thread to check for expected or unexpected
13832 * death of the client process that has a direct session to this machine.
13833 *
13834 * On Win32 and on OS/2, this method is called only when we've got the
13835 * mutex (i.e. the client has either died or terminated normally) so it always
13836 * returns @c true (the client is terminated, the session machine is
13837 * uninitialized).
13838 *
13839 * On other platforms, the method returns @c true if the client process has
13840 * terminated normally or abnormally and the session machine was uninitialized,
13841 * and @c false if the client process is still alive.
13842 *
13843 * @note Locks this object for writing.
13844 */
13845bool SessionMachine::i_checkForDeath()
13846{
13847 Uninit::Reason reason;
13848 bool terminated = false;
13849
13850 /* Enclose autoCaller with a block because calling uninit() from under it
13851 * will deadlock. */
13852 {
13853 AutoCaller autoCaller(this);
13854 if (!autoCaller.isOk())
13855 {
13856 /* return true if not ready, to cause the client watcher to exclude
13857 * the corresponding session from watching */
13858 LogFlowThisFunc(("Already uninitialized!\n"));
13859 return true;
13860 }
13861
13862 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13863
13864 /* Determine the reason of death: if the session state is Closing here,
13865 * everything is fine. Otherwise it means that the client did not call
13866 * OnSessionEnd() before it released the IPC semaphore. This may happen
13867 * either because the client process has abnormally terminated, or
13868 * because it simply forgot to call ISession::Close() before exiting. We
13869 * threat the latter also as an abnormal termination (see
13870 * Session::uninit() for details). */
13871 reason = mData->mSession.mState == SessionState_Unlocking ?
13872 Uninit::Normal :
13873 Uninit::Abnormal;
13874
13875 if (mClientToken)
13876 terminated = mClientToken->release();
13877 } /* AutoCaller block */
13878
13879 if (terminated)
13880 uninit(reason);
13881
13882 return terminated;
13883}
13884
13885void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13886{
13887 LogFlowThisFunc(("\n"));
13888
13889 strTokenId.setNull();
13890
13891 AutoCaller autoCaller(this);
13892 AssertComRCReturnVoid(autoCaller.rc());
13893
13894 Assert(mClientToken);
13895 if (mClientToken)
13896 mClientToken->getId(strTokenId);
13897}
13898#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13899IToken *SessionMachine::i_getToken()
13900{
13901 LogFlowThisFunc(("\n"));
13902
13903 AutoCaller autoCaller(this);
13904 AssertComRCReturn(autoCaller.rc(), NULL);
13905
13906 Assert(mClientToken);
13907 if (mClientToken)
13908 return mClientToken->getToken();
13909 else
13910 return NULL;
13911}
13912#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13913
13914Machine::ClientToken *SessionMachine::i_getClientToken()
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), NULL);
13920
13921 return mClientToken;
13922}
13923
13924
13925/**
13926 * @note Locks this object for reading.
13927 */
13928HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13929{
13930 LogFlowThisFunc(("\n"));
13931
13932 AutoCaller autoCaller(this);
13933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13934
13935 ComPtr<IInternalSessionControl> directControl;
13936 {
13937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13938 if (mData->mSession.mLockType == LockType_VM)
13939 directControl = mData->mSession.mDirectControl;
13940 }
13941
13942 /* ignore notifications sent after #OnSessionEnd() is called */
13943 if (!directControl)
13944 return S_OK;
13945
13946 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13947}
13948
13949/**
13950 * @note Locks this object for reading.
13951 */
13952HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13953 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13954 IN_BSTR aGuestIp, LONG aGuestPort)
13955{
13956 LogFlowThisFunc(("\n"));
13957
13958 AutoCaller autoCaller(this);
13959 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13960
13961 ComPtr<IInternalSessionControl> directControl;
13962 {
13963 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13964 if (mData->mSession.mLockType == LockType_VM)
13965 directControl = mData->mSession.mDirectControl;
13966 }
13967
13968 /* ignore notifications sent after #OnSessionEnd() is called */
13969 if (!directControl)
13970 return S_OK;
13971 /*
13972 * instead acting like callback we ask IVirtualBox deliver corresponding event
13973 */
13974
13975 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13976 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13977 return S_OK;
13978}
13979
13980/**
13981 * @note Locks this object for reading.
13982 */
13983HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13984{
13985 LogFlowThisFunc(("\n"));
13986
13987 AutoCaller autoCaller(this);
13988 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13989
13990 ComPtr<IInternalSessionControl> directControl;
13991 {
13992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13993 if (mData->mSession.mLockType == LockType_VM)
13994 directControl = mData->mSession.mDirectControl;
13995 }
13996
13997 /* ignore notifications sent after #OnSessionEnd() is called */
13998 if (!directControl)
13999 return S_OK;
14000
14001 return directControl->OnAudioAdapterChange(audioAdapter);
14002}
14003
14004/**
14005 * @note Locks this object for reading.
14006 */
14007HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14013
14014 ComPtr<IInternalSessionControl> directControl;
14015 {
14016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14017 if (mData->mSession.mLockType == LockType_VM)
14018 directControl = mData->mSession.mDirectControl;
14019 }
14020
14021 /* ignore notifications sent after #OnSessionEnd() is called */
14022 if (!directControl)
14023 return S_OK;
14024
14025 return directControl->OnSerialPortChange(serialPort);
14026}
14027
14028/**
14029 * @note Locks this object for reading.
14030 */
14031HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14032{
14033 LogFlowThisFunc(("\n"));
14034
14035 AutoCaller autoCaller(this);
14036 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14037
14038 ComPtr<IInternalSessionControl> directControl;
14039 {
14040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14041 if (mData->mSession.mLockType == LockType_VM)
14042 directControl = mData->mSession.mDirectControl;
14043 }
14044
14045 /* ignore notifications sent after #OnSessionEnd() is called */
14046 if (!directControl)
14047 return S_OK;
14048
14049 return directControl->OnParallelPortChange(parallelPort);
14050}
14051
14052/**
14053 * @note Locks this object for reading.
14054 */
14055HRESULT SessionMachine::i_onStorageControllerChange()
14056{
14057 LogFlowThisFunc(("\n"));
14058
14059 AutoCaller autoCaller(this);
14060 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14061
14062 ComPtr<IInternalSessionControl> directControl;
14063 {
14064 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14065 if (mData->mSession.mLockType == LockType_VM)
14066 directControl = mData->mSession.mDirectControl;
14067 }
14068
14069 /* ignore notifications sent after #OnSessionEnd() is called */
14070 if (!directControl)
14071 return S_OK;
14072
14073 return directControl->OnStorageControllerChange();
14074}
14075
14076/**
14077 * @note Locks this object for reading.
14078 */
14079HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14080{
14081 LogFlowThisFunc(("\n"));
14082
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14085
14086 ComPtr<IInternalSessionControl> directControl;
14087 {
14088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14089 if (mData->mSession.mLockType == LockType_VM)
14090 directControl = mData->mSession.mDirectControl;
14091 }
14092
14093 /* ignore notifications sent after #OnSessionEnd() is called */
14094 if (!directControl)
14095 return S_OK;
14096
14097 return directControl->OnMediumChange(aAttachment, aForce);
14098}
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnCPUChange(aCPU, aRemove);
14122}
14123
14124HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14125{
14126 LogFlowThisFunc(("\n"));
14127
14128 AutoCaller autoCaller(this);
14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14130
14131 ComPtr<IInternalSessionControl> directControl;
14132 {
14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14134 if (mData->mSession.mLockType == LockType_VM)
14135 directControl = mData->mSession.mDirectControl;
14136 }
14137
14138 /* ignore notifications sent after #OnSessionEnd() is called */
14139 if (!directControl)
14140 return S_OK;
14141
14142 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14143}
14144
14145/**
14146 * @note Locks this object for reading.
14147 */
14148HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 if (mData->mSession.mLockType == LockType_VM)
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnVRDEServerChange(aRestart);
14167}
14168
14169/**
14170 * @note Locks this object for reading.
14171 */
14172HRESULT SessionMachine::i_onVideoCaptureChange()
14173{
14174 LogFlowThisFunc(("\n"));
14175
14176 AutoCaller autoCaller(this);
14177 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14178
14179 ComPtr<IInternalSessionControl> directControl;
14180 {
14181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14182 if (mData->mSession.mLockType == LockType_VM)
14183 directControl = mData->mSession.mDirectControl;
14184 }
14185
14186 /* ignore notifications sent after #OnSessionEnd() is called */
14187 if (!directControl)
14188 return S_OK;
14189
14190 return directControl->OnVideoCaptureChange();
14191}
14192
14193/**
14194 * @note Locks this object for reading.
14195 */
14196HRESULT SessionMachine::i_onUSBControllerChange()
14197{
14198 LogFlowThisFunc(("\n"));
14199
14200 AutoCaller autoCaller(this);
14201 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14202
14203 ComPtr<IInternalSessionControl> directControl;
14204 {
14205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14206 if (mData->mSession.mLockType == LockType_VM)
14207 directControl = mData->mSession.mDirectControl;
14208 }
14209
14210 /* ignore notifications sent after #OnSessionEnd() is called */
14211 if (!directControl)
14212 return S_OK;
14213
14214 return directControl->OnUSBControllerChange();
14215}
14216
14217/**
14218 * @note Locks this object for reading.
14219 */
14220HRESULT SessionMachine::i_onSharedFolderChange()
14221{
14222 LogFlowThisFunc(("\n"));
14223
14224 AutoCaller autoCaller(this);
14225 AssertComRCReturnRC(autoCaller.rc());
14226
14227 ComPtr<IInternalSessionControl> directControl;
14228 {
14229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14230 if (mData->mSession.mLockType == LockType_VM)
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturnRC(autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 if (mData->mSession.mLockType == LockType_VM)
14255 directControl = mData->mSession.mDirectControl;
14256 }
14257
14258 /* ignore notifications sent after #OnSessionEnd() is called */
14259 if (!directControl)
14260 return S_OK;
14261
14262 return directControl->OnClipboardModeChange(aClipboardMode);
14263}
14264
14265/**
14266 * @note Locks this object for reading.
14267 */
14268HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14269{
14270 LogFlowThisFunc(("\n"));
14271
14272 AutoCaller autoCaller(this);
14273 AssertComRCReturnRC(autoCaller.rc());
14274
14275 ComPtr<IInternalSessionControl> directControl;
14276 {
14277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14278 if (mData->mSession.mLockType == LockType_VM)
14279 directControl = mData->mSession.mDirectControl;
14280 }
14281
14282 /* ignore notifications sent after #OnSessionEnd() is called */
14283 if (!directControl)
14284 return S_OK;
14285
14286 return directControl->OnDnDModeChange(aDnDMode);
14287}
14288
14289/**
14290 * @note Locks this object for reading.
14291 */
14292HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14293{
14294 LogFlowThisFunc(("\n"));
14295
14296 AutoCaller autoCaller(this);
14297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14298
14299 ComPtr<IInternalSessionControl> directControl;
14300 {
14301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14302 if (mData->mSession.mLockType == LockType_VM)
14303 directControl = mData->mSession.mDirectControl;
14304 }
14305
14306 /* ignore notifications sent after #OnSessionEnd() is called */
14307 if (!directControl)
14308 return S_OK;
14309
14310 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14311}
14312
14313/**
14314 * @note Locks this object for reading.
14315 */
14316HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14317{
14318 LogFlowThisFunc(("\n"));
14319
14320 AutoCaller autoCaller(this);
14321 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14322
14323 ComPtr<IInternalSessionControl> directControl;
14324 {
14325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14326 if (mData->mSession.mLockType == LockType_VM)
14327 directControl = mData->mSession.mDirectControl;
14328 }
14329
14330 /* ignore notifications sent after #OnSessionEnd() is called */
14331 if (!directControl)
14332 return S_OK;
14333
14334 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14335}
14336
14337/**
14338 * Returns @c true if this machine's USB controller reports it has a matching
14339 * filter for the given USB device and @c false otherwise.
14340 *
14341 * @note locks this object for reading.
14342 */
14343bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14344{
14345 AutoCaller autoCaller(this);
14346 /* silently return if not ready -- this method may be called after the
14347 * direct machine session has been called */
14348 if (!autoCaller.isOk())
14349 return false;
14350
14351#ifdef VBOX_WITH_USB
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353
14354 switch (mData->mMachineState)
14355 {
14356 case MachineState_Starting:
14357 case MachineState_Restoring:
14358 case MachineState_TeleportingIn:
14359 case MachineState_Paused:
14360 case MachineState_Running:
14361 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14362 * elsewhere... */
14363 alock.release();
14364 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14365 default: break;
14366 }
14367#else
14368 NOREF(aDevice);
14369 NOREF(aMaskedIfs);
14370#endif
14371 return false;
14372}
14373
14374/**
14375 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14376 */
14377HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14378 IVirtualBoxErrorInfo *aError,
14379 ULONG aMaskedIfs,
14380 const com::Utf8Str &aCaptureFilename)
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385
14386 /* This notification may happen after the machine object has been
14387 * uninitialized (the session was closed), so don't assert. */
14388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14389
14390 ComPtr<IInternalSessionControl> directControl;
14391 {
14392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14393 if (mData->mSession.mLockType == LockType_VM)
14394 directControl = mData->mSession.mDirectControl;
14395 }
14396
14397 /* fail on notifications sent after #OnSessionEnd() is called, it is
14398 * expected by the caller */
14399 if (!directControl)
14400 return E_FAIL;
14401
14402 /* No locks should be held at this point. */
14403 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14404 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14405
14406 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14407}
14408
14409/**
14410 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14411 */
14412HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14413 IVirtualBoxErrorInfo *aError)
14414{
14415 LogFlowThisFunc(("\n"));
14416
14417 AutoCaller autoCaller(this);
14418
14419 /* This notification may happen after the machine object has been
14420 * uninitialized (the session was closed), so don't assert. */
14421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14422
14423 ComPtr<IInternalSessionControl> directControl;
14424 {
14425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14426 if (mData->mSession.mLockType == LockType_VM)
14427 directControl = mData->mSession.mDirectControl;
14428 }
14429
14430 /* fail on notifications sent after #OnSessionEnd() is called, it is
14431 * expected by the caller */
14432 if (!directControl)
14433 return E_FAIL;
14434
14435 /* No locks should be held at this point. */
14436 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14437 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14438
14439 return directControl->OnUSBDeviceDetach(aId, aError);
14440}
14441
14442// protected methods
14443/////////////////////////////////////////////////////////////////////////////
14444
14445/**
14446 * Deletes the given file if it is no longer in use by either the current machine state
14447 * (if the machine is "saved") or any of the machine's snapshots.
14448 *
14449 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14450 * but is different for each SnapshotMachine. When calling this, the order of calling this
14451 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14452 * is therefore critical. I know, it's all rather messy.
14453 *
14454 * @param strStateFile
14455 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14456 * the test for whether the saved state file is in use.
14457 */
14458void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14459 Snapshot *pSnapshotToIgnore)
14460{
14461 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14462 if ( (strStateFile.isNotEmpty())
14463 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14464 )
14465 // ... and it must also not be shared with other snapshots
14466 if ( !mData->mFirstSnapshot
14467 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14468 // this checks the SnapshotMachine's state file paths
14469 )
14470 RTFileDelete(strStateFile.c_str());
14471}
14472
14473/**
14474 * Locks the attached media.
14475 *
14476 * All attached hard disks are locked for writing and DVD/floppy are locked for
14477 * reading. Parents of attached hard disks (if any) are locked for reading.
14478 *
14479 * This method also performs accessibility check of all media it locks: if some
14480 * media is inaccessible, the method will return a failure and a bunch of
14481 * extended error info objects per each inaccessible medium.
14482 *
14483 * Note that this method is atomic: if it returns a success, all media are
14484 * locked as described above; on failure no media is locked at all (all
14485 * succeeded individual locks will be undone).
14486 *
14487 * The caller is responsible for doing the necessary state sanity checks.
14488 *
14489 * The locks made by this method must be undone by calling #unlockMedia() when
14490 * no more needed.
14491 */
14492HRESULT SessionMachine::i_lockMedia()
14493{
14494 AutoCaller autoCaller(this);
14495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14496
14497 AutoMultiWriteLock2 alock(this->lockHandle(),
14498 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14499
14500 /* bail out if trying to lock things with already set up locking */
14501 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14502
14503 MultiResult mrc(S_OK);
14504
14505 /* Collect locking information for all medium objects attached to the VM. */
14506 for (MediumAttachmentList::const_iterator
14507 it = mMediumAttachments->begin();
14508 it != mMediumAttachments->end();
14509 ++it)
14510 {
14511 MediumAttachment *pAtt = *it;
14512 DeviceType_T devType = pAtt->i_getType();
14513 Medium *pMedium = pAtt->i_getMedium();
14514
14515 MediumLockList *pMediumLockList(new MediumLockList());
14516 // There can be attachments without a medium (floppy/dvd), and thus
14517 // it's impossible to create a medium lock list. It still makes sense
14518 // to have the empty medium lock list in the map in case a medium is
14519 // attached later.
14520 if (pMedium != NULL)
14521 {
14522 MediumType_T mediumType = pMedium->i_getType();
14523 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14524 || mediumType == MediumType_Shareable;
14525 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14526
14527 alock.release();
14528 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14529 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14530 false /* fMediumLockWriteAll */,
14531 NULL,
14532 *pMediumLockList);
14533 alock.acquire();
14534 if (FAILED(mrc))
14535 {
14536 delete pMediumLockList;
14537 mData->mSession.mLockedMedia.Clear();
14538 break;
14539 }
14540 }
14541
14542 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14543 if (FAILED(rc))
14544 {
14545 mData->mSession.mLockedMedia.Clear();
14546 mrc = setError(rc,
14547 tr("Collecting locking information for all attached media failed"));
14548 break;
14549 }
14550 }
14551
14552 if (SUCCEEDED(mrc))
14553 {
14554 /* Now lock all media. If this fails, nothing is locked. */
14555 alock.release();
14556 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14557 alock.acquire();
14558 if (FAILED(rc))
14559 {
14560 mrc = setError(rc,
14561 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14562 }
14563 }
14564
14565 return mrc;
14566}
14567
14568/**
14569 * Undoes the locks made by by #lockMedia().
14570 */
14571HRESULT SessionMachine::i_unlockMedia()
14572{
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14575
14576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14577
14578 /* we may be holding important error info on the current thread;
14579 * preserve it */
14580 ErrorInfoKeeper eik;
14581
14582 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14583 AssertComRC(rc);
14584 return rc;
14585}
14586
14587/**
14588 * Helper to change the machine state (reimplementation).
14589 *
14590 * @note Locks this object for writing.
14591 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14592 * it can cause crashes in random places due to unexpectedly committing
14593 * the current settings. The caller is responsible for that. The call
14594 * to saveStateSettings is fine, because this method does not commit.
14595 */
14596HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14597{
14598 LogFlowThisFuncEnter();
14599 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14600
14601 AutoCaller autoCaller(this);
14602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14603
14604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14605
14606 MachineState_T oldMachineState = mData->mMachineState;
14607
14608 AssertMsgReturn(oldMachineState != aMachineState,
14609 ("oldMachineState=%s, aMachineState=%s\n",
14610 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14611 E_FAIL);
14612
14613 HRESULT rc = S_OK;
14614
14615 int stsFlags = 0;
14616 bool deleteSavedState = false;
14617
14618 /* detect some state transitions */
14619
14620 if ( ( oldMachineState == MachineState_Saved
14621 && aMachineState == MachineState_Restoring)
14622 || ( ( oldMachineState == MachineState_PoweredOff
14623 || oldMachineState == MachineState_Teleported
14624 || oldMachineState == MachineState_Aborted
14625 )
14626 && ( aMachineState == MachineState_TeleportingIn
14627 || aMachineState == MachineState_Starting
14628 )
14629 )
14630 )
14631 {
14632 /* The EMT thread is about to start */
14633
14634 /* Nothing to do here for now... */
14635
14636 /// @todo NEWMEDIA don't let mDVDDrive and other children
14637 /// change anything when in the Starting/Restoring state
14638 }
14639 else if ( ( oldMachineState == MachineState_Running
14640 || oldMachineState == MachineState_Paused
14641 || oldMachineState == MachineState_Teleporting
14642 || oldMachineState == MachineState_OnlineSnapshotting
14643 || oldMachineState == MachineState_LiveSnapshotting
14644 || oldMachineState == MachineState_Stuck
14645 || oldMachineState == MachineState_Starting
14646 || oldMachineState == MachineState_Stopping
14647 || oldMachineState == MachineState_Saving
14648 || oldMachineState == MachineState_Restoring
14649 || oldMachineState == MachineState_TeleportingPausedVM
14650 || oldMachineState == MachineState_TeleportingIn
14651 )
14652 && ( aMachineState == MachineState_PoweredOff
14653 || aMachineState == MachineState_Saved
14654 || aMachineState == MachineState_Teleported
14655 || aMachineState == MachineState_Aborted
14656 )
14657 )
14658 {
14659 /* The EMT thread has just stopped, unlock attached media. Note that as
14660 * opposed to locking that is done from Console, we do unlocking here
14661 * because the VM process may have aborted before having a chance to
14662 * properly unlock all media it locked. */
14663
14664 unlockMedia();
14665 }
14666
14667 if (oldMachineState == MachineState_Restoring)
14668 {
14669 if (aMachineState != MachineState_Saved)
14670 {
14671 /*
14672 * delete the saved state file once the machine has finished
14673 * restoring from it (note that Console sets the state from
14674 * Restoring to Saved if the VM couldn't restore successfully,
14675 * to give the user an ability to fix an error and retry --
14676 * we keep the saved state file in this case)
14677 */
14678 deleteSavedState = true;
14679 }
14680 }
14681 else if ( oldMachineState == MachineState_Saved
14682 && ( aMachineState == MachineState_PoweredOff
14683 || aMachineState == MachineState_Aborted
14684 || aMachineState == MachineState_Teleported
14685 )
14686 )
14687 {
14688 /*
14689 * delete the saved state after SessionMachine::ForgetSavedState() is called
14690 * or if the VM process (owning a direct VM session) crashed while the
14691 * VM was Saved
14692 */
14693
14694 /// @todo (dmik)
14695 // Not sure that deleting the saved state file just because of the
14696 // client death before it attempted to restore the VM is a good
14697 // thing. But when it crashes we need to go to the Aborted state
14698 // which cannot have the saved state file associated... The only
14699 // way to fix this is to make the Aborted condition not a VM state
14700 // but a bool flag: i.e., when a crash occurs, set it to true and
14701 // change the state to PoweredOff or Saved depending on the
14702 // saved state presence.
14703
14704 deleteSavedState = true;
14705 mData->mCurrentStateModified = TRUE;
14706 stsFlags |= SaveSTS_CurStateModified;
14707 }
14708
14709 if ( aMachineState == MachineState_Starting
14710 || aMachineState == MachineState_Restoring
14711 || aMachineState == MachineState_TeleportingIn
14712 )
14713 {
14714 /* set the current state modified flag to indicate that the current
14715 * state is no more identical to the state in the
14716 * current snapshot */
14717 if (!mData->mCurrentSnapshot.isNull())
14718 {
14719 mData->mCurrentStateModified = TRUE;
14720 stsFlags |= SaveSTS_CurStateModified;
14721 }
14722 }
14723
14724 if (deleteSavedState)
14725 {
14726 if (mRemoveSavedState)
14727 {
14728 Assert(!mSSData->strStateFilePath.isEmpty());
14729
14730 // it is safe to delete the saved state file if ...
14731 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14732 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14733 // ... none of the snapshots share the saved state file
14734 )
14735 RTFileDelete(mSSData->strStateFilePath.c_str());
14736 }
14737
14738 mSSData->strStateFilePath.setNull();
14739 stsFlags |= SaveSTS_StateFilePath;
14740 }
14741
14742 /* redirect to the underlying peer machine */
14743 mPeer->i_setMachineState(aMachineState);
14744
14745 if ( oldMachineState != MachineState_RestoringSnapshot
14746 && ( aMachineState == MachineState_PoweredOff
14747 || aMachineState == MachineState_Teleported
14748 || aMachineState == MachineState_Aborted
14749 || aMachineState == MachineState_Saved))
14750 {
14751 /* the machine has stopped execution
14752 * (or the saved state file was adopted) */
14753 stsFlags |= SaveSTS_StateTimeStamp;
14754 }
14755
14756 if ( ( oldMachineState == MachineState_PoweredOff
14757 || oldMachineState == MachineState_Aborted
14758 || oldMachineState == MachineState_Teleported
14759 )
14760 && aMachineState == MachineState_Saved)
14761 {
14762 /* the saved state file was adopted */
14763 Assert(!mSSData->strStateFilePath.isEmpty());
14764 stsFlags |= SaveSTS_StateFilePath;
14765 }
14766
14767#ifdef VBOX_WITH_GUEST_PROPS
14768 if ( aMachineState == MachineState_PoweredOff
14769 || aMachineState == MachineState_Aborted
14770 || aMachineState == MachineState_Teleported)
14771 {
14772 /* Make sure any transient guest properties get removed from the
14773 * property store on shutdown. */
14774 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14775
14776 /* remove it from the settings representation */
14777 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14778 for (settings::GuestPropertiesList::iterator
14779 it = llGuestProperties.begin();
14780 it != llGuestProperties.end();
14781 /*nothing*/)
14782 {
14783 const settings::GuestProperty &prop = *it;
14784 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14785 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14786 {
14787 it = llGuestProperties.erase(it);
14788 fNeedsSaving = true;
14789 }
14790 else
14791 {
14792 ++it;
14793 }
14794 }
14795
14796 /* Additionally remove it from the HWData representation. Required to
14797 * keep everything in sync, as this is what the API keeps using. */
14798 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14799 for (HWData::GuestPropertyMap::iterator
14800 it = llHWGuestProperties.begin();
14801 it != llHWGuestProperties.end();
14802 /*nothing*/)
14803 {
14804 uint32_t fFlags = it->second.mFlags;
14805 if ( fFlags & guestProp::TRANSIENT
14806 || fFlags & guestProp::TRANSRESET)
14807 {
14808 /* iterator where we need to continue after the erase call
14809 * (C++03 is a fact still, and it doesn't return the iterator
14810 * which would allow continuing) */
14811 HWData::GuestPropertyMap::iterator it2 = it;
14812 ++it2;
14813 llHWGuestProperties.erase(it);
14814 it = it2;
14815 fNeedsSaving = true;
14816 }
14817 else
14818 {
14819 ++it;
14820 }
14821 }
14822
14823 if (fNeedsSaving)
14824 {
14825 mData->mCurrentStateModified = TRUE;
14826 stsFlags |= SaveSTS_CurStateModified;
14827 }
14828 }
14829#endif /* VBOX_WITH_GUEST_PROPS */
14830
14831 rc = i_saveStateSettings(stsFlags);
14832
14833 if ( ( oldMachineState != MachineState_PoweredOff
14834 && oldMachineState != MachineState_Aborted
14835 && oldMachineState != MachineState_Teleported
14836 )
14837 && ( aMachineState == MachineState_PoweredOff
14838 || aMachineState == MachineState_Aborted
14839 || aMachineState == MachineState_Teleported
14840 )
14841 )
14842 {
14843 /* we've been shut down for any reason */
14844 /* no special action so far */
14845 }
14846
14847 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14848 LogFlowThisFuncLeave();
14849 return rc;
14850}
14851
14852/**
14853 * Sends the current machine state value to the VM process.
14854 *
14855 * @note Locks this object for reading, then calls a client process.
14856 */
14857HRESULT SessionMachine::i_updateMachineStateOnClient()
14858{
14859 AutoCaller autoCaller(this);
14860 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14861
14862 ComPtr<IInternalSessionControl> directControl;
14863 {
14864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14865 AssertReturn(!!mData, E_FAIL);
14866 if (mData->mSession.mLockType == LockType_VM)
14867 directControl = mData->mSession.mDirectControl;
14868
14869 /* directControl may be already set to NULL here in #OnSessionEnd()
14870 * called too early by the direct session process while there is still
14871 * some operation (like deleting the snapshot) in progress. The client
14872 * process in this case is waiting inside Session::close() for the
14873 * "end session" process object to complete, while #uninit() called by
14874 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14875 * operation to complete. For now, we accept this inconsistent behavior
14876 * and simply do nothing here. */
14877
14878 if (mData->mSession.mState == SessionState_Unlocking)
14879 return S_OK;
14880 }
14881
14882 /* ignore notifications sent after #OnSessionEnd() is called */
14883 if (!directControl)
14884 return S_OK;
14885
14886 return directControl->UpdateMachineState(mData->mMachineState);
14887}
14888
14889
14890/*static*/
14891HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14892{
14893 va_list args;
14894 va_start(args, pcszMsg);
14895 HRESULT rc = setErrorInternal(aResultCode,
14896 getStaticClassIID(),
14897 getStaticComponentName(),
14898 Utf8Str(pcszMsg, args),
14899 false /* aWarning */,
14900 true /* aLogIt */);
14901 va_end(args);
14902 return rc;
14903}
14904
14905
14906HRESULT Machine::updateState(MachineState_T aState)
14907{
14908 NOREF(aState);
14909 ReturnComNotImplemented();
14910}
14911
14912HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14913{
14914 NOREF(aProgress);
14915 ReturnComNotImplemented();
14916}
14917
14918HRESULT Machine::endPowerUp(LONG aResult)
14919{
14920 NOREF(aResult);
14921 ReturnComNotImplemented();
14922}
14923
14924HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14925{
14926 NOREF(aProgress);
14927 ReturnComNotImplemented();
14928}
14929
14930HRESULT Machine::endPoweringDown(LONG aResult,
14931 const com::Utf8Str &aErrMsg)
14932{
14933 NOREF(aResult);
14934 NOREF(aErrMsg);
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14939 BOOL *aMatched,
14940 ULONG *aMaskedInterfaces)
14941{
14942 NOREF(aDevice);
14943 NOREF(aMatched);
14944 NOREF(aMaskedInterfaces);
14945 ReturnComNotImplemented();
14946
14947}
14948
14949HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14950{
14951 NOREF(aId); NOREF(aCaptureFilename);
14952 ReturnComNotImplemented();
14953}
14954
14955HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14956 BOOL aDone)
14957{
14958 NOREF(aId);
14959 NOREF(aDone);
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::autoCaptureUSBDevices()
14964{
14965 ReturnComNotImplemented();
14966}
14967
14968HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14969{
14970 NOREF(aDone);
14971 ReturnComNotImplemented();
14972}
14973
14974HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14975 ComPtr<IProgress> &aProgress)
14976{
14977 NOREF(aSession);
14978 NOREF(aProgress);
14979 ReturnComNotImplemented();
14980}
14981
14982HRESULT Machine::finishOnlineMergeMedium()
14983{
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14988 std::vector<com::Utf8Str> &aValues,
14989 std::vector<LONG64> &aTimestamps,
14990 std::vector<com::Utf8Str> &aFlags)
14991{
14992 NOREF(aNames);
14993 NOREF(aValues);
14994 NOREF(aTimestamps);
14995 NOREF(aFlags);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15000 const com::Utf8Str &aValue,
15001 LONG64 aTimestamp,
15002 const com::Utf8Str &aFlags)
15003{
15004 NOREF(aName);
15005 NOREF(aValue);
15006 NOREF(aTimestamp);
15007 NOREF(aFlags);
15008 ReturnComNotImplemented();
15009}
15010
15011HRESULT Machine::lockMedia()
15012{
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::unlockMedia()
15017{
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15022 ComPtr<IMediumAttachment> &aNewAttachment)
15023{
15024 NOREF(aAttachment);
15025 NOREF(aNewAttachment);
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15030 ULONG aCpuUser,
15031 ULONG aCpuKernel,
15032 ULONG aCpuIdle,
15033 ULONG aMemTotal,
15034 ULONG aMemFree,
15035 ULONG aMemBalloon,
15036 ULONG aMemShared,
15037 ULONG aMemCache,
15038 ULONG aPagedTotal,
15039 ULONG aMemAllocTotal,
15040 ULONG aMemFreeTotal,
15041 ULONG aMemBalloonTotal,
15042 ULONG aMemSharedTotal,
15043 ULONG aVmNetRx,
15044 ULONG aVmNetTx)
15045{
15046 NOREF(aValidStats);
15047 NOREF(aCpuUser);
15048 NOREF(aCpuKernel);
15049 NOREF(aCpuIdle);
15050 NOREF(aMemTotal);
15051 NOREF(aMemFree);
15052 NOREF(aMemBalloon);
15053 NOREF(aMemShared);
15054 NOREF(aMemCache);
15055 NOREF(aPagedTotal);
15056 NOREF(aMemAllocTotal);
15057 NOREF(aMemFreeTotal);
15058 NOREF(aMemBalloonTotal);
15059 NOREF(aMemSharedTotal);
15060 NOREF(aVmNetRx);
15061 NOREF(aVmNetTx);
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15066 com::Utf8Str &aResult)
15067{
15068 NOREF(aAuthParams);
15069 NOREF(aResult);
15070 ReturnComNotImplemented();
15071}
15072
15073HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15074{
15075 NOREF(aFlags);
15076 ReturnComNotImplemented();
15077}
15078
15079/* This isn't handled entirely by the wrapper generator yet. */
15080#ifdef VBOX_WITH_XPCOM
15081NS_DECL_CLASSINFO(SessionMachine)
15082NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15083
15084NS_DECL_CLASSINFO(SnapshotMachine)
15085NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15086#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