VirtualBox

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

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

Guest properties: Added a length check for GuestPropWriteFlags() and got rid of strcpy(), as this now also is marked as being deprecated on macOS.

  • 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 70086 2017-12-12 17:36:51Z 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 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5650
5651 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5652 if (it != mHWData->mGuestProperties.end())
5653 {
5654 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5655 aValue = it->second.strValue;
5656 *aTimestamp = it->second.mTimestamp;
5657 GuestPropWriteFlags(it->second.mFlags, szFlags, sizeof(szFlags));
5658 aFlags = Utf8Str(szFlags);
5659 }
5660
5661 return S_OK;
5662}
5663
5664/**
5665 * Query the VM that a guest property belongs to for the property.
5666 * @returns E_ACCESSDENIED if the VM process is not available or not
5667 * currently handling queries and the lookup should then be done in
5668 * VBoxSVC.
5669 */
5670HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5671 com::Utf8Str &aValue,
5672 LONG64 *aTimestamp,
5673 com::Utf8Str &aFlags) const
5674{
5675 HRESULT rc = S_OK;
5676 BSTR bValue = NULL;
5677 BSTR bFlags = NULL;
5678
5679 ComPtr<IInternalSessionControl> directControl;
5680 {
5681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5682 if (mData->mSession.mLockType == LockType_VM)
5683 directControl = mData->mSession.mDirectControl;
5684 }
5685
5686 /* ignore calls made after #OnSessionEnd() is called */
5687 if (!directControl)
5688 rc = E_ACCESSDENIED;
5689 else
5690 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5691 0 /* accessMode */,
5692 &bValue, aTimestamp, &bFlags);
5693
5694 aValue = bValue;
5695 aFlags = bFlags;
5696
5697 return rc;
5698}
5699#endif // VBOX_WITH_GUEST_PROPS
5700
5701HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5702 com::Utf8Str &aValue,
5703 LONG64 *aTimestamp,
5704 com::Utf8Str &aFlags)
5705{
5706#ifndef VBOX_WITH_GUEST_PROPS
5707 ReturnComNotImplemented();
5708#else // VBOX_WITH_GUEST_PROPS
5709
5710 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5711
5712 if (rc == E_ACCESSDENIED)
5713 /* The VM is not running or the service is not (yet) accessible */
5714 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5715 return rc;
5716#endif // VBOX_WITH_GUEST_PROPS
5717}
5718
5719HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5720{
5721 LONG64 dummyTimestamp;
5722 com::Utf8Str dummyFlags;
5723 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5724 return rc;
5725
5726}
5727HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5728{
5729 com::Utf8Str dummyFlags;
5730 com::Utf8Str dummyValue;
5731 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5732 return rc;
5733}
5734
5735#ifdef VBOX_WITH_GUEST_PROPS
5736/**
5737 * Set a guest property in VBoxSVC's internal structures.
5738 */
5739HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5740 const com::Utf8Str &aFlags, bool fDelete)
5741{
5742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5743 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5744 if (FAILED(rc)) return rc;
5745
5746 try
5747 {
5748 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5749 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5750 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5751
5752 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5753 if (it == mHWData->mGuestProperties.end())
5754 {
5755 if (!fDelete)
5756 {
5757 i_setModified(IsModified_MachineData);
5758 mHWData.backupEx();
5759
5760 RTTIMESPEC time;
5761 HWData::GuestProperty prop;
5762 prop.strValue = Bstr(aValue).raw();
5763 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5764 prop.mFlags = fFlags;
5765 mHWData->mGuestProperties[aName] = prop;
5766 }
5767 }
5768 else
5769 {
5770 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5771 {
5772 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5773 }
5774 else
5775 {
5776 i_setModified(IsModified_MachineData);
5777 mHWData.backupEx();
5778
5779 /* The backupEx() operation invalidates our iterator,
5780 * so get a new one. */
5781 it = mHWData->mGuestProperties.find(aName);
5782 Assert(it != mHWData->mGuestProperties.end());
5783
5784 if (!fDelete)
5785 {
5786 RTTIMESPEC time;
5787 it->second.strValue = aValue;
5788 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5789 it->second.mFlags = fFlags;
5790 }
5791 else
5792 mHWData->mGuestProperties.erase(it);
5793 }
5794 }
5795
5796 if (SUCCEEDED(rc))
5797 {
5798 alock.release();
5799
5800 mParent->i_onGuestPropertyChange(mData->mUuid,
5801 Bstr(aName).raw(),
5802 Bstr(aValue).raw(),
5803 Bstr(aFlags).raw());
5804 }
5805 }
5806 catch (std::bad_alloc &)
5807 {
5808 rc = E_OUTOFMEMORY;
5809 }
5810
5811 return rc;
5812}
5813
5814/**
5815 * Set a property on the VM that that property belongs to.
5816 * @returns E_ACCESSDENIED if the VM process is not available or not
5817 * currently handling queries and the setting should then be done in
5818 * VBoxSVC.
5819 */
5820HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5821 const com::Utf8Str &aFlags, bool fDelete)
5822{
5823 HRESULT rc;
5824
5825 try
5826 {
5827 ComPtr<IInternalSessionControl> directControl;
5828 {
5829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5830 if (mData->mSession.mLockType == LockType_VM)
5831 directControl = mData->mSession.mDirectControl;
5832 }
5833
5834 BSTR dummy = NULL; /* will not be changed (setter) */
5835 LONG64 dummy64;
5836 if (!directControl)
5837 rc = E_ACCESSDENIED;
5838 else
5839 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5840 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5841 fDelete? 2: 1 /* accessMode */,
5842 &dummy, &dummy64, &dummy);
5843 }
5844 catch (std::bad_alloc &)
5845 {
5846 rc = E_OUTOFMEMORY;
5847 }
5848
5849 return rc;
5850}
5851#endif // VBOX_WITH_GUEST_PROPS
5852
5853HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5854 const com::Utf8Str &aFlags)
5855{
5856#ifndef VBOX_WITH_GUEST_PROPS
5857 ReturnComNotImplemented();
5858#else // VBOX_WITH_GUEST_PROPS
5859 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5860 if (rc == E_ACCESSDENIED)
5861 /* The VM is not running or the service is not (yet) accessible */
5862 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5863 return rc;
5864#endif // VBOX_WITH_GUEST_PROPS
5865}
5866
5867HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5868{
5869 return setGuestProperty(aProperty, aValue, "");
5870}
5871
5872HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5873{
5874#ifndef VBOX_WITH_GUEST_PROPS
5875 ReturnComNotImplemented();
5876#else // VBOX_WITH_GUEST_PROPS
5877 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5878 if (rc == E_ACCESSDENIED)
5879 /* The VM is not running or the service is not (yet) accessible */
5880 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5881 return rc;
5882#endif // VBOX_WITH_GUEST_PROPS
5883}
5884
5885#ifdef VBOX_WITH_GUEST_PROPS
5886/**
5887 * Enumerate the guest properties in VBoxSVC's internal structures.
5888 */
5889HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5890 std::vector<com::Utf8Str> &aNames,
5891 std::vector<com::Utf8Str> &aValues,
5892 std::vector<LONG64> &aTimestamps,
5893 std::vector<com::Utf8Str> &aFlags)
5894{
5895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5896 Utf8Str strPatterns(aPatterns);
5897
5898 /*
5899 * Look for matching patterns and build up a list.
5900 */
5901 HWData::GuestPropertyMap propMap;
5902 for (HWData::GuestPropertyMap::const_iterator
5903 it = mHWData->mGuestProperties.begin();
5904 it != mHWData->mGuestProperties.end();
5905 ++it)
5906 {
5907 if ( strPatterns.isEmpty()
5908 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5909 RTSTR_MAX,
5910 it->first.c_str(),
5911 RTSTR_MAX,
5912 NULL)
5913 )
5914 propMap.insert(*it);
5915 }
5916
5917 alock.release();
5918
5919 /*
5920 * And build up the arrays for returning the property information.
5921 */
5922 size_t cEntries = propMap.size();
5923
5924 aNames.resize(cEntries);
5925 aValues.resize(cEntries);
5926 aTimestamps.resize(cEntries);
5927 aFlags.resize(cEntries);
5928
5929 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5930 size_t i = 0;
5931 for (HWData::GuestPropertyMap::const_iterator
5932 it = propMap.begin();
5933 it != propMap.end();
5934 ++it, ++i)
5935 {
5936 aNames[i] = it->first;
5937 aValues[i] = it->second.strValue;
5938 aTimestamps[i] = it->second.mTimestamp;
5939 GuestPropWriteFlags(it->second.mFlags, szFlags, sizeof(szFlags));
5940 aFlags[i] = Utf8Str(szFlags);
5941 }
5942
5943 return S_OK;
5944}
5945
5946/**
5947 * Enumerate the properties managed by a VM.
5948 * @returns E_ACCESSDENIED if the VM process is not available or not
5949 * currently handling queries and the setting should then be done in
5950 * VBoxSVC.
5951 */
5952HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5953 std::vector<com::Utf8Str> &aNames,
5954 std::vector<com::Utf8Str> &aValues,
5955 std::vector<LONG64> &aTimestamps,
5956 std::vector<com::Utf8Str> &aFlags)
5957{
5958 HRESULT rc;
5959 ComPtr<IInternalSessionControl> directControl;
5960 {
5961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5962 if (mData->mSession.mLockType == LockType_VM)
5963 directControl = mData->mSession.mDirectControl;
5964 }
5965
5966 com::SafeArray<BSTR> bNames;
5967 com::SafeArray<BSTR> bValues;
5968 com::SafeArray<LONG64> bTimestamps;
5969 com::SafeArray<BSTR> bFlags;
5970
5971 if (!directControl)
5972 rc = E_ACCESSDENIED;
5973 else
5974 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5975 ComSafeArrayAsOutParam(bNames),
5976 ComSafeArrayAsOutParam(bValues),
5977 ComSafeArrayAsOutParam(bTimestamps),
5978 ComSafeArrayAsOutParam(bFlags));
5979 size_t i;
5980 aNames.resize(bNames.size());
5981 for (i = 0; i < bNames.size(); ++i)
5982 aNames[i] = Utf8Str(bNames[i]);
5983 aValues.resize(bValues.size());
5984 for (i = 0; i < bValues.size(); ++i)
5985 aValues[i] = Utf8Str(bValues[i]);
5986 aTimestamps.resize(bTimestamps.size());
5987 for (i = 0; i < bTimestamps.size(); ++i)
5988 aTimestamps[i] = bTimestamps[i];
5989 aFlags.resize(bFlags.size());
5990 for (i = 0; i < bFlags.size(); ++i)
5991 aFlags[i] = Utf8Str(bFlags[i]);
5992
5993 return rc;
5994}
5995#endif // VBOX_WITH_GUEST_PROPS
5996HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5997 std::vector<com::Utf8Str> &aNames,
5998 std::vector<com::Utf8Str> &aValues,
5999 std::vector<LONG64> &aTimestamps,
6000 std::vector<com::Utf8Str> &aFlags)
6001{
6002#ifndef VBOX_WITH_GUEST_PROPS
6003 ReturnComNotImplemented();
6004#else // VBOX_WITH_GUEST_PROPS
6005
6006 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6007
6008 if (rc == E_ACCESSDENIED)
6009 /* The VM is not running or the service is not (yet) accessible */
6010 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6011 return rc;
6012#endif // VBOX_WITH_GUEST_PROPS
6013}
6014
6015HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6016 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6017{
6018 MediumAttachmentList atts;
6019
6020 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6021 if (FAILED(rc)) return rc;
6022
6023 aMediumAttachments.resize(atts.size());
6024 size_t i = 0;
6025 for (MediumAttachmentList::const_iterator
6026 it = atts.begin();
6027 it != atts.end();
6028 ++it, ++i)
6029 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6030
6031 return S_OK;
6032}
6033
6034HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6035 LONG aControllerPort,
6036 LONG aDevice,
6037 ComPtr<IMediumAttachment> &aAttachment)
6038{
6039 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6040 aName.c_str(), aControllerPort, aDevice));
6041
6042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6043
6044 aAttachment = NULL;
6045
6046 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6047 aName,
6048 aControllerPort,
6049 aDevice);
6050 if (pAttach.isNull())
6051 return setError(VBOX_E_OBJECT_NOT_FOUND,
6052 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6053 aDevice, aControllerPort, aName.c_str());
6054
6055 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6056
6057 return S_OK;
6058}
6059
6060
6061HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6062 StorageBus_T aConnectionType,
6063 ComPtr<IStorageController> &aController)
6064{
6065 if ( (aConnectionType <= StorageBus_Null)
6066 || (aConnectionType > StorageBus_PCIe))
6067 return setError(E_INVALIDARG,
6068 tr("Invalid connection type: %d"),
6069 aConnectionType);
6070
6071 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6072
6073 HRESULT rc = i_checkStateDependency(MutableStateDep);
6074 if (FAILED(rc)) return rc;
6075
6076 /* try to find one with the name first. */
6077 ComObjPtr<StorageController> ctrl;
6078
6079 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6080 if (SUCCEEDED(rc))
6081 return setError(VBOX_E_OBJECT_IN_USE,
6082 tr("Storage controller named '%s' already exists"),
6083 aName.c_str());
6084
6085 ctrl.createObject();
6086
6087 /* get a new instance number for the storage controller */
6088 ULONG ulInstance = 0;
6089 bool fBootable = true;
6090 for (StorageControllerList::const_iterator
6091 it = mStorageControllers->begin();
6092 it != mStorageControllers->end();
6093 ++it)
6094 {
6095 if ((*it)->i_getStorageBus() == aConnectionType)
6096 {
6097 ULONG ulCurInst = (*it)->i_getInstance();
6098
6099 if (ulCurInst >= ulInstance)
6100 ulInstance = ulCurInst + 1;
6101
6102 /* Only one controller of each type can be marked as bootable. */
6103 if ((*it)->i_getBootable())
6104 fBootable = false;
6105 }
6106 }
6107
6108 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6109 if (FAILED(rc)) return rc;
6110
6111 i_setModified(IsModified_Storage);
6112 mStorageControllers.backup();
6113 mStorageControllers->push_back(ctrl);
6114
6115 ctrl.queryInterfaceTo(aController.asOutParam());
6116
6117 /* inform the direct session if any */
6118 alock.release();
6119 i_onStorageControllerChange();
6120
6121 return S_OK;
6122}
6123
6124HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6125 ComPtr<IStorageController> &aStorageController)
6126{
6127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6128
6129 ComObjPtr<StorageController> ctrl;
6130
6131 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6132 if (SUCCEEDED(rc))
6133 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6134
6135 return rc;
6136}
6137
6138HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6139 ULONG aInstance,
6140 ComPtr<IStorageController> &aStorageController)
6141{
6142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 for (StorageControllerList::const_iterator
6145 it = mStorageControllers->begin();
6146 it != mStorageControllers->end();
6147 ++it)
6148 {
6149 if ( (*it)->i_getStorageBus() == aConnectionType
6150 && (*it)->i_getInstance() == aInstance)
6151 {
6152 (*it).queryInterfaceTo(aStorageController.asOutParam());
6153 return S_OK;
6154 }
6155 }
6156
6157 return setError(VBOX_E_OBJECT_NOT_FOUND,
6158 tr("Could not find a storage controller with instance number '%lu'"),
6159 aInstance);
6160}
6161
6162HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6163{
6164 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6165
6166 HRESULT rc = i_checkStateDependency(MutableStateDep);
6167 if (FAILED(rc)) return rc;
6168
6169 ComObjPtr<StorageController> ctrl;
6170
6171 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6172 if (SUCCEEDED(rc))
6173 {
6174 /* Ensure that only one controller of each type is marked as bootable. */
6175 if (aBootable == TRUE)
6176 {
6177 for (StorageControllerList::const_iterator
6178 it = mStorageControllers->begin();
6179 it != mStorageControllers->end();
6180 ++it)
6181 {
6182 ComObjPtr<StorageController> aCtrl = (*it);
6183
6184 if ( (aCtrl->i_getName() != aName)
6185 && aCtrl->i_getBootable() == TRUE
6186 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6187 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6188 {
6189 aCtrl->i_setBootable(FALSE);
6190 break;
6191 }
6192 }
6193 }
6194
6195 if (SUCCEEDED(rc))
6196 {
6197 ctrl->i_setBootable(aBootable);
6198 i_setModified(IsModified_Storage);
6199 }
6200 }
6201
6202 if (SUCCEEDED(rc))
6203 {
6204 /* inform the direct session if any */
6205 alock.release();
6206 i_onStorageControllerChange();
6207 }
6208
6209 return rc;
6210}
6211
6212HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6213{
6214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6215
6216 HRESULT rc = i_checkStateDependency(MutableStateDep);
6217 if (FAILED(rc)) return rc;
6218
6219 ComObjPtr<StorageController> ctrl;
6220 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6221 if (FAILED(rc)) return rc;
6222
6223 {
6224 /* find all attached devices to the appropriate storage controller and detach them all */
6225 // make a temporary list because detachDevice invalidates iterators into
6226 // mMediumAttachments
6227 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6228
6229 for (MediumAttachmentList::const_iterator
6230 it = llAttachments2.begin();
6231 it != llAttachments2.end();
6232 ++it)
6233 {
6234 MediumAttachment *pAttachTemp = *it;
6235
6236 AutoCaller localAutoCaller(pAttachTemp);
6237 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6238
6239 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6240
6241 if (pAttachTemp->i_getControllerName() == aName)
6242 {
6243 rc = i_detachDevice(pAttachTemp, alock, NULL);
6244 if (FAILED(rc)) return rc;
6245 }
6246 }
6247 }
6248
6249 /* We can remove it now. */
6250 i_setModified(IsModified_Storage);
6251 mStorageControllers.backup();
6252
6253 ctrl->i_unshare();
6254
6255 mStorageControllers->remove(ctrl);
6256
6257 /* inform the direct session if any */
6258 alock.release();
6259 i_onStorageControllerChange();
6260
6261 return S_OK;
6262}
6263
6264HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6265 ComPtr<IUSBController> &aController)
6266{
6267 if ( (aType <= USBControllerType_Null)
6268 || (aType >= USBControllerType_Last))
6269 return setError(E_INVALIDARG,
6270 tr("Invalid USB controller type: %d"),
6271 aType);
6272
6273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6274
6275 HRESULT rc = i_checkStateDependency(MutableStateDep);
6276 if (FAILED(rc)) return rc;
6277
6278 /* try to find one with the same type first. */
6279 ComObjPtr<USBController> ctrl;
6280
6281 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6282 if (SUCCEEDED(rc))
6283 return setError(VBOX_E_OBJECT_IN_USE,
6284 tr("USB controller named '%s' already exists"),
6285 aName.c_str());
6286
6287 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6288 ULONG maxInstances;
6289 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6290 if (FAILED(rc))
6291 return rc;
6292
6293 ULONG cInstances = i_getUSBControllerCountByType(aType);
6294 if (cInstances >= maxInstances)
6295 return setError(E_INVALIDARG,
6296 tr("Too many USB controllers of this type"));
6297
6298 ctrl.createObject();
6299
6300 rc = ctrl->init(this, aName, aType);
6301 if (FAILED(rc)) return rc;
6302
6303 i_setModified(IsModified_USB);
6304 mUSBControllers.backup();
6305 mUSBControllers->push_back(ctrl);
6306
6307 ctrl.queryInterfaceTo(aController.asOutParam());
6308
6309 /* inform the direct session if any */
6310 alock.release();
6311 i_onUSBControllerChange();
6312
6313 return S_OK;
6314}
6315
6316HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6317{
6318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 ComObjPtr<USBController> ctrl;
6321
6322 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6323 if (SUCCEEDED(rc))
6324 ctrl.queryInterfaceTo(aController.asOutParam());
6325
6326 return rc;
6327}
6328
6329HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6330 ULONG *aControllers)
6331{
6332 if ( (aType <= USBControllerType_Null)
6333 || (aType >= USBControllerType_Last))
6334 return setError(E_INVALIDARG,
6335 tr("Invalid USB controller type: %d"),
6336 aType);
6337
6338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6339
6340 ComObjPtr<USBController> ctrl;
6341
6342 *aControllers = i_getUSBControllerCountByType(aType);
6343
6344 return S_OK;
6345}
6346
6347HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6348{
6349
6350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6351
6352 HRESULT rc = i_checkStateDependency(MutableStateDep);
6353 if (FAILED(rc)) return rc;
6354
6355 ComObjPtr<USBController> ctrl;
6356 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6357 if (FAILED(rc)) return rc;
6358
6359 i_setModified(IsModified_USB);
6360 mUSBControllers.backup();
6361
6362 ctrl->i_unshare();
6363
6364 mUSBControllers->remove(ctrl);
6365
6366 /* inform the direct session if any */
6367 alock.release();
6368 i_onUSBControllerChange();
6369
6370 return S_OK;
6371}
6372
6373HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6374 ULONG *aOriginX,
6375 ULONG *aOriginY,
6376 ULONG *aWidth,
6377 ULONG *aHeight,
6378 BOOL *aEnabled)
6379{
6380 uint32_t u32OriginX= 0;
6381 uint32_t u32OriginY= 0;
6382 uint32_t u32Width = 0;
6383 uint32_t u32Height = 0;
6384 uint16_t u16Flags = 0;
6385
6386 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6387 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6388 if (RT_FAILURE(vrc))
6389 {
6390#ifdef RT_OS_WINDOWS
6391 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6392 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6393 * So just assign fEnable to TRUE again.
6394 * The right fix would be to change GUI API wrappers to make sure that parameters
6395 * are changed only if API succeeds.
6396 */
6397 *aEnabled = TRUE;
6398#endif
6399 return setError(VBOX_E_IPRT_ERROR,
6400 tr("Saved guest size is not available (%Rrc)"),
6401 vrc);
6402 }
6403
6404 *aOriginX = u32OriginX;
6405 *aOriginY = u32OriginY;
6406 *aWidth = u32Width;
6407 *aHeight = u32Height;
6408 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6409
6410 return S_OK;
6411}
6412
6413HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6414 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6415{
6416 if (aScreenId != 0)
6417 return E_NOTIMPL;
6418
6419 if ( aBitmapFormat != BitmapFormat_BGR0
6420 && aBitmapFormat != BitmapFormat_BGRA
6421 && aBitmapFormat != BitmapFormat_RGBA
6422 && aBitmapFormat != BitmapFormat_PNG)
6423 return setError(E_NOTIMPL,
6424 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6425
6426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 uint8_t *pu8Data = NULL;
6429 uint32_t cbData = 0;
6430 uint32_t u32Width = 0;
6431 uint32_t u32Height = 0;
6432
6433 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6434
6435 if (RT_FAILURE(vrc))
6436 return setError(VBOX_E_IPRT_ERROR,
6437 tr("Saved thumbnail data is not available (%Rrc)"),
6438 vrc);
6439
6440 HRESULT hr = S_OK;
6441
6442 *aWidth = u32Width;
6443 *aHeight = u32Height;
6444
6445 if (cbData > 0)
6446 {
6447 /* Convert pixels to the format expected by the API caller. */
6448 if (aBitmapFormat == BitmapFormat_BGR0)
6449 {
6450 /* [0] B, [1] G, [2] R, [3] 0. */
6451 aData.resize(cbData);
6452 memcpy(&aData.front(), pu8Data, cbData);
6453 }
6454 else if (aBitmapFormat == BitmapFormat_BGRA)
6455 {
6456 /* [0] B, [1] G, [2] R, [3] A. */
6457 aData.resize(cbData);
6458 for (uint32_t i = 0; i < cbData; i += 4)
6459 {
6460 aData[i] = pu8Data[i];
6461 aData[i + 1] = pu8Data[i + 1];
6462 aData[i + 2] = pu8Data[i + 2];
6463 aData[i + 3] = 0xff;
6464 }
6465 }
6466 else if (aBitmapFormat == BitmapFormat_RGBA)
6467 {
6468 /* [0] R, [1] G, [2] B, [3] A. */
6469 aData.resize(cbData);
6470 for (uint32_t i = 0; i < cbData; i += 4)
6471 {
6472 aData[i] = pu8Data[i + 2];
6473 aData[i + 1] = pu8Data[i + 1];
6474 aData[i + 2] = pu8Data[i];
6475 aData[i + 3] = 0xff;
6476 }
6477 }
6478 else if (aBitmapFormat == BitmapFormat_PNG)
6479 {
6480 uint8_t *pu8PNG = NULL;
6481 uint32_t cbPNG = 0;
6482 uint32_t cxPNG = 0;
6483 uint32_t cyPNG = 0;
6484
6485 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6486
6487 if (RT_SUCCESS(vrc))
6488 {
6489 aData.resize(cbPNG);
6490 if (cbPNG)
6491 memcpy(&aData.front(), pu8PNG, cbPNG);
6492 }
6493 else
6494 hr = setError(VBOX_E_IPRT_ERROR,
6495 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6496 vrc);
6497
6498 RTMemFree(pu8PNG);
6499 }
6500 }
6501
6502 freeSavedDisplayScreenshot(pu8Data);
6503
6504 return hr;
6505}
6506
6507HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6508 ULONG *aWidth,
6509 ULONG *aHeight,
6510 std::vector<BitmapFormat_T> &aBitmapFormats)
6511{
6512 if (aScreenId != 0)
6513 return E_NOTIMPL;
6514
6515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 uint8_t *pu8Data = NULL;
6518 uint32_t cbData = 0;
6519 uint32_t u32Width = 0;
6520 uint32_t u32Height = 0;
6521
6522 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6523
6524 if (RT_FAILURE(vrc))
6525 return setError(VBOX_E_IPRT_ERROR,
6526 tr("Saved screenshot data is not available (%Rrc)"),
6527 vrc);
6528
6529 *aWidth = u32Width;
6530 *aHeight = u32Height;
6531 aBitmapFormats.resize(1);
6532 aBitmapFormats[0] = BitmapFormat_PNG;
6533
6534 freeSavedDisplayScreenshot(pu8Data);
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6540 BitmapFormat_T aBitmapFormat,
6541 ULONG *aWidth,
6542 ULONG *aHeight,
6543 std::vector<BYTE> &aData)
6544{
6545 if (aScreenId != 0)
6546 return E_NOTIMPL;
6547
6548 if (aBitmapFormat != BitmapFormat_PNG)
6549 return E_NOTIMPL;
6550
6551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6552
6553 uint8_t *pu8Data = NULL;
6554 uint32_t cbData = 0;
6555 uint32_t u32Width = 0;
6556 uint32_t u32Height = 0;
6557
6558 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6559
6560 if (RT_FAILURE(vrc))
6561 return setError(VBOX_E_IPRT_ERROR,
6562 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6563 vrc);
6564
6565 *aWidth = u32Width;
6566 *aHeight = u32Height;
6567
6568 aData.resize(cbData);
6569 if (cbData)
6570 memcpy(&aData.front(), pu8Data, cbData);
6571
6572 freeSavedDisplayScreenshot(pu8Data);
6573
6574 return S_OK;
6575}
6576
6577HRESULT Machine::hotPlugCPU(ULONG aCpu)
6578{
6579 HRESULT rc = S_OK;
6580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6581
6582 if (!mHWData->mCPUHotPlugEnabled)
6583 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6584
6585 if (aCpu >= mHWData->mCPUCount)
6586 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6587
6588 if (mHWData->mCPUAttached[aCpu])
6589 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6590
6591 alock.release();
6592 rc = i_onCPUChange(aCpu, false);
6593 alock.acquire();
6594 if (FAILED(rc)) return rc;
6595
6596 i_setModified(IsModified_MachineData);
6597 mHWData.backup();
6598 mHWData->mCPUAttached[aCpu] = true;
6599
6600 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6601 if (Global::IsOnline(mData->mMachineState))
6602 i_saveSettings(NULL);
6603
6604 return S_OK;
6605}
6606
6607HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6608{
6609 HRESULT rc = S_OK;
6610
6611 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6612
6613 if (!mHWData->mCPUHotPlugEnabled)
6614 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6615
6616 if (aCpu >= SchemaDefs::MaxCPUCount)
6617 return setError(E_INVALIDARG,
6618 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6619 SchemaDefs::MaxCPUCount);
6620
6621 if (!mHWData->mCPUAttached[aCpu])
6622 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6623
6624 /* CPU 0 can't be detached */
6625 if (aCpu == 0)
6626 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6627
6628 alock.release();
6629 rc = i_onCPUChange(aCpu, true);
6630 alock.acquire();
6631 if (FAILED(rc)) return rc;
6632
6633 i_setModified(IsModified_MachineData);
6634 mHWData.backup();
6635 mHWData->mCPUAttached[aCpu] = false;
6636
6637 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6638 if (Global::IsOnline(mData->mMachineState))
6639 i_saveSettings(NULL);
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6645{
6646 *aAttached = false;
6647
6648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6649
6650 /* If hotplug is enabled the CPU is always enabled. */
6651 if (!mHWData->mCPUHotPlugEnabled)
6652 {
6653 if (aCpu < mHWData->mCPUCount)
6654 *aAttached = true;
6655 }
6656 else
6657 {
6658 if (aCpu < SchemaDefs::MaxCPUCount)
6659 *aAttached = mHWData->mCPUAttached[aCpu];
6660 }
6661
6662 return S_OK;
6663}
6664
6665HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6666{
6667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6668
6669 Utf8Str log = i_getLogFilename(aIdx);
6670 if (!RTFileExists(log.c_str()))
6671 log.setNull();
6672 aFilename = log;
6673
6674 return S_OK;
6675}
6676
6677HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6678{
6679 if (aSize < 0)
6680 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6681
6682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 HRESULT rc = S_OK;
6685 Utf8Str log = i_getLogFilename(aIdx);
6686
6687 /* do not unnecessarily hold the lock while doing something which does
6688 * not need the lock and potentially takes a long time. */
6689 alock.release();
6690
6691 /* Limit the chunk size to 32K for now, as that gives better performance
6692 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6693 * One byte expands to approx. 25 bytes of breathtaking XML. */
6694 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6695 aData.resize(cbData);
6696
6697 RTFILE LogFile;
6698 int vrc = RTFileOpen(&LogFile, log.c_str(),
6699 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6700 if (RT_SUCCESS(vrc))
6701 {
6702 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6703 if (RT_SUCCESS(vrc))
6704 aData.resize(cbData);
6705 else
6706 rc = setError(VBOX_E_IPRT_ERROR,
6707 tr("Could not read log file '%s' (%Rrc)"),
6708 log.c_str(), vrc);
6709 RTFileClose(LogFile);
6710 }
6711 else
6712 rc = setError(VBOX_E_IPRT_ERROR,
6713 tr("Could not open log file '%s' (%Rrc)"),
6714 log.c_str(), vrc);
6715
6716 if (FAILED(rc))
6717 aData.resize(0);
6718
6719 return rc;
6720}
6721
6722
6723/**
6724 * Currently this method doesn't attach device to the running VM,
6725 * just makes sure it's plugged on next VM start.
6726 */
6727HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6728{
6729 // lock scope
6730 {
6731 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6732
6733 HRESULT rc = i_checkStateDependency(MutableStateDep);
6734 if (FAILED(rc)) return rc;
6735
6736 ChipsetType_T aChipset = ChipsetType_PIIX3;
6737 COMGETTER(ChipsetType)(&aChipset);
6738
6739 if (aChipset != ChipsetType_ICH9)
6740 {
6741 return setError(E_INVALIDARG,
6742 tr("Host PCI attachment only supported with ICH9 chipset"));
6743 }
6744
6745 // check if device with this host PCI address already attached
6746 for (HWData::PCIDeviceAssignmentList::const_iterator
6747 it = mHWData->mPCIDeviceAssignments.begin();
6748 it != mHWData->mPCIDeviceAssignments.end();
6749 ++it)
6750 {
6751 LONG iHostAddress = -1;
6752 ComPtr<PCIDeviceAttachment> pAttach;
6753 pAttach = *it;
6754 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6755 if (iHostAddress == aHostAddress)
6756 return setError(E_INVALIDARG,
6757 tr("Device with host PCI address already attached to this VM"));
6758 }
6759
6760 ComObjPtr<PCIDeviceAttachment> pda;
6761 char name[32];
6762
6763 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6764 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6765 pda.createObject();
6766 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6767 i_setModified(IsModified_MachineData);
6768 mHWData.backup();
6769 mHWData->mPCIDeviceAssignments.push_back(pda);
6770 }
6771
6772 return S_OK;
6773}
6774
6775/**
6776 * Currently this method doesn't detach device from the running VM,
6777 * just makes sure it's not plugged on next VM start.
6778 */
6779HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6780{
6781 ComObjPtr<PCIDeviceAttachment> pAttach;
6782 bool fRemoved = false;
6783 HRESULT rc;
6784
6785 // lock scope
6786 {
6787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6788
6789 rc = i_checkStateDependency(MutableStateDep);
6790 if (FAILED(rc)) return rc;
6791
6792 for (HWData::PCIDeviceAssignmentList::const_iterator
6793 it = mHWData->mPCIDeviceAssignments.begin();
6794 it != mHWData->mPCIDeviceAssignments.end();
6795 ++it)
6796 {
6797 LONG iHostAddress = -1;
6798 pAttach = *it;
6799 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6800 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6801 {
6802 i_setModified(IsModified_MachineData);
6803 mHWData.backup();
6804 mHWData->mPCIDeviceAssignments.remove(pAttach);
6805 fRemoved = true;
6806 break;
6807 }
6808 }
6809 }
6810
6811
6812 /* Fire event outside of the lock */
6813 if (fRemoved)
6814 {
6815 Assert(!pAttach.isNull());
6816 ComPtr<IEventSource> es;
6817 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6818 Assert(SUCCEEDED(rc));
6819 Bstr mid;
6820 rc = this->COMGETTER(Id)(mid.asOutParam());
6821 Assert(SUCCEEDED(rc));
6822 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6823 }
6824
6825 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6826 tr("No host PCI device %08x attached"),
6827 aHostAddress
6828 );
6829}
6830
6831HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6832{
6833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6834
6835 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6836 size_t i = 0;
6837 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6838 it = mHWData->mPCIDeviceAssignments.begin();
6839 it != mHWData->mPCIDeviceAssignments.end();
6840 ++it, ++i)
6841 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6842
6843 return S_OK;
6844}
6845
6846HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6847{
6848 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6854{
6855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6856
6857 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6858
6859 return S_OK;
6860}
6861
6862HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6863{
6864 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6865 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6866 if (SUCCEEDED(hrc))
6867 {
6868 hrc = mHWData.backupEx();
6869 if (SUCCEEDED(hrc))
6870 {
6871 i_setModified(IsModified_MachineData);
6872 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6873 }
6874 }
6875 return hrc;
6876}
6877
6878HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6879{
6880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6881 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6882 return S_OK;
6883}
6884
6885HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6886{
6887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6888 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6889 if (SUCCEEDED(hrc))
6890 {
6891 hrc = mHWData.backupEx();
6892 if (SUCCEEDED(hrc))
6893 {
6894 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6895 if (SUCCEEDED(hrc))
6896 i_setModified(IsModified_MachineData);
6897 }
6898 }
6899 return hrc;
6900}
6901
6902HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6903{
6904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6905
6906 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6907
6908 return S_OK;
6909}
6910
6911HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6912{
6913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6914 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6915 if (SUCCEEDED(hrc))
6916 {
6917 hrc = mHWData.backupEx();
6918 if (SUCCEEDED(hrc))
6919 {
6920 i_setModified(IsModified_MachineData);
6921 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6922 }
6923 }
6924 return hrc;
6925}
6926
6927HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6928{
6929 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6930
6931 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6932
6933 return S_OK;
6934}
6935
6936HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6937{
6938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6941 if ( SUCCEEDED(hrc)
6942 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6943 {
6944 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6945 int vrc;
6946
6947 if (aAutostartEnabled)
6948 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6949 else
6950 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6951
6952 if (RT_SUCCESS(vrc))
6953 {
6954 hrc = mHWData.backupEx();
6955 if (SUCCEEDED(hrc))
6956 {
6957 i_setModified(IsModified_MachineData);
6958 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6959 }
6960 }
6961 else if (vrc == VERR_NOT_SUPPORTED)
6962 hrc = setError(VBOX_E_NOT_SUPPORTED,
6963 tr("The VM autostart feature is not supported on this platform"));
6964 else if (vrc == VERR_PATH_NOT_FOUND)
6965 hrc = setError(E_FAIL,
6966 tr("The path to the autostart database is not set"));
6967 else
6968 hrc = setError(E_UNEXPECTED,
6969 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6970 aAutostartEnabled ? "Adding" : "Removing",
6971 mUserData->s.strName.c_str(), vrc);
6972 }
6973 return hrc;
6974}
6975
6976HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6977{
6978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6981
6982 return S_OK;
6983}
6984
6985HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6986{
6987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6988 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6989 if (SUCCEEDED(hrc))
6990 {
6991 hrc = mHWData.backupEx();
6992 if (SUCCEEDED(hrc))
6993 {
6994 i_setModified(IsModified_MachineData);
6995 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6996 }
6997 }
6998 return hrc;
6999}
7000
7001HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7002{
7003 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7004
7005 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7006
7007 return S_OK;
7008}
7009
7010HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7011{
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7014 if ( SUCCEEDED(hrc)
7015 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7016 {
7017 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7018 int vrc;
7019
7020 if (aAutostopType != AutostopType_Disabled)
7021 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7022 else
7023 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7024
7025 if (RT_SUCCESS(vrc))
7026 {
7027 hrc = mHWData.backupEx();
7028 if (SUCCEEDED(hrc))
7029 {
7030 i_setModified(IsModified_MachineData);
7031 mHWData->mAutostart.enmAutostopType = aAutostopType;
7032 }
7033 }
7034 else if (vrc == VERR_NOT_SUPPORTED)
7035 hrc = setError(VBOX_E_NOT_SUPPORTED,
7036 tr("The VM autostop feature is not supported on this platform"));
7037 else if (vrc == VERR_PATH_NOT_FOUND)
7038 hrc = setError(E_FAIL,
7039 tr("The path to the autostart database is not set"));
7040 else
7041 hrc = setError(E_UNEXPECTED,
7042 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7043 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7044 mUserData->s.strName.c_str(), vrc);
7045 }
7046 return hrc;
7047}
7048
7049HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7050{
7051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7052
7053 aDefaultFrontend = mHWData->mDefaultFrontend;
7054
7055 return S_OK;
7056}
7057
7058HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7059{
7060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7061 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7062 if (SUCCEEDED(hrc))
7063 {
7064 hrc = mHWData.backupEx();
7065 if (SUCCEEDED(hrc))
7066 {
7067 i_setModified(IsModified_MachineData);
7068 mHWData->mDefaultFrontend = aDefaultFrontend;
7069 }
7070 }
7071 return hrc;
7072}
7073
7074HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7075{
7076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7077 size_t cbIcon = mUserData->s.ovIcon.size();
7078 aIcon.resize(cbIcon);
7079 if (cbIcon)
7080 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7081 return S_OK;
7082}
7083
7084HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7085{
7086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7087 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7088 if (SUCCEEDED(hrc))
7089 {
7090 i_setModified(IsModified_MachineData);
7091 mUserData.backup();
7092 size_t cbIcon = aIcon.size();
7093 mUserData->s.ovIcon.resize(cbIcon);
7094 if (cbIcon)
7095 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7096 }
7097 return hrc;
7098}
7099
7100HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7101{
7102#ifdef VBOX_WITH_USB
7103 *aUSBProxyAvailable = true;
7104#else
7105 *aUSBProxyAvailable = false;
7106#endif
7107 return S_OK;
7108}
7109
7110HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7111{
7112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7113
7114 aVMProcessPriority = mUserData->s.strVMPriority;
7115
7116 return S_OK;
7117}
7118
7119HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7120{
7121 RT_NOREF(aVMProcessPriority);
7122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7123 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7124 if (SUCCEEDED(hrc))
7125 {
7126 /** @todo r=klaus: currently this is marked as not implemented, as
7127 * the code for setting the priority of the process is not there
7128 * (neither when starting the VM nor at runtime). */
7129 ReturnComNotImplemented();
7130#if 0
7131 hrc = mUserData.backupEx();
7132 if (SUCCEEDED(hrc))
7133 {
7134 i_setModified(IsModified_MachineData);
7135 mUserData->s.strVMPriority = aVMProcessPriority;
7136 }
7137#endif
7138 }
7139 return hrc;
7140}
7141
7142HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7143 ComPtr<IProgress> &aProgress)
7144{
7145 ComObjPtr<Progress> pP;
7146 Progress *ppP = pP;
7147 IProgress *iP = static_cast<IProgress *>(ppP);
7148 IProgress **pProgress = &iP;
7149
7150 IMachine *pTarget = aTarget;
7151
7152 /* Convert the options. */
7153 RTCList<CloneOptions_T> optList;
7154 if (aOptions.size())
7155 for (size_t i = 0; i < aOptions.size(); ++i)
7156 optList.append(aOptions[i]);
7157
7158 if (optList.contains(CloneOptions_Link))
7159 {
7160 if (!i_isSnapshotMachine())
7161 return setError(E_INVALIDARG,
7162 tr("Linked clone can only be created from a snapshot"));
7163 if (aMode != CloneMode_MachineState)
7164 return setError(E_INVALIDARG,
7165 tr("Linked clone can only be created for a single machine state"));
7166 }
7167 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7168
7169 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7170
7171 HRESULT rc = pWorker->start(pProgress);
7172
7173 pP = static_cast<Progress *>(*pProgress);
7174 pP.queryInterfaceTo(aProgress.asOutParam());
7175
7176 return rc;
7177
7178}
7179
7180HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7181{
7182 NOREF(aProgress);
7183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7184
7185 // This check should always fail.
7186 HRESULT rc = i_checkStateDependency(MutableStateDep);
7187 if (FAILED(rc)) return rc;
7188
7189 AssertFailedReturn(E_NOTIMPL);
7190}
7191
7192HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7193{
7194 NOREF(aSavedStateFile);
7195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7196
7197 // This check should always fail.
7198 HRESULT rc = i_checkStateDependency(MutableStateDep);
7199 if (FAILED(rc)) return rc;
7200
7201 AssertFailedReturn(E_NOTIMPL);
7202}
7203
7204HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7205{
7206 NOREF(aFRemoveFile);
7207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7208
7209 // This check should always fail.
7210 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7211 if (FAILED(rc)) return rc;
7212
7213 AssertFailedReturn(E_NOTIMPL);
7214}
7215
7216// public methods for internal purposes
7217/////////////////////////////////////////////////////////////////////////////
7218
7219/**
7220 * Adds the given IsModified_* flag to the dirty flags of the machine.
7221 * This must be called either during i_loadSettings or under the machine write lock.
7222 * @param fl Flag
7223 * @param fAllowStateModification If state modifications are allowed.
7224 */
7225void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7226{
7227 mData->flModifications |= fl;
7228 if (fAllowStateModification && i_isStateModificationAllowed())
7229 mData->mCurrentStateModified = true;
7230}
7231
7232/**
7233 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7234 * care of the write locking.
7235 *
7236 * @param fModification The flag to add.
7237 * @param fAllowStateModification If state modifications are allowed.
7238 */
7239void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7240{
7241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7242 i_setModified(fModification, fAllowStateModification);
7243}
7244
7245/**
7246 * Saves the registry entry of this machine to the given configuration node.
7247 *
7248 * @param data Machine registry data.
7249 *
7250 * @note locks this object for reading.
7251 */
7252HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7253{
7254 AutoLimitedCaller autoCaller(this);
7255 AssertComRCReturnRC(autoCaller.rc());
7256
7257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7258
7259 data.uuid = mData->mUuid;
7260 data.strSettingsFile = mData->m_strConfigFile;
7261
7262 return S_OK;
7263}
7264
7265/**
7266 * Calculates the absolute path of the given path taking the directory of the
7267 * machine settings file as the current directory.
7268 *
7269 * @param strPath Path to calculate the absolute path for.
7270 * @param aResult Where to put the result (used only on success, can be the
7271 * same Utf8Str instance as passed in @a aPath).
7272 * @return IPRT result.
7273 *
7274 * @note Locks this object for reading.
7275 */
7276int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7277{
7278 AutoCaller autoCaller(this);
7279 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7280
7281 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7282
7283 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7284
7285 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7286
7287 strSettingsDir.stripFilename();
7288 char folder[RTPATH_MAX];
7289 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7290 if (RT_SUCCESS(vrc))
7291 aResult = folder;
7292
7293 return vrc;
7294}
7295
7296/**
7297 * Copies strSource to strTarget, making it relative to the machine folder
7298 * if it is a subdirectory thereof, or simply copying it otherwise.
7299 *
7300 * @param strSource Path to evaluate and copy.
7301 * @param strTarget Buffer to receive target path.
7302 *
7303 * @note Locks this object for reading.
7304 */
7305void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7306 Utf8Str &strTarget)
7307{
7308 AutoCaller autoCaller(this);
7309 AssertComRCReturn(autoCaller.rc(), (void)0);
7310
7311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7312
7313 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7314 // use strTarget as a temporary buffer to hold the machine settings dir
7315 strTarget = mData->m_strConfigFileFull;
7316 strTarget.stripFilename();
7317 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7318 {
7319 // is relative: then append what's left
7320 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7321 // for empty paths (only possible for subdirs) use "." to avoid
7322 // triggering default settings for not present config attributes.
7323 if (strTarget.isEmpty())
7324 strTarget = ".";
7325 }
7326 else
7327 // is not relative: then overwrite
7328 strTarget = strSource;
7329}
7330
7331/**
7332 * Returns the full path to the machine's log folder in the
7333 * \a aLogFolder argument.
7334 */
7335void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7336{
7337 AutoCaller autoCaller(this);
7338 AssertComRCReturnVoid(autoCaller.rc());
7339
7340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7341
7342 char szTmp[RTPATH_MAX];
7343 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7344 if (RT_SUCCESS(vrc))
7345 {
7346 if (szTmp[0] && !mUserData.isNull())
7347 {
7348 char szTmp2[RTPATH_MAX];
7349 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7350 if (RT_SUCCESS(vrc))
7351 aLogFolder = Utf8StrFmt("%s%c%s",
7352 szTmp2,
7353 RTPATH_DELIMITER,
7354 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7355 }
7356 else
7357 vrc = VERR_PATH_IS_RELATIVE;
7358 }
7359
7360 if (RT_FAILURE(vrc))
7361 {
7362 // fallback if VBOX_USER_LOGHOME is not set or invalid
7363 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7364 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7365 aLogFolder.append(RTPATH_DELIMITER);
7366 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7367 }
7368}
7369
7370/**
7371 * Returns the full path to the machine's log file for an given index.
7372 */
7373Utf8Str Machine::i_getLogFilename(ULONG idx)
7374{
7375 Utf8Str logFolder;
7376 getLogFolder(logFolder);
7377 Assert(logFolder.length());
7378
7379 Utf8Str log;
7380 if (idx == 0)
7381 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7382#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7383 else if (idx == 1)
7384 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7385 else
7386 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7387#else
7388 else
7389 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7390#endif
7391 return log;
7392}
7393
7394/**
7395 * Returns the full path to the machine's hardened log file.
7396 */
7397Utf8Str Machine::i_getHardeningLogFilename(void)
7398{
7399 Utf8Str strFilename;
7400 getLogFolder(strFilename);
7401 Assert(strFilename.length());
7402 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7403 return strFilename;
7404}
7405
7406
7407/**
7408 * Composes a unique saved state filename based on the current system time. The filename is
7409 * granular to the second so this will work so long as no more than one snapshot is taken on
7410 * a machine per second.
7411 *
7412 * Before version 4.1, we used this formula for saved state files:
7413 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7414 * which no longer works because saved state files can now be shared between the saved state of the
7415 * "saved" machine and an online snapshot, and the following would cause problems:
7416 * 1) save machine
7417 * 2) create online snapshot from that machine state --> reusing saved state file
7418 * 3) save machine again --> filename would be reused, breaking the online snapshot
7419 *
7420 * So instead we now use a timestamp.
7421 *
7422 * @param strStateFilePath
7423 */
7424
7425void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7426{
7427 AutoCaller autoCaller(this);
7428 AssertComRCReturnVoid(autoCaller.rc());
7429
7430 {
7431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7432 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7433 }
7434
7435 RTTIMESPEC ts;
7436 RTTimeNow(&ts);
7437 RTTIME time;
7438 RTTimeExplode(&time, &ts);
7439
7440 strStateFilePath += RTPATH_DELIMITER;
7441 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7442 time.i32Year, time.u8Month, time.u8MonthDay,
7443 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7444}
7445
7446/**
7447 * Returns the full path to the default video capture file.
7448 */
7449void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7450{
7451 AutoCaller autoCaller(this);
7452 AssertComRCReturnVoid(autoCaller.rc());
7453
7454 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7455
7456 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7457 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7458 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7459}
7460
7461/**
7462 * Returns whether at least one USB controller is present for the VM.
7463 */
7464bool Machine::i_isUSBControllerPresent()
7465{
7466 AutoCaller autoCaller(this);
7467 AssertComRCReturn(autoCaller.rc(), false);
7468
7469 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7470
7471 return (mUSBControllers->size() > 0);
7472}
7473
7474/**
7475 * @note Locks this object for writing, calls the client process
7476 * (inside the lock).
7477 */
7478HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7479 const Utf8Str &strFrontend,
7480 const Utf8Str &strEnvironment,
7481 ProgressProxy *aProgress)
7482{
7483 LogFlowThisFuncEnter();
7484
7485 AssertReturn(aControl, E_FAIL);
7486 AssertReturn(aProgress, E_FAIL);
7487 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7488
7489 AutoCaller autoCaller(this);
7490 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7491
7492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7493
7494 if (!mData->mRegistered)
7495 return setError(E_UNEXPECTED,
7496 tr("The machine '%s' is not registered"),
7497 mUserData->s.strName.c_str());
7498
7499 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7500
7501 /* The process started when launching a VM with separate UI/VM processes is always
7502 * the UI process, i.e. needs special handling as it won't claim the session. */
7503 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7504
7505 if (fSeparate)
7506 {
7507 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7508 return setError(VBOX_E_INVALID_OBJECT_STATE,
7509 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7510 mUserData->s.strName.c_str());
7511 }
7512 else
7513 {
7514 if ( mData->mSession.mState == SessionState_Locked
7515 || mData->mSession.mState == SessionState_Spawning
7516 || mData->mSession.mState == SessionState_Unlocking)
7517 return setError(VBOX_E_INVALID_OBJECT_STATE,
7518 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7519 mUserData->s.strName.c_str());
7520
7521 /* may not be busy */
7522 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7523 }
7524
7525 /* get the path to the executable */
7526 char szPath[RTPATH_MAX];
7527 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7528 size_t cchBufLeft = strlen(szPath);
7529 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7530 szPath[cchBufLeft] = 0;
7531 char *pszNamePart = szPath + cchBufLeft;
7532 cchBufLeft = sizeof(szPath) - cchBufLeft;
7533
7534 int vrc = VINF_SUCCESS;
7535 RTPROCESS pid = NIL_RTPROCESS;
7536
7537 RTENV env = RTENV_DEFAULT;
7538
7539 if (!strEnvironment.isEmpty())
7540 {
7541 char *newEnvStr = NULL;
7542
7543 do
7544 {
7545 /* clone the current environment */
7546 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7547 AssertRCBreakStmt(vrc2, vrc = vrc2);
7548
7549 newEnvStr = RTStrDup(strEnvironment.c_str());
7550 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7551
7552 /* put new variables to the environment
7553 * (ignore empty variable names here since RTEnv API
7554 * intentionally doesn't do that) */
7555 char *var = newEnvStr;
7556 for (char *p = newEnvStr; *p; ++p)
7557 {
7558 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7559 {
7560 *p = '\0';
7561 if (*var)
7562 {
7563 char *val = strchr(var, '=');
7564 if (val)
7565 {
7566 *val++ = '\0';
7567 vrc2 = RTEnvSetEx(env, var, val);
7568 }
7569 else
7570 vrc2 = RTEnvUnsetEx(env, var);
7571 if (RT_FAILURE(vrc2))
7572 break;
7573 }
7574 var = p + 1;
7575 }
7576 }
7577 if (RT_SUCCESS(vrc2) && *var)
7578 vrc2 = RTEnvPutEx(env, var);
7579
7580 AssertRCBreakStmt(vrc2, vrc = vrc2);
7581 }
7582 while (0);
7583
7584 if (newEnvStr != NULL)
7585 RTStrFree(newEnvStr);
7586 }
7587
7588 /* Hardening logging */
7589#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7590 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7591 {
7592 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7593 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7594 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7595 {
7596 Utf8Str strStartupLogDir = strHardeningLogFile;
7597 strStartupLogDir.stripFilename();
7598 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7599 file without stripping the file. */
7600 }
7601 strSupHardeningLogArg.append(strHardeningLogFile);
7602
7603 /* Remove legacy log filename to avoid confusion. */
7604 Utf8Str strOldStartupLogFile;
7605 getLogFolder(strOldStartupLogFile);
7606 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7607 RTFileDelete(strOldStartupLogFile.c_str());
7608 }
7609 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7610#else
7611 const char *pszSupHardeningLogArg = NULL;
7612#endif
7613
7614 Utf8Str strCanonicalName;
7615
7616#ifdef VBOX_WITH_QTGUI
7617 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7618 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7619 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7620 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7621 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7622 {
7623 strCanonicalName = "GUI/Qt";
7624# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7625 /* Modify the base path so that we don't need to use ".." below. */
7626 RTPathStripTrailingSlash(szPath);
7627 RTPathStripFilename(szPath);
7628 cchBufLeft = strlen(szPath);
7629 pszNamePart = szPath + cchBufLeft;
7630 cchBufLeft = sizeof(szPath) - cchBufLeft;
7631
7632# define OSX_APP_NAME "VirtualBoxVM"
7633# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7634
7635 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7636 if ( strAppOverride.contains(".")
7637 || strAppOverride.contains("/")
7638 || strAppOverride.contains("\\")
7639 || strAppOverride.contains(":"))
7640 strAppOverride.setNull();
7641 Utf8Str strAppPath;
7642 if (!strAppOverride.isEmpty())
7643 {
7644 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7645 Utf8Str strFullPath(szPath);
7646 strFullPath.append(strAppPath);
7647 /* there is a race, but people using this deserve the failure */
7648 if (!RTFileExists(strFullPath.c_str()))
7649 strAppOverride.setNull();
7650 }
7651 if (strAppOverride.isEmpty())
7652 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7653 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7654 strcpy(pszNamePart, strAppPath.c_str());
7655# else
7656 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7657 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7658 strcpy(pszNamePart, s_szVirtualBox_exe);
7659# endif
7660
7661 Utf8Str idStr = mData->mUuid.toString();
7662 const char *apszArgs[] =
7663 {
7664 szPath,
7665 "--comment", mUserData->s.strName.c_str(),
7666 "--startvm", idStr.c_str(),
7667 "--no-startvm-errormsgbox",
7668 NULL, /* For "--separate". */
7669 NULL, /* For "--sup-startup-log". */
7670 NULL
7671 };
7672 unsigned iArg = 6;
7673 if (fSeparate)
7674 apszArgs[iArg++] = "--separate";
7675 apszArgs[iArg++] = pszSupHardeningLogArg;
7676
7677 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7678 }
7679#else /* !VBOX_WITH_QTGUI */
7680 if (0)
7681 ;
7682#endif /* VBOX_WITH_QTGUI */
7683
7684 else
7685
7686#ifdef VBOX_WITH_VBOXSDL
7687 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7688 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7689 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7690 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7691 {
7692 strCanonicalName = "GUI/SDL";
7693 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7694 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7695 strcpy(pszNamePart, s_szVBoxSDL_exe);
7696
7697 Utf8Str idStr = mData->mUuid.toString();
7698 const char *apszArgs[] =
7699 {
7700 szPath,
7701 "--comment", mUserData->s.strName.c_str(),
7702 "--startvm", idStr.c_str(),
7703 NULL, /* For "--separate". */
7704 NULL, /* For "--sup-startup-log". */
7705 NULL
7706 };
7707 unsigned iArg = 5;
7708 if (fSeparate)
7709 apszArgs[iArg++] = "--separate";
7710 apszArgs[iArg++] = pszSupHardeningLogArg;
7711
7712 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7713 }
7714#else /* !VBOX_WITH_VBOXSDL */
7715 if (0)
7716 ;
7717#endif /* !VBOX_WITH_VBOXSDL */
7718
7719 else
7720
7721#ifdef VBOX_WITH_HEADLESS
7722 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7723 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7724 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7725 )
7726 {
7727 strCanonicalName = "headless";
7728 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7729 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7730 * and a VM works even if the server has not been installed.
7731 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7732 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7733 * differently in 4.0 and 3.x.
7734 */
7735 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7736 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7737 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7738
7739 Utf8Str idStr = mData->mUuid.toString();
7740 const char *apszArgs[] =
7741 {
7742 szPath,
7743 "--comment", mUserData->s.strName.c_str(),
7744 "--startvm", idStr.c_str(),
7745 "--vrde", "config",
7746 NULL, /* For "--capture". */
7747 NULL, /* For "--sup-startup-log". */
7748 NULL
7749 };
7750 unsigned iArg = 7;
7751 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7752 apszArgs[iArg++] = "--capture";
7753 apszArgs[iArg++] = pszSupHardeningLogArg;
7754
7755# ifdef RT_OS_WINDOWS
7756 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7757# else
7758 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7759# endif
7760 }
7761#else /* !VBOX_WITH_HEADLESS */
7762 if (0)
7763 ;
7764#endif /* !VBOX_WITH_HEADLESS */
7765 else
7766 {
7767 RTEnvDestroy(env);
7768 return setError(E_INVALIDARG,
7769 tr("Invalid frontend name: '%s'"),
7770 strFrontend.c_str());
7771 }
7772
7773 RTEnvDestroy(env);
7774
7775 if (RT_FAILURE(vrc))
7776 return setError(VBOX_E_IPRT_ERROR,
7777 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7778 mUserData->s.strName.c_str(), vrc);
7779
7780 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7781
7782 if (!fSeparate)
7783 {
7784 /*
7785 * Note that we don't release the lock here before calling the client,
7786 * because it doesn't need to call us back if called with a NULL argument.
7787 * Releasing the lock here is dangerous because we didn't prepare the
7788 * launch data yet, but the client we've just started may happen to be
7789 * too fast and call LockMachine() that will fail (because of PID, etc.),
7790 * so that the Machine will never get out of the Spawning session state.
7791 */
7792
7793 /* inform the session that it will be a remote one */
7794 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7795#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7796 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7797#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7798 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7799#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7800 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7801
7802 if (FAILED(rc))
7803 {
7804 /* restore the session state */
7805 mData->mSession.mState = SessionState_Unlocked;
7806 alock.release();
7807 mParent->i_addProcessToReap(pid);
7808 /* The failure may occur w/o any error info (from RPC), so provide one */
7809 return setError(VBOX_E_VM_ERROR,
7810 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7811 }
7812
7813 /* attach launch data to the machine */
7814 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7815 mData->mSession.mRemoteControls.push_back(aControl);
7816 mData->mSession.mProgress = aProgress;
7817 mData->mSession.mPID = pid;
7818 mData->mSession.mState = SessionState_Spawning;
7819 Assert(strCanonicalName.isNotEmpty());
7820 mData->mSession.mName = strCanonicalName;
7821 }
7822 else
7823 {
7824 /* For separate UI process we declare the launch as completed instantly, as the
7825 * actual headless VM start may or may not come. No point in remembering anything
7826 * yet, as what matters for us is when the headless VM gets started. */
7827 aProgress->i_notifyComplete(S_OK);
7828 }
7829
7830 alock.release();
7831 mParent->i_addProcessToReap(pid);
7832
7833 LogFlowThisFuncLeave();
7834 return S_OK;
7835}
7836
7837/**
7838 * Returns @c true if the given session machine instance has an open direct
7839 * session (and optionally also for direct sessions which are closing) and
7840 * returns the session control machine instance if so.
7841 *
7842 * Note that when the method returns @c false, the arguments remain unchanged.
7843 *
7844 * @param aMachine Session machine object.
7845 * @param aControl Direct session control object (optional).
7846 * @param aRequireVM If true then only allow VM sessions.
7847 * @param aAllowClosing If true then additionally a session which is currently
7848 * being closed will also be allowed.
7849 *
7850 * @note locks this object for reading.
7851 */
7852bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7853 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7854 bool aRequireVM /*= false*/,
7855 bool aAllowClosing /*= false*/)
7856{
7857 AutoLimitedCaller autoCaller(this);
7858 AssertComRCReturn(autoCaller.rc(), false);
7859
7860 /* just return false for inaccessible machines */
7861 if (getObjectState().getState() != ObjectState::Ready)
7862 return false;
7863
7864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7865
7866 if ( ( mData->mSession.mState == SessionState_Locked
7867 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7868 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7869 )
7870 {
7871 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7872
7873 aMachine = mData->mSession.mMachine;
7874
7875 if (aControl != NULL)
7876 *aControl = mData->mSession.mDirectControl;
7877
7878 return true;
7879 }
7880
7881 return false;
7882}
7883
7884/**
7885 * Returns @c true if the given machine has an spawning direct session.
7886 *
7887 * @note locks this object for reading.
7888 */
7889bool Machine::i_isSessionSpawning()
7890{
7891 AutoLimitedCaller autoCaller(this);
7892 AssertComRCReturn(autoCaller.rc(), false);
7893
7894 /* just return false for inaccessible machines */
7895 if (getObjectState().getState() != ObjectState::Ready)
7896 return false;
7897
7898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7899
7900 if (mData->mSession.mState == SessionState_Spawning)
7901 return true;
7902
7903 return false;
7904}
7905
7906/**
7907 * Called from the client watcher thread to check for unexpected client process
7908 * death during Session_Spawning state (e.g. before it successfully opened a
7909 * direct session).
7910 *
7911 * On Win32 and on OS/2, this method is called only when we've got the
7912 * direct client's process termination notification, so it always returns @c
7913 * true.
7914 *
7915 * On other platforms, this method returns @c true if the client process is
7916 * terminated and @c false if it's still alive.
7917 *
7918 * @note Locks this object for writing.
7919 */
7920bool Machine::i_checkForSpawnFailure()
7921{
7922 AutoCaller autoCaller(this);
7923 if (!autoCaller.isOk())
7924 {
7925 /* nothing to do */
7926 LogFlowThisFunc(("Already uninitialized!\n"));
7927 return true;
7928 }
7929
7930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7931
7932 if (mData->mSession.mState != SessionState_Spawning)
7933 {
7934 /* nothing to do */
7935 LogFlowThisFunc(("Not spawning any more!\n"));
7936 return true;
7937 }
7938
7939 HRESULT rc = S_OK;
7940
7941 /* PID not yet initialized, skip check. */
7942 if (mData->mSession.mPID == NIL_RTPROCESS)
7943 return false;
7944
7945 RTPROCSTATUS status;
7946 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7947
7948 if (vrc != VERR_PROCESS_RUNNING)
7949 {
7950 Utf8Str strExtraInfo;
7951
7952#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7953 /* If the startup logfile exists and is of non-zero length, tell the
7954 user to look there for more details to encourage them to attach it
7955 when reporting startup issues. */
7956 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7957 uint64_t cbStartupLogFile = 0;
7958 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7959 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7960 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7961#endif
7962
7963 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7964 rc = setError(E_FAIL,
7965 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7966 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7967 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7968 rc = setError(E_FAIL,
7969 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7970 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7971 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7972 rc = setError(E_FAIL,
7973 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7974 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7975 else
7976 rc = setError(E_FAIL,
7977 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7978 i_getName().c_str(), vrc, strExtraInfo.c_str());
7979 }
7980
7981 if (FAILED(rc))
7982 {
7983 /* Close the remote session, remove the remote control from the list
7984 * and reset session state to Closed (@note keep the code in sync with
7985 * the relevant part in LockMachine()). */
7986
7987 Assert(mData->mSession.mRemoteControls.size() == 1);
7988 if (mData->mSession.mRemoteControls.size() == 1)
7989 {
7990 ErrorInfoKeeper eik;
7991 mData->mSession.mRemoteControls.front()->Uninitialize();
7992 }
7993
7994 mData->mSession.mRemoteControls.clear();
7995 mData->mSession.mState = SessionState_Unlocked;
7996
7997 /* finalize the progress after setting the state */
7998 if (!mData->mSession.mProgress.isNull())
7999 {
8000 mData->mSession.mProgress->notifyComplete(rc);
8001 mData->mSession.mProgress.setNull();
8002 }
8003
8004 mData->mSession.mPID = NIL_RTPROCESS;
8005
8006 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8007 return true;
8008 }
8009
8010 return false;
8011}
8012
8013/**
8014 * Checks whether the machine can be registered. If so, commits and saves
8015 * all settings.
8016 *
8017 * @note Must be called from mParent's write lock. Locks this object and
8018 * children for writing.
8019 */
8020HRESULT Machine::i_prepareRegister()
8021{
8022 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8023
8024 AutoLimitedCaller autoCaller(this);
8025 AssertComRCReturnRC(autoCaller.rc());
8026
8027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8028
8029 /* wait for state dependents to drop to zero */
8030 i_ensureNoStateDependencies();
8031
8032 if (!mData->mAccessible)
8033 return setError(VBOX_E_INVALID_OBJECT_STATE,
8034 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8035 mUserData->s.strName.c_str(),
8036 mData->mUuid.toString().c_str());
8037
8038 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8039
8040 if (mData->mRegistered)
8041 return setError(VBOX_E_INVALID_OBJECT_STATE,
8042 tr("The machine '%s' with UUID {%s} is already registered"),
8043 mUserData->s.strName.c_str(),
8044 mData->mUuid.toString().c_str());
8045
8046 HRESULT rc = S_OK;
8047
8048 // Ensure the settings are saved. If we are going to be registered and
8049 // no config file exists yet, create it by calling i_saveSettings() too.
8050 if ( (mData->flModifications)
8051 || (!mData->pMachineConfigFile->fileExists())
8052 )
8053 {
8054 rc = i_saveSettings(NULL);
8055 // no need to check whether VirtualBox.xml needs saving too since
8056 // we can't have a machine XML file rename pending
8057 if (FAILED(rc)) return rc;
8058 }
8059
8060 /* more config checking goes here */
8061
8062 if (SUCCEEDED(rc))
8063 {
8064 /* we may have had implicit modifications we want to fix on success */
8065 i_commit();
8066
8067 mData->mRegistered = true;
8068 }
8069 else
8070 {
8071 /* we may have had implicit modifications we want to cancel on failure*/
8072 i_rollback(false /* aNotify */);
8073 }
8074
8075 return rc;
8076}
8077
8078/**
8079 * Increases the number of objects dependent on the machine state or on the
8080 * registered state. Guarantees that these two states will not change at least
8081 * until #i_releaseStateDependency() is called.
8082 *
8083 * Depending on the @a aDepType value, additional state checks may be made.
8084 * These checks will set extended error info on failure. See
8085 * #i_checkStateDependency() for more info.
8086 *
8087 * If this method returns a failure, the dependency is not added and the caller
8088 * is not allowed to rely on any particular machine state or registration state
8089 * value and may return the failed result code to the upper level.
8090 *
8091 * @param aDepType Dependency type to add.
8092 * @param aState Current machine state (NULL if not interested).
8093 * @param aRegistered Current registered state (NULL if not interested).
8094 *
8095 * @note Locks this object for writing.
8096 */
8097HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8098 MachineState_T *aState /* = NULL */,
8099 BOOL *aRegistered /* = NULL */)
8100{
8101 AutoCaller autoCaller(this);
8102 AssertComRCReturnRC(autoCaller.rc());
8103
8104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8105
8106 HRESULT rc = i_checkStateDependency(aDepType);
8107 if (FAILED(rc)) return rc;
8108
8109 {
8110 if (mData->mMachineStateChangePending != 0)
8111 {
8112 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8113 * drop to zero so don't add more. It may make sense to wait a bit
8114 * and retry before reporting an error (since the pending state
8115 * transition should be really quick) but let's just assert for
8116 * now to see if it ever happens on practice. */
8117
8118 AssertFailed();
8119
8120 return setError(E_ACCESSDENIED,
8121 tr("Machine state change is in progress. Please retry the operation later."));
8122 }
8123
8124 ++mData->mMachineStateDeps;
8125 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8126 }
8127
8128 if (aState)
8129 *aState = mData->mMachineState;
8130 if (aRegistered)
8131 *aRegistered = mData->mRegistered;
8132
8133 return S_OK;
8134}
8135
8136/**
8137 * Decreases the number of objects dependent on the machine state.
8138 * Must always complete the #i_addStateDependency() call after the state
8139 * dependency is no more necessary.
8140 */
8141void Machine::i_releaseStateDependency()
8142{
8143 AutoCaller autoCaller(this);
8144 AssertComRCReturnVoid(autoCaller.rc());
8145
8146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8147
8148 /* releaseStateDependency() w/o addStateDependency()? */
8149 AssertReturnVoid(mData->mMachineStateDeps != 0);
8150 -- mData->mMachineStateDeps;
8151
8152 if (mData->mMachineStateDeps == 0)
8153 {
8154 /* inform i_ensureNoStateDependencies() that there are no more deps */
8155 if (mData->mMachineStateChangePending != 0)
8156 {
8157 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8158 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8159 }
8160 }
8161}
8162
8163Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8164{
8165 /* start with nothing found */
8166 Utf8Str strResult("");
8167
8168 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8169
8170 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8171 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8172 // found:
8173 strResult = it->second; // source is a Utf8Str
8174
8175 return strResult;
8176}
8177
8178// protected methods
8179/////////////////////////////////////////////////////////////////////////////
8180
8181/**
8182 * Performs machine state checks based on the @a aDepType value. If a check
8183 * fails, this method will set extended error info, otherwise it will return
8184 * S_OK. It is supposed, that on failure, the caller will immediately return
8185 * the return value of this method to the upper level.
8186 *
8187 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8188 *
8189 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8190 * current state of this machine object allows to change settings of the
8191 * machine (i.e. the machine is not registered, or registered but not running
8192 * and not saved). It is useful to call this method from Machine setters
8193 * before performing any change.
8194 *
8195 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8196 * as for MutableStateDep except that if the machine is saved, S_OK is also
8197 * returned. This is useful in setters which allow changing machine
8198 * properties when it is in the saved state.
8199 *
8200 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8201 * if the current state of this machine object allows to change runtime
8202 * changeable settings of the machine (i.e. the machine is not registered, or
8203 * registered but either running or not running and not saved). It is useful
8204 * to call this method from Machine setters before performing any changes to
8205 * runtime changeable settings.
8206 *
8207 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8208 * the same as for MutableOrRunningStateDep except that if the machine is
8209 * saved, S_OK is also returned. This is useful in setters which allow
8210 * changing runtime and saved state changeable machine properties.
8211 *
8212 * @param aDepType Dependency type to check.
8213 *
8214 * @note Non Machine based classes should use #i_addStateDependency() and
8215 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8216 * template.
8217 *
8218 * @note This method must be called from under this object's read or write
8219 * lock.
8220 */
8221HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8222{
8223 switch (aDepType)
8224 {
8225 case AnyStateDep:
8226 {
8227 break;
8228 }
8229 case MutableStateDep:
8230 {
8231 if ( mData->mRegistered
8232 && ( !i_isSessionMachine()
8233 || ( mData->mMachineState != MachineState_Aborted
8234 && mData->mMachineState != MachineState_Teleported
8235 && mData->mMachineState != MachineState_PoweredOff
8236 )
8237 )
8238 )
8239 return setError(VBOX_E_INVALID_VM_STATE,
8240 tr("The machine is not mutable (state is %s)"),
8241 Global::stringifyMachineState(mData->mMachineState));
8242 break;
8243 }
8244 case MutableOrSavedStateDep:
8245 {
8246 if ( mData->mRegistered
8247 && ( !i_isSessionMachine()
8248 || ( mData->mMachineState != MachineState_Aborted
8249 && mData->mMachineState != MachineState_Teleported
8250 && mData->mMachineState != MachineState_Saved
8251 && mData->mMachineState != MachineState_PoweredOff
8252 )
8253 )
8254 )
8255 return setError(VBOX_E_INVALID_VM_STATE,
8256 tr("The machine is not mutable or saved (state is %s)"),
8257 Global::stringifyMachineState(mData->mMachineState));
8258 break;
8259 }
8260 case MutableOrRunningStateDep:
8261 {
8262 if ( mData->mRegistered
8263 && ( !i_isSessionMachine()
8264 || ( mData->mMachineState != MachineState_Aborted
8265 && mData->mMachineState != MachineState_Teleported
8266 && mData->mMachineState != MachineState_PoweredOff
8267 && !Global::IsOnline(mData->mMachineState)
8268 )
8269 )
8270 )
8271 return setError(VBOX_E_INVALID_VM_STATE,
8272 tr("The machine is not mutable or running (state is %s)"),
8273 Global::stringifyMachineState(mData->mMachineState));
8274 break;
8275 }
8276 case MutableOrSavedOrRunningStateDep:
8277 {
8278 if ( mData->mRegistered
8279 && ( !i_isSessionMachine()
8280 || ( mData->mMachineState != MachineState_Aborted
8281 && mData->mMachineState != MachineState_Teleported
8282 && mData->mMachineState != MachineState_Saved
8283 && mData->mMachineState != MachineState_PoweredOff
8284 && !Global::IsOnline(mData->mMachineState)
8285 )
8286 )
8287 )
8288 return setError(VBOX_E_INVALID_VM_STATE,
8289 tr("The machine is not mutable, saved or running (state is %s)"),
8290 Global::stringifyMachineState(mData->mMachineState));
8291 break;
8292 }
8293 }
8294
8295 return S_OK;
8296}
8297
8298/**
8299 * Helper to initialize all associated child objects and allocate data
8300 * structures.
8301 *
8302 * This method must be called as a part of the object's initialization procedure
8303 * (usually done in the #init() method).
8304 *
8305 * @note Must be called only from #init() or from #i_registeredInit().
8306 */
8307HRESULT Machine::initDataAndChildObjects()
8308{
8309 AutoCaller autoCaller(this);
8310 AssertComRCReturnRC(autoCaller.rc());
8311 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8312 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8313
8314 AssertReturn(!mData->mAccessible, E_FAIL);
8315
8316 /* allocate data structures */
8317 mSSData.allocate();
8318 mUserData.allocate();
8319 mHWData.allocate();
8320 mMediumAttachments.allocate();
8321 mStorageControllers.allocate();
8322 mUSBControllers.allocate();
8323
8324 /* initialize mOSTypeId */
8325 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8326
8327/** @todo r=bird: init() methods never fails, right? Why don't we make them
8328 * return void then! */
8329
8330 /* create associated BIOS settings object */
8331 unconst(mBIOSSettings).createObject();
8332 mBIOSSettings->init(this);
8333
8334 /* create an associated VRDE object (default is disabled) */
8335 unconst(mVRDEServer).createObject();
8336 mVRDEServer->init(this);
8337
8338 /* create associated serial port objects */
8339 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8340 {
8341 unconst(mSerialPorts[slot]).createObject();
8342 mSerialPorts[slot]->init(this, slot);
8343 }
8344
8345 /* create associated parallel port objects */
8346 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8347 {
8348 unconst(mParallelPorts[slot]).createObject();
8349 mParallelPorts[slot]->init(this, slot);
8350 }
8351
8352 /* create the audio adapter object (always present, default is disabled) */
8353 unconst(mAudioAdapter).createObject();
8354 mAudioAdapter->init(this);
8355
8356 /* create the USB device filters object (always present) */
8357 unconst(mUSBDeviceFilters).createObject();
8358 mUSBDeviceFilters->init(this);
8359
8360 /* create associated network adapter objects */
8361 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8362 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8363 {
8364 unconst(mNetworkAdapters[slot]).createObject();
8365 mNetworkAdapters[slot]->init(this, slot);
8366 }
8367
8368 /* create the bandwidth control */
8369 unconst(mBandwidthControl).createObject();
8370 mBandwidthControl->init(this);
8371
8372 return S_OK;
8373}
8374
8375/**
8376 * Helper to uninitialize all associated child objects and to free all data
8377 * structures.
8378 *
8379 * This method must be called as a part of the object's uninitialization
8380 * procedure (usually done in the #uninit() method).
8381 *
8382 * @note Must be called only from #uninit() or from #i_registeredInit().
8383 */
8384void Machine::uninitDataAndChildObjects()
8385{
8386 AutoCaller autoCaller(this);
8387 AssertComRCReturnVoid(autoCaller.rc());
8388 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8389 || getObjectState().getState() == ObjectState::Limited);
8390
8391 /* tell all our other child objects we've been uninitialized */
8392 if (mBandwidthControl)
8393 {
8394 mBandwidthControl->uninit();
8395 unconst(mBandwidthControl).setNull();
8396 }
8397
8398 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8399 {
8400 if (mNetworkAdapters[slot])
8401 {
8402 mNetworkAdapters[slot]->uninit();
8403 unconst(mNetworkAdapters[slot]).setNull();
8404 }
8405 }
8406
8407 if (mUSBDeviceFilters)
8408 {
8409 mUSBDeviceFilters->uninit();
8410 unconst(mUSBDeviceFilters).setNull();
8411 }
8412
8413 if (mAudioAdapter)
8414 {
8415 mAudioAdapter->uninit();
8416 unconst(mAudioAdapter).setNull();
8417 }
8418
8419 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8420 {
8421 if (mParallelPorts[slot])
8422 {
8423 mParallelPorts[slot]->uninit();
8424 unconst(mParallelPorts[slot]).setNull();
8425 }
8426 }
8427
8428 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8429 {
8430 if (mSerialPorts[slot])
8431 {
8432 mSerialPorts[slot]->uninit();
8433 unconst(mSerialPorts[slot]).setNull();
8434 }
8435 }
8436
8437 if (mVRDEServer)
8438 {
8439 mVRDEServer->uninit();
8440 unconst(mVRDEServer).setNull();
8441 }
8442
8443 if (mBIOSSettings)
8444 {
8445 mBIOSSettings->uninit();
8446 unconst(mBIOSSettings).setNull();
8447 }
8448
8449 /* Deassociate media (only when a real Machine or a SnapshotMachine
8450 * instance is uninitialized; SessionMachine instances refer to real
8451 * Machine media). This is necessary for a clean re-initialization of
8452 * the VM after successfully re-checking the accessibility state. Note
8453 * that in case of normal Machine or SnapshotMachine uninitialization (as
8454 * a result of unregistering or deleting the snapshot), outdated media
8455 * attachments will already be uninitialized and deleted, so this
8456 * code will not affect them. */
8457 if ( !mMediumAttachments.isNull()
8458 && !i_isSessionMachine()
8459 )
8460 {
8461 for (MediumAttachmentList::const_iterator
8462 it = mMediumAttachments->begin();
8463 it != mMediumAttachments->end();
8464 ++it)
8465 {
8466 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8467 if (pMedium.isNull())
8468 continue;
8469 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8470 AssertComRC(rc);
8471 }
8472 }
8473
8474 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8475 {
8476 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8477 if (mData->mFirstSnapshot)
8478 {
8479 // snapshots tree is protected by machine write lock; strictly
8480 // this isn't necessary here since we're deleting the entire
8481 // machine, but otherwise we assert in Snapshot::uninit()
8482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8483 mData->mFirstSnapshot->uninit();
8484 mData->mFirstSnapshot.setNull();
8485 }
8486
8487 mData->mCurrentSnapshot.setNull();
8488 }
8489
8490 /* free data structures (the essential mData structure is not freed here
8491 * since it may be still in use) */
8492 mMediumAttachments.free();
8493 mStorageControllers.free();
8494 mUSBControllers.free();
8495 mHWData.free();
8496 mUserData.free();
8497 mSSData.free();
8498}
8499
8500/**
8501 * Returns a pointer to the Machine object for this machine that acts like a
8502 * parent for complex machine data objects such as shared folders, etc.
8503 *
8504 * For primary Machine objects and for SnapshotMachine objects, returns this
8505 * object's pointer itself. For SessionMachine objects, returns the peer
8506 * (primary) machine pointer.
8507 */
8508Machine *Machine::i_getMachine()
8509{
8510 if (i_isSessionMachine())
8511 return (Machine*)mPeer;
8512 return this;
8513}
8514
8515/**
8516 * Makes sure that there are no machine state dependents. If necessary, waits
8517 * for the number of dependents to drop to zero.
8518 *
8519 * Make sure this method is called from under this object's write lock to
8520 * guarantee that no new dependents may be added when this method returns
8521 * control to the caller.
8522 *
8523 * @note Locks this object for writing. The lock will be released while waiting
8524 * (if necessary).
8525 *
8526 * @warning To be used only in methods that change the machine state!
8527 */
8528void Machine::i_ensureNoStateDependencies()
8529{
8530 AssertReturnVoid(isWriteLockOnCurrentThread());
8531
8532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8533
8534 /* Wait for all state dependents if necessary */
8535 if (mData->mMachineStateDeps != 0)
8536 {
8537 /* lazy semaphore creation */
8538 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8539 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8540
8541 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8542 mData->mMachineStateDeps));
8543
8544 ++mData->mMachineStateChangePending;
8545
8546 /* reset the semaphore before waiting, the last dependent will signal
8547 * it */
8548 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8549
8550 alock.release();
8551
8552 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8553
8554 alock.acquire();
8555
8556 -- mData->mMachineStateChangePending;
8557 }
8558}
8559
8560/**
8561 * Changes the machine state and informs callbacks.
8562 *
8563 * This method is not intended to fail so it either returns S_OK or asserts (and
8564 * returns a failure).
8565 *
8566 * @note Locks this object for writing.
8567 */
8568HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8569{
8570 LogFlowThisFuncEnter();
8571 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8572 Assert(aMachineState != MachineState_Null);
8573
8574 AutoCaller autoCaller(this);
8575 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8576
8577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8578
8579 /* wait for state dependents to drop to zero */
8580 i_ensureNoStateDependencies();
8581
8582 MachineState_T const enmOldState = mData->mMachineState;
8583 if (enmOldState != aMachineState)
8584 {
8585 mData->mMachineState = aMachineState;
8586 RTTimeNow(&mData->mLastStateChange);
8587
8588#ifdef VBOX_WITH_DTRACE_R3_MAIN
8589 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8590#endif
8591 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8592 }
8593
8594 LogFlowThisFuncLeave();
8595 return S_OK;
8596}
8597
8598/**
8599 * Searches for a shared folder with the given logical name
8600 * in the collection of shared folders.
8601 *
8602 * @param aName logical name of the shared folder
8603 * @param aSharedFolder where to return the found object
8604 * @param aSetError whether to set the error info if the folder is
8605 * not found
8606 * @return
8607 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8608 *
8609 * @note
8610 * must be called from under the object's lock!
8611 */
8612HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8613 ComObjPtr<SharedFolder> &aSharedFolder,
8614 bool aSetError /* = false */)
8615{
8616 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8617 for (HWData::SharedFolderList::const_iterator
8618 it = mHWData->mSharedFolders.begin();
8619 it != mHWData->mSharedFolders.end();
8620 ++it)
8621 {
8622 SharedFolder *pSF = *it;
8623 AutoCaller autoCaller(pSF);
8624 if (pSF->i_getName() == aName)
8625 {
8626 aSharedFolder = pSF;
8627 rc = S_OK;
8628 break;
8629 }
8630 }
8631
8632 if (aSetError && FAILED(rc))
8633 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8634
8635 return rc;
8636}
8637
8638/**
8639 * Initializes all machine instance data from the given settings structures
8640 * from XML. The exception is the machine UUID which needs special handling
8641 * depending on the caller's use case, so the caller needs to set that herself.
8642 *
8643 * This gets called in several contexts during machine initialization:
8644 *
8645 * -- When machine XML exists on disk already and needs to be loaded into memory,
8646 * for example, from #i_registeredInit() to load all registered machines on
8647 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8648 * attached to the machine should be part of some media registry already.
8649 *
8650 * -- During OVF import, when a machine config has been constructed from an
8651 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8652 * ensure that the media listed as attachments in the config (which have
8653 * been imported from the OVF) receive the correct registry ID.
8654 *
8655 * -- During VM cloning.
8656 *
8657 * @param config Machine settings from XML.
8658 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8659 * for each attached medium in the config.
8660 * @return
8661 */
8662HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8663 const Guid *puuidRegistry)
8664{
8665 // copy name, description, OS type, teleporter, UTC etc.
8666 mUserData->s = config.machineUserData;
8667
8668 // look up the object by Id to check it is valid
8669 ComObjPtr<GuestOSType> pGuestOSType;
8670 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8671 pGuestOSType);
8672 if (FAILED(rc)) return rc;
8673 mUserData->s.strOsType = pGuestOSType->i_id();
8674
8675 // stateFile (optional)
8676 if (config.strStateFile.isEmpty())
8677 mSSData->strStateFilePath.setNull();
8678 else
8679 {
8680 Utf8Str stateFilePathFull(config.strStateFile);
8681 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8682 if (RT_FAILURE(vrc))
8683 return setError(E_FAIL,
8684 tr("Invalid saved state file path '%s' (%Rrc)"),
8685 config.strStateFile.c_str(),
8686 vrc);
8687 mSSData->strStateFilePath = stateFilePathFull;
8688 }
8689
8690 // snapshot folder needs special processing so set it again
8691 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8692 if (FAILED(rc)) return rc;
8693
8694 /* Copy the extra data items (config may or may not be the same as
8695 * mData->pMachineConfigFile) if necessary. When loading the XML files
8696 * from disk they are the same, but not for OVF import. */
8697 if (mData->pMachineConfigFile != &config)
8698 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8699
8700 /* currentStateModified (optional, default is true) */
8701 mData->mCurrentStateModified = config.fCurrentStateModified;
8702
8703 mData->mLastStateChange = config.timeLastStateChange;
8704
8705 /*
8706 * note: all mUserData members must be assigned prior this point because
8707 * we need to commit changes in order to let mUserData be shared by all
8708 * snapshot machine instances.
8709 */
8710 mUserData.commitCopy();
8711
8712 // machine registry, if present (must be loaded before snapshots)
8713 if (config.canHaveOwnMediaRegistry())
8714 {
8715 // determine machine folder
8716 Utf8Str strMachineFolder = i_getSettingsFileFull();
8717 strMachineFolder.stripFilename();
8718 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8719 config.mediaRegistry,
8720 strMachineFolder);
8721 if (FAILED(rc)) return rc;
8722 }
8723
8724 /* Snapshot node (optional) */
8725 size_t cRootSnapshots;
8726 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8727 {
8728 // there must be only one root snapshot
8729 Assert(cRootSnapshots == 1);
8730
8731 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8732
8733 rc = i_loadSnapshot(snap,
8734 config.uuidCurrentSnapshot,
8735 NULL); // no parent == first snapshot
8736 if (FAILED(rc)) return rc;
8737 }
8738
8739 // hardware data
8740 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8741 if (FAILED(rc)) return rc;
8742
8743 /*
8744 * NOTE: the assignment below must be the last thing to do,
8745 * otherwise it will be not possible to change the settings
8746 * somewhere in the code above because all setters will be
8747 * blocked by i_checkStateDependency(MutableStateDep).
8748 */
8749
8750 /* set the machine state to Aborted or Saved when appropriate */
8751 if (config.fAborted)
8752 {
8753 mSSData->strStateFilePath.setNull();
8754
8755 /* no need to use i_setMachineState() during init() */
8756 mData->mMachineState = MachineState_Aborted;
8757 }
8758 else if (!mSSData->strStateFilePath.isEmpty())
8759 {
8760 /* no need to use i_setMachineState() during init() */
8761 mData->mMachineState = MachineState_Saved;
8762 }
8763
8764 // after loading settings, we are no longer different from the XML on disk
8765 mData->flModifications = 0;
8766
8767 return S_OK;
8768}
8769
8770/**
8771 * Recursively loads all snapshots starting from the given.
8772 *
8773 * @param data snapshot settings.
8774 * @param aCurSnapshotId Current snapshot ID from the settings file.
8775 * @param aParentSnapshot Parent snapshot.
8776 */
8777HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8778 const Guid &aCurSnapshotId,
8779 Snapshot *aParentSnapshot)
8780{
8781 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8782 AssertReturn(!i_isSessionMachine(), E_FAIL);
8783
8784 HRESULT rc = S_OK;
8785
8786 Utf8Str strStateFile;
8787 if (!data.strStateFile.isEmpty())
8788 {
8789 /* optional */
8790 strStateFile = data.strStateFile;
8791 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8792 if (RT_FAILURE(vrc))
8793 return setError(E_FAIL,
8794 tr("Invalid saved state file path '%s' (%Rrc)"),
8795 strStateFile.c_str(),
8796 vrc);
8797 }
8798
8799 /* create a snapshot machine object */
8800 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8801 pSnapshotMachine.createObject();
8802 rc = pSnapshotMachine->initFromSettings(this,
8803 data.hardware,
8804 &data.debugging,
8805 &data.autostart,
8806 data.uuid.ref(),
8807 strStateFile);
8808 if (FAILED(rc)) return rc;
8809
8810 /* create a snapshot object */
8811 ComObjPtr<Snapshot> pSnapshot;
8812 pSnapshot.createObject();
8813 /* initialize the snapshot */
8814 rc = pSnapshot->init(mParent, // VirtualBox object
8815 data.uuid,
8816 data.strName,
8817 data.strDescription,
8818 data.timestamp,
8819 pSnapshotMachine,
8820 aParentSnapshot);
8821 if (FAILED(rc)) return rc;
8822
8823 /* memorize the first snapshot if necessary */
8824 if (!mData->mFirstSnapshot)
8825 mData->mFirstSnapshot = pSnapshot;
8826
8827 /* memorize the current snapshot when appropriate */
8828 if ( !mData->mCurrentSnapshot
8829 && pSnapshot->i_getId() == aCurSnapshotId
8830 )
8831 mData->mCurrentSnapshot = pSnapshot;
8832
8833 // now create the children
8834 for (settings::SnapshotsList::const_iterator
8835 it = data.llChildSnapshots.begin();
8836 it != data.llChildSnapshots.end();
8837 ++it)
8838 {
8839 const settings::Snapshot &childData = *it;
8840 // recurse
8841 rc = i_loadSnapshot(childData,
8842 aCurSnapshotId,
8843 pSnapshot); // parent = the one we created above
8844 if (FAILED(rc)) return rc;
8845 }
8846
8847 return rc;
8848}
8849
8850/**
8851 * Loads settings into mHWData.
8852 *
8853 * @param puuidRegistry Registry ID.
8854 * @param puuidSnapshot Snapshot ID
8855 * @param data Reference to the hardware settings.
8856 * @param pDbg Pointer to the debugging settings.
8857 * @param pAutostart Pointer to the autostart settings.
8858 */
8859HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8860 const Guid *puuidSnapshot,
8861 const settings::Hardware &data,
8862 const settings::Debugging *pDbg,
8863 const settings::Autostart *pAutostart)
8864{
8865 AssertReturn(!i_isSessionMachine(), E_FAIL);
8866
8867 HRESULT rc = S_OK;
8868
8869 try
8870 {
8871 ComObjPtr<GuestOSType> pGuestOSType;
8872 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8873 pGuestOSType);
8874 if (FAILED(rc))
8875 return rc;
8876
8877 /* The hardware version attribute (optional). */
8878 mHWData->mHWVersion = data.strVersion;
8879 mHWData->mHardwareUUID = data.uuid;
8880
8881 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8882 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8883 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8884 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8885 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8886 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8887 mHWData->mPAEEnabled = data.fPAE;
8888 mHWData->mLongMode = data.enmLongMode;
8889 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8890 mHWData->mAPIC = data.fAPIC;
8891 mHWData->mX2APIC = data.fX2APIC;
8892 mHWData->mCPUCount = data.cCPUs;
8893 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8894 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8895 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8896 mHWData->mCpuProfile = data.strCpuProfile;
8897
8898 // cpu
8899 if (mHWData->mCPUHotPlugEnabled)
8900 {
8901 for (settings::CpuList::const_iterator
8902 it = data.llCpus.begin();
8903 it != data.llCpus.end();
8904 ++it)
8905 {
8906 const settings::Cpu &cpu = *it;
8907
8908 mHWData->mCPUAttached[cpu.ulId] = true;
8909 }
8910 }
8911
8912 // cpuid leafs
8913 for (settings::CpuIdLeafsList::const_iterator
8914 it = data.llCpuIdLeafs.begin();
8915 it != data.llCpuIdLeafs.end();
8916 ++it)
8917 {
8918 const settings::CpuIdLeaf &rLeaf= *it;
8919 if ( rLeaf.idx < UINT32_C(0x20)
8920 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8921 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8922 mHWData->mCpuIdLeafList.push_back(rLeaf);
8923 /* else: just ignore */
8924 }
8925
8926 mHWData->mMemorySize = data.ulMemorySizeMB;
8927 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8928
8929 // boot order
8930 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8931 {
8932 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8933 if (it == data.mapBootOrder.end())
8934 mHWData->mBootOrder[i] = DeviceType_Null;
8935 else
8936 mHWData->mBootOrder[i] = it->second;
8937 }
8938
8939 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8940 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8941 mHWData->mMonitorCount = data.cMonitors;
8942 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8943 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8944 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8945 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8946 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8947 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8948 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8949 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8950 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8951 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8952 if (!data.strVideoCaptureFile.isEmpty())
8953 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8954 else
8955 mHWData->mVideoCaptureFile.setNull();
8956 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
8957 mHWData->mFirmwareType = data.firmwareType;
8958 mHWData->mPointingHIDType = data.pointingHIDType;
8959 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8960 mHWData->mChipsetType = data.chipsetType;
8961 mHWData->mParavirtProvider = data.paravirtProvider;
8962 mHWData->mParavirtDebug = data.strParavirtDebug;
8963 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8964 mHWData->mHPETEnabled = data.fHPETEnabled;
8965
8966 /* VRDEServer */
8967 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8968 if (FAILED(rc)) return rc;
8969
8970 /* BIOS */
8971 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8972 if (FAILED(rc)) return rc;
8973
8974 // Bandwidth control (must come before network adapters)
8975 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8976 if (FAILED(rc)) return rc;
8977
8978 /* Shared folders */
8979 for (settings::USBControllerList::const_iterator
8980 it = data.usbSettings.llUSBControllers.begin();
8981 it != data.usbSettings.llUSBControllers.end();
8982 ++it)
8983 {
8984 const settings::USBController &settingsCtrl = *it;
8985 ComObjPtr<USBController> newCtrl;
8986
8987 newCtrl.createObject();
8988 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8989 mUSBControllers->push_back(newCtrl);
8990 }
8991
8992 /* USB device filters */
8993 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8994 if (FAILED(rc)) return rc;
8995
8996 // network adapters (establish array size first and apply defaults, to
8997 // ensure reading the same settings as we saved, since the list skips
8998 // adapters having defaults)
8999 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9000 size_t oldCount = mNetworkAdapters.size();
9001 if (newCount > oldCount)
9002 {
9003 mNetworkAdapters.resize(newCount);
9004 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9005 {
9006 unconst(mNetworkAdapters[slot]).createObject();
9007 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9008 }
9009 }
9010 else if (newCount < oldCount)
9011 mNetworkAdapters.resize(newCount);
9012 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9013 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9014 for (settings::NetworkAdaptersList::const_iterator
9015 it = data.llNetworkAdapters.begin();
9016 it != data.llNetworkAdapters.end();
9017 ++it)
9018 {
9019 const settings::NetworkAdapter &nic = *it;
9020
9021 /* slot uniqueness is guaranteed by XML Schema */
9022 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9023 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9024 if (FAILED(rc)) return rc;
9025 }
9026
9027 // serial ports (establish defaults first, to ensure reading the same
9028 // settings as we saved, since the list skips ports having defaults)
9029 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9030 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9031 for (settings::SerialPortsList::const_iterator
9032 it = data.llSerialPorts.begin();
9033 it != data.llSerialPorts.end();
9034 ++it)
9035 {
9036 const settings::SerialPort &s = *it;
9037
9038 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9039 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9040 if (FAILED(rc)) return rc;
9041 }
9042
9043 // parallel ports (establish defaults first, to ensure reading the same
9044 // settings as we saved, since the list skips ports having defaults)
9045 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9046 mParallelPorts[i]->i_applyDefaults();
9047 for (settings::ParallelPortsList::const_iterator
9048 it = data.llParallelPorts.begin();
9049 it != data.llParallelPorts.end();
9050 ++it)
9051 {
9052 const settings::ParallelPort &p = *it;
9053
9054 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9055 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9056 if (FAILED(rc)) return rc;
9057 }
9058
9059 /* AudioAdapter */
9060 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9061 if (FAILED(rc)) return rc;
9062
9063 /* storage controllers */
9064 rc = i_loadStorageControllers(data.storage,
9065 puuidRegistry,
9066 puuidSnapshot);
9067 if (FAILED(rc)) return rc;
9068
9069 /* Shared folders */
9070 for (settings::SharedFoldersList::const_iterator
9071 it = data.llSharedFolders.begin();
9072 it != data.llSharedFolders.end();
9073 ++it)
9074 {
9075 const settings::SharedFolder &sf = *it;
9076
9077 ComObjPtr<SharedFolder> sharedFolder;
9078 /* Check for double entries. Not allowed! */
9079 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9080 if (SUCCEEDED(rc))
9081 return setError(VBOX_E_OBJECT_IN_USE,
9082 tr("Shared folder named '%s' already exists"),
9083 sf.strName.c_str());
9084
9085 /* Create the new shared folder. Don't break on error. This will be
9086 * reported when the machine starts. */
9087 sharedFolder.createObject();
9088 rc = sharedFolder->init(i_getMachine(),
9089 sf.strName,
9090 sf.strHostPath,
9091 RT_BOOL(sf.fWritable),
9092 RT_BOOL(sf.fAutoMount),
9093 false /* fFailOnError */);
9094 if (FAILED(rc)) return rc;
9095 mHWData->mSharedFolders.push_back(sharedFolder);
9096 }
9097
9098 // Clipboard
9099 mHWData->mClipboardMode = data.clipboardMode;
9100
9101 // drag'n'drop
9102 mHWData->mDnDMode = data.dndMode;
9103
9104 // guest settings
9105 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9106
9107 // IO settings
9108 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9109 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9110
9111 // Host PCI devices
9112 for (settings::HostPCIDeviceAttachmentList::const_iterator
9113 it = data.pciAttachments.begin();
9114 it != data.pciAttachments.end();
9115 ++it)
9116 {
9117 const settings::HostPCIDeviceAttachment &hpda = *it;
9118 ComObjPtr<PCIDeviceAttachment> pda;
9119
9120 pda.createObject();
9121 pda->i_loadSettings(this, hpda);
9122 mHWData->mPCIDeviceAssignments.push_back(pda);
9123 }
9124
9125 /*
9126 * (The following isn't really real hardware, but it lives in HWData
9127 * for reasons of convenience.)
9128 */
9129
9130#ifdef VBOX_WITH_GUEST_PROPS
9131 /* Guest properties (optional) */
9132
9133 /* Only load transient guest properties for configs which have saved
9134 * state, because there shouldn't be any for powered off VMs. The same
9135 * logic applies for snapshots, as offline snapshots shouldn't have
9136 * any such properties. They confuse the code in various places.
9137 * Note: can't rely on the machine state, as it isn't set yet. */
9138 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9139 /* apologies for the hacky unconst() usage, but this needs hacking
9140 * actually inconsistent settings into consistency, otherwise there
9141 * will be some corner cases where the inconsistency survives
9142 * surprisingly long without getting fixed, especially for snapshots
9143 * as there are no config changes. */
9144 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9145 for (settings::GuestPropertiesList::iterator
9146 it = llGuestProperties.begin();
9147 it != llGuestProperties.end();
9148 /*nothing*/)
9149 {
9150 const settings::GuestProperty &prop = *it;
9151 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9152 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9153 if ( fSkipTransientGuestProperties
9154 && ( fFlags & GUEST_PROP_F_TRANSIENT
9155 || fFlags & GUEST_PROP_F_TRANSRESET))
9156 {
9157 it = llGuestProperties.erase(it);
9158 continue;
9159 }
9160 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9161 mHWData->mGuestProperties[prop.strName] = property;
9162 ++it;
9163 }
9164#endif /* VBOX_WITH_GUEST_PROPS defined */
9165
9166 rc = i_loadDebugging(pDbg);
9167 if (FAILED(rc))
9168 return rc;
9169
9170 mHWData->mAutostart = *pAutostart;
9171
9172 /* default frontend */
9173 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9174 }
9175 catch (std::bad_alloc &)
9176 {
9177 return E_OUTOFMEMORY;
9178 }
9179
9180 AssertComRC(rc);
9181 return rc;
9182}
9183
9184/**
9185 * Called from i_loadHardware() to load the debugging settings of the
9186 * machine.
9187 *
9188 * @param pDbg Pointer to the settings.
9189 */
9190HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9191{
9192 mHWData->mDebugging = *pDbg;
9193 /* no more processing currently required, this will probably change. */
9194 return S_OK;
9195}
9196
9197/**
9198 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9199 *
9200 * @param data storage settings.
9201 * @param puuidRegistry media registry ID to set media to or NULL;
9202 * see Machine::i_loadMachineDataFromSettings()
9203 * @param puuidSnapshot snapshot ID
9204 * @return
9205 */
9206HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9207 const Guid *puuidRegistry,
9208 const Guid *puuidSnapshot)
9209{
9210 AssertReturn(!i_isSessionMachine(), E_FAIL);
9211
9212 HRESULT rc = S_OK;
9213
9214 for (settings::StorageControllersList::const_iterator
9215 it = data.llStorageControllers.begin();
9216 it != data.llStorageControllers.end();
9217 ++it)
9218 {
9219 const settings::StorageController &ctlData = *it;
9220
9221 ComObjPtr<StorageController> pCtl;
9222 /* Try to find one with the name first. */
9223 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9224 if (SUCCEEDED(rc))
9225 return setError(VBOX_E_OBJECT_IN_USE,
9226 tr("Storage controller named '%s' already exists"),
9227 ctlData.strName.c_str());
9228
9229 pCtl.createObject();
9230 rc = pCtl->init(this,
9231 ctlData.strName,
9232 ctlData.storageBus,
9233 ctlData.ulInstance,
9234 ctlData.fBootable);
9235 if (FAILED(rc)) return rc;
9236
9237 mStorageControllers->push_back(pCtl);
9238
9239 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9240 if (FAILED(rc)) return rc;
9241
9242 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9243 if (FAILED(rc)) return rc;
9244
9245 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9246 if (FAILED(rc)) return rc;
9247
9248 /* Load the attached devices now. */
9249 rc = i_loadStorageDevices(pCtl,
9250 ctlData,
9251 puuidRegistry,
9252 puuidSnapshot);
9253 if (FAILED(rc)) return rc;
9254 }
9255
9256 return S_OK;
9257}
9258
9259/**
9260 * Called from i_loadStorageControllers for a controller's devices.
9261 *
9262 * @param aStorageController
9263 * @param data
9264 * @param puuidRegistry media registry ID to set media to or NULL; see
9265 * Machine::i_loadMachineDataFromSettings()
9266 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9267 * @return
9268 */
9269HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9270 const settings::StorageController &data,
9271 const Guid *puuidRegistry,
9272 const Guid *puuidSnapshot)
9273{
9274 HRESULT rc = S_OK;
9275
9276 /* paranoia: detect duplicate attachments */
9277 for (settings::AttachedDevicesList::const_iterator
9278 it = data.llAttachedDevices.begin();
9279 it != data.llAttachedDevices.end();
9280 ++it)
9281 {
9282 const settings::AttachedDevice &ad = *it;
9283
9284 for (settings::AttachedDevicesList::const_iterator it2 = it;
9285 it2 != data.llAttachedDevices.end();
9286 ++it2)
9287 {
9288 if (it == it2)
9289 continue;
9290
9291 const settings::AttachedDevice &ad2 = *it2;
9292
9293 if ( ad.lPort == ad2.lPort
9294 && ad.lDevice == ad2.lDevice)
9295 {
9296 return setError(E_FAIL,
9297 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9298 aStorageController->i_getName().c_str(),
9299 ad.lPort,
9300 ad.lDevice,
9301 mUserData->s.strName.c_str());
9302 }
9303 }
9304 }
9305
9306 for (settings::AttachedDevicesList::const_iterator
9307 it = data.llAttachedDevices.begin();
9308 it != data.llAttachedDevices.end();
9309 ++it)
9310 {
9311 const settings::AttachedDevice &dev = *it;
9312 ComObjPtr<Medium> medium;
9313
9314 switch (dev.deviceType)
9315 {
9316 case DeviceType_Floppy:
9317 case DeviceType_DVD:
9318 if (dev.strHostDriveSrc.isNotEmpty())
9319 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9320 false /* fRefresh */, medium);
9321 else
9322 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9323 dev.uuid,
9324 false /* fRefresh */,
9325 false /* aSetError */,
9326 medium);
9327 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9328 // This is not an error. The host drive or UUID might have vanished, so just go
9329 // ahead without this removeable medium attachment
9330 rc = S_OK;
9331 break;
9332
9333 case DeviceType_HardDisk:
9334 {
9335 /* find a hard disk by UUID */
9336 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9337 if (FAILED(rc))
9338 {
9339 if (i_isSnapshotMachine())
9340 {
9341 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9342 // so the user knows that the bad disk is in a snapshot somewhere
9343 com::ErrorInfo info;
9344 return setError(E_FAIL,
9345 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9346 puuidSnapshot->raw(),
9347 info.getText().raw());
9348 }
9349 else
9350 return rc;
9351 }
9352
9353 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9354
9355 if (medium->i_getType() == MediumType_Immutable)
9356 {
9357 if (i_isSnapshotMachine())
9358 return setError(E_FAIL,
9359 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9360 "of the virtual machine '%s' ('%s')"),
9361 medium->i_getLocationFull().c_str(),
9362 dev.uuid.raw(),
9363 puuidSnapshot->raw(),
9364 mUserData->s.strName.c_str(),
9365 mData->m_strConfigFileFull.c_str());
9366
9367 return setError(E_FAIL,
9368 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9369 medium->i_getLocationFull().c_str(),
9370 dev.uuid.raw(),
9371 mUserData->s.strName.c_str(),
9372 mData->m_strConfigFileFull.c_str());
9373 }
9374
9375 if (medium->i_getType() == MediumType_MultiAttach)
9376 {
9377 if (i_isSnapshotMachine())
9378 return setError(E_FAIL,
9379 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9380 "of the virtual machine '%s' ('%s')"),
9381 medium->i_getLocationFull().c_str(),
9382 dev.uuid.raw(),
9383 puuidSnapshot->raw(),
9384 mUserData->s.strName.c_str(),
9385 mData->m_strConfigFileFull.c_str());
9386
9387 return setError(E_FAIL,
9388 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9389 medium->i_getLocationFull().c_str(),
9390 dev.uuid.raw(),
9391 mUserData->s.strName.c_str(),
9392 mData->m_strConfigFileFull.c_str());
9393 }
9394
9395 if ( !i_isSnapshotMachine()
9396 && medium->i_getChildren().size() != 0
9397 )
9398 return setError(E_FAIL,
9399 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9400 "because it has %d differencing child hard disks"),
9401 medium->i_getLocationFull().c_str(),
9402 dev.uuid.raw(),
9403 mUserData->s.strName.c_str(),
9404 mData->m_strConfigFileFull.c_str(),
9405 medium->i_getChildren().size());
9406
9407 if (i_findAttachment(*mMediumAttachments.data(),
9408 medium))
9409 return setError(E_FAIL,
9410 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9411 medium->i_getLocationFull().c_str(),
9412 dev.uuid.raw(),
9413 mUserData->s.strName.c_str(),
9414 mData->m_strConfigFileFull.c_str());
9415
9416 break;
9417 }
9418
9419 default:
9420 return setError(E_FAIL,
9421 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9422 medium->i_getLocationFull().c_str(),
9423 mUserData->s.strName.c_str(),
9424 mData->m_strConfigFileFull.c_str());
9425 }
9426
9427 if (FAILED(rc))
9428 break;
9429
9430 /* Bandwidth groups are loaded at this point. */
9431 ComObjPtr<BandwidthGroup> pBwGroup;
9432
9433 if (!dev.strBwGroup.isEmpty())
9434 {
9435 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9436 if (FAILED(rc))
9437 return setError(E_FAIL,
9438 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9439 medium->i_getLocationFull().c_str(),
9440 dev.strBwGroup.c_str(),
9441 mUserData->s.strName.c_str(),
9442 mData->m_strConfigFileFull.c_str());
9443 pBwGroup->i_reference();
9444 }
9445
9446 const Utf8Str controllerName = aStorageController->i_getName();
9447 ComObjPtr<MediumAttachment> pAttachment;
9448 pAttachment.createObject();
9449 rc = pAttachment->init(this,
9450 medium,
9451 controllerName,
9452 dev.lPort,
9453 dev.lDevice,
9454 dev.deviceType,
9455 false,
9456 dev.fPassThrough,
9457 dev.fTempEject,
9458 dev.fNonRotational,
9459 dev.fDiscard,
9460 dev.fHotPluggable,
9461 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9462 if (FAILED(rc)) break;
9463
9464 /* associate the medium with this machine and snapshot */
9465 if (!medium.isNull())
9466 {
9467 AutoCaller medCaller(medium);
9468 if (FAILED(medCaller.rc())) return medCaller.rc();
9469 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9470
9471 if (i_isSnapshotMachine())
9472 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9473 else
9474 rc = medium->i_addBackReference(mData->mUuid);
9475 /* If the medium->addBackReference fails it sets an appropriate
9476 * error message, so no need to do any guesswork here. */
9477
9478 if (puuidRegistry)
9479 // caller wants registry ID to be set on all attached media (OVF import case)
9480 medium->i_addRegistry(*puuidRegistry);
9481 }
9482
9483 if (FAILED(rc))
9484 break;
9485
9486 /* back up mMediumAttachments to let registeredInit() properly rollback
9487 * on failure (= limited accessibility) */
9488 i_setModified(IsModified_Storage);
9489 mMediumAttachments.backup();
9490 mMediumAttachments->push_back(pAttachment);
9491 }
9492
9493 return rc;
9494}
9495
9496/**
9497 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9498 *
9499 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9500 * @param aSnapshot where to return the found snapshot
9501 * @param aSetError true to set extended error info on failure
9502 */
9503HRESULT Machine::i_findSnapshotById(const Guid &aId,
9504 ComObjPtr<Snapshot> &aSnapshot,
9505 bool aSetError /* = false */)
9506{
9507 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9508
9509 if (!mData->mFirstSnapshot)
9510 {
9511 if (aSetError)
9512 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9513 return E_FAIL;
9514 }
9515
9516 if (aId.isZero())
9517 aSnapshot = mData->mFirstSnapshot;
9518 else
9519 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9520
9521 if (!aSnapshot)
9522 {
9523 if (aSetError)
9524 return setError(E_FAIL,
9525 tr("Could not find a snapshot with UUID {%s}"),
9526 aId.toString().c_str());
9527 return E_FAIL;
9528 }
9529
9530 return S_OK;
9531}
9532
9533/**
9534 * Returns the snapshot with the given name or fails of no such snapshot.
9535 *
9536 * @param strName snapshot name to find
9537 * @param aSnapshot where to return the found snapshot
9538 * @param aSetError true to set extended error info on failure
9539 */
9540HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9541 ComObjPtr<Snapshot> &aSnapshot,
9542 bool aSetError /* = false */)
9543{
9544 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9545
9546 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9547
9548 if (!mData->mFirstSnapshot)
9549 {
9550 if (aSetError)
9551 return setError(VBOX_E_OBJECT_NOT_FOUND,
9552 tr("This machine does not have any snapshots"));
9553 return VBOX_E_OBJECT_NOT_FOUND;
9554 }
9555
9556 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9557
9558 if (!aSnapshot)
9559 {
9560 if (aSetError)
9561 return setError(VBOX_E_OBJECT_NOT_FOUND,
9562 tr("Could not find a snapshot named '%s'"), strName.c_str());
9563 return VBOX_E_OBJECT_NOT_FOUND;
9564 }
9565
9566 return S_OK;
9567}
9568
9569/**
9570 * Returns a storage controller object with the given name.
9571 *
9572 * @param aName storage controller name to find
9573 * @param aStorageController where to return the found storage controller
9574 * @param aSetError true to set extended error info on failure
9575 */
9576HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9577 ComObjPtr<StorageController> &aStorageController,
9578 bool aSetError /* = false */)
9579{
9580 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9581
9582 for (StorageControllerList::const_iterator
9583 it = mStorageControllers->begin();
9584 it != mStorageControllers->end();
9585 ++it)
9586 {
9587 if ((*it)->i_getName() == aName)
9588 {
9589 aStorageController = (*it);
9590 return S_OK;
9591 }
9592 }
9593
9594 if (aSetError)
9595 return setError(VBOX_E_OBJECT_NOT_FOUND,
9596 tr("Could not find a storage controller named '%s'"),
9597 aName.c_str());
9598 return VBOX_E_OBJECT_NOT_FOUND;
9599}
9600
9601/**
9602 * Returns a USB controller object with the given name.
9603 *
9604 * @param aName USB controller name to find
9605 * @param aUSBController where to return the found USB controller
9606 * @param aSetError true to set extended error info on failure
9607 */
9608HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9609 ComObjPtr<USBController> &aUSBController,
9610 bool aSetError /* = false */)
9611{
9612 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9613
9614 for (USBControllerList::const_iterator
9615 it = mUSBControllers->begin();
9616 it != mUSBControllers->end();
9617 ++it)
9618 {
9619 if ((*it)->i_getName() == aName)
9620 {
9621 aUSBController = (*it);
9622 return S_OK;
9623 }
9624 }
9625
9626 if (aSetError)
9627 return setError(VBOX_E_OBJECT_NOT_FOUND,
9628 tr("Could not find a storage controller named '%s'"),
9629 aName.c_str());
9630 return VBOX_E_OBJECT_NOT_FOUND;
9631}
9632
9633/**
9634 * Returns the number of USB controller instance of the given type.
9635 *
9636 * @param enmType USB controller type.
9637 */
9638ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9639{
9640 ULONG cCtrls = 0;
9641
9642 for (USBControllerList::const_iterator
9643 it = mUSBControllers->begin();
9644 it != mUSBControllers->end();
9645 ++it)
9646 {
9647 if ((*it)->i_getControllerType() == enmType)
9648 cCtrls++;
9649 }
9650
9651 return cCtrls;
9652}
9653
9654HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9655 MediumAttachmentList &atts)
9656{
9657 AutoCaller autoCaller(this);
9658 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9659
9660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9661
9662 for (MediumAttachmentList::const_iterator
9663 it = mMediumAttachments->begin();
9664 it != mMediumAttachments->end();
9665 ++it)
9666 {
9667 const ComObjPtr<MediumAttachment> &pAtt = *it;
9668 // should never happen, but deal with NULL pointers in the list.
9669 AssertContinue(!pAtt.isNull());
9670
9671 // getControllerName() needs caller+read lock
9672 AutoCaller autoAttCaller(pAtt);
9673 if (FAILED(autoAttCaller.rc()))
9674 {
9675 atts.clear();
9676 return autoAttCaller.rc();
9677 }
9678 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9679
9680 if (pAtt->i_getControllerName() == aName)
9681 atts.push_back(pAtt);
9682 }
9683
9684 return S_OK;
9685}
9686
9687
9688/**
9689 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9690 * file if the machine name was changed and about creating a new settings file
9691 * if this is a new machine.
9692 *
9693 * @note Must be never called directly but only from #saveSettings().
9694 */
9695HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9696{
9697 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9698
9699 HRESULT rc = S_OK;
9700
9701 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9702
9703 /// @todo need to handle primary group change, too
9704
9705 /* attempt to rename the settings file if machine name is changed */
9706 if ( mUserData->s.fNameSync
9707 && mUserData.isBackedUp()
9708 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9709 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9710 )
9711 {
9712 bool dirRenamed = false;
9713 bool fileRenamed = false;
9714
9715 Utf8Str configFile, newConfigFile;
9716 Utf8Str configFilePrev, newConfigFilePrev;
9717 Utf8Str configDir, newConfigDir;
9718
9719 do
9720 {
9721 int vrc = VINF_SUCCESS;
9722
9723 Utf8Str name = mUserData.backedUpData()->s.strName;
9724 Utf8Str newName = mUserData->s.strName;
9725 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9726 if (group == "/")
9727 group.setNull();
9728 Utf8Str newGroup = mUserData->s.llGroups.front();
9729 if (newGroup == "/")
9730 newGroup.setNull();
9731
9732 configFile = mData->m_strConfigFileFull;
9733
9734 /* first, rename the directory if it matches the group and machine name */
9735 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9736 group.c_str(), RTPATH_DELIMITER, name.c_str());
9737 /** @todo hack, make somehow use of ComposeMachineFilename */
9738 if (mUserData->s.fDirectoryIncludesUUID)
9739 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9740 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9741 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9742 /** @todo hack, make somehow use of ComposeMachineFilename */
9743 if (mUserData->s.fDirectoryIncludesUUID)
9744 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9745 configDir = configFile;
9746 configDir.stripFilename();
9747 newConfigDir = configDir;
9748 if ( configDir.length() >= groupPlusName.length()
9749 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9750 groupPlusName.c_str()))
9751 {
9752 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9753 Utf8Str newConfigBaseDir(newConfigDir);
9754 newConfigDir.append(newGroupPlusName);
9755 /* consistency: use \ if appropriate on the platform */
9756 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9757 /* new dir and old dir cannot be equal here because of 'if'
9758 * above and because name != newName */
9759 Assert(configDir != newConfigDir);
9760 if (!fSettingsFileIsNew)
9761 {
9762 /* perform real rename only if the machine is not new */
9763 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9764 if ( vrc == VERR_FILE_NOT_FOUND
9765 || vrc == VERR_PATH_NOT_FOUND)
9766 {
9767 /* create the parent directory, then retry renaming */
9768 Utf8Str parent(newConfigDir);
9769 parent.stripFilename();
9770 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9771 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9772 }
9773 if (RT_FAILURE(vrc))
9774 {
9775 rc = setError(E_FAIL,
9776 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9777 configDir.c_str(),
9778 newConfigDir.c_str(),
9779 vrc);
9780 break;
9781 }
9782 /* delete subdirectories which are no longer needed */
9783 Utf8Str dir(configDir);
9784 dir.stripFilename();
9785 while (dir != newConfigBaseDir && dir != ".")
9786 {
9787 vrc = RTDirRemove(dir.c_str());
9788 if (RT_FAILURE(vrc))
9789 break;
9790 dir.stripFilename();
9791 }
9792 dirRenamed = true;
9793 }
9794 }
9795
9796 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9797 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9798
9799 /* then try to rename the settings file itself */
9800 if (newConfigFile != configFile)
9801 {
9802 /* get the path to old settings file in renamed directory */
9803 configFile = Utf8StrFmt("%s%c%s",
9804 newConfigDir.c_str(),
9805 RTPATH_DELIMITER,
9806 RTPathFilename(configFile.c_str()));
9807 if (!fSettingsFileIsNew)
9808 {
9809 /* perform real rename only if the machine is not new */
9810 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9811 if (RT_FAILURE(vrc))
9812 {
9813 rc = setError(E_FAIL,
9814 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9815 configFile.c_str(),
9816 newConfigFile.c_str(),
9817 vrc);
9818 break;
9819 }
9820 fileRenamed = true;
9821 configFilePrev = configFile;
9822 configFilePrev += "-prev";
9823 newConfigFilePrev = newConfigFile;
9824 newConfigFilePrev += "-prev";
9825 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9826 }
9827 }
9828
9829 // update m_strConfigFileFull amd mConfigFile
9830 mData->m_strConfigFileFull = newConfigFile;
9831 // compute the relative path too
9832 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9833
9834 // store the old and new so that VirtualBox::i_saveSettings() can update
9835 // the media registry
9836 if ( mData->mRegistered
9837 && (configDir != newConfigDir || configFile != newConfigFile))
9838 {
9839 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9840
9841 if (pfNeedsGlobalSaveSettings)
9842 *pfNeedsGlobalSaveSettings = true;
9843 }
9844
9845 // in the saved state file path, replace the old directory with the new directory
9846 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9847 {
9848 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9849 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9850 }
9851
9852 // and do the same thing for the saved state file paths of all the online snapshots
9853 if (mData->mFirstSnapshot)
9854 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9855 newConfigDir.c_str());
9856 }
9857 while (0);
9858
9859 if (FAILED(rc))
9860 {
9861 /* silently try to rename everything back */
9862 if (fileRenamed)
9863 {
9864 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9865 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9866 }
9867 if (dirRenamed)
9868 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9869 }
9870
9871 if (FAILED(rc)) return rc;
9872 }
9873
9874 if (fSettingsFileIsNew)
9875 {
9876 /* create a virgin config file */
9877 int vrc = VINF_SUCCESS;
9878
9879 /* ensure the settings directory exists */
9880 Utf8Str path(mData->m_strConfigFileFull);
9881 path.stripFilename();
9882 if (!RTDirExists(path.c_str()))
9883 {
9884 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9885 if (RT_FAILURE(vrc))
9886 {
9887 return setError(E_FAIL,
9888 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9889 path.c_str(),
9890 vrc);
9891 }
9892 }
9893
9894 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9895 path = Utf8Str(mData->m_strConfigFileFull);
9896 RTFILE f = NIL_RTFILE;
9897 vrc = RTFileOpen(&f, path.c_str(),
9898 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9899 if (RT_FAILURE(vrc))
9900 return setError(E_FAIL,
9901 tr("Could not create the settings file '%s' (%Rrc)"),
9902 path.c_str(),
9903 vrc);
9904 RTFileClose(f);
9905 }
9906
9907 return rc;
9908}
9909
9910/**
9911 * Saves and commits machine data, user data and hardware data.
9912 *
9913 * Note that on failure, the data remains uncommitted.
9914 *
9915 * @a aFlags may combine the following flags:
9916 *
9917 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9918 * Used when saving settings after an operation that makes them 100%
9919 * correspond to the settings from the current snapshot.
9920 * - SaveS_Force: settings will be saved without doing a deep compare of the
9921 * settings structures. This is used when this is called because snapshots
9922 * have changed to avoid the overhead of the deep compare.
9923 *
9924 * @note Must be called from under this object's write lock. Locks children for
9925 * writing.
9926 *
9927 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9928 * initialized to false and that will be set to true by this function if
9929 * the caller must invoke VirtualBox::i_saveSettings() because the global
9930 * settings have changed. This will happen if a machine rename has been
9931 * saved and the global machine and media registries will therefore need
9932 * updating.
9933 * @param aFlags Flags.
9934 */
9935HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9936 int aFlags /*= 0*/)
9937{
9938 LogFlowThisFuncEnter();
9939
9940 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9941
9942 /* make sure child objects are unable to modify the settings while we are
9943 * saving them */
9944 i_ensureNoStateDependencies();
9945
9946 AssertReturn(!i_isSnapshotMachine(),
9947 E_FAIL);
9948
9949 HRESULT rc = S_OK;
9950 bool fNeedsWrite = false;
9951
9952 /* First, prepare to save settings. It will care about renaming the
9953 * settings directory and file if the machine name was changed and about
9954 * creating a new settings file if this is a new machine. */
9955 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9956 if (FAILED(rc)) return rc;
9957
9958 // keep a pointer to the current settings structures
9959 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9960 settings::MachineConfigFile *pNewConfig = NULL;
9961
9962 try
9963 {
9964 // make a fresh one to have everyone write stuff into
9965 pNewConfig = new settings::MachineConfigFile(NULL);
9966 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9967
9968 // now go and copy all the settings data from COM to the settings structures
9969 // (this calls i_saveSettings() on all the COM objects in the machine)
9970 i_copyMachineDataToSettings(*pNewConfig);
9971
9972 if (aFlags & SaveS_ResetCurStateModified)
9973 {
9974 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9975 mData->mCurrentStateModified = FALSE;
9976 fNeedsWrite = true; // always, no need to compare
9977 }
9978 else if (aFlags & SaveS_Force)
9979 {
9980 fNeedsWrite = true; // always, no need to compare
9981 }
9982 else
9983 {
9984 if (!mData->mCurrentStateModified)
9985 {
9986 // do a deep compare of the settings that we just saved with the settings
9987 // previously stored in the config file; this invokes MachineConfigFile::operator==
9988 // which does a deep compare of all the settings, which is expensive but less expensive
9989 // than writing out XML in vain
9990 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9991
9992 // could still be modified if any settings changed
9993 mData->mCurrentStateModified = fAnySettingsChanged;
9994
9995 fNeedsWrite = fAnySettingsChanged;
9996 }
9997 else
9998 fNeedsWrite = true;
9999 }
10000
10001 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10002
10003 if (fNeedsWrite)
10004 // now spit it all out!
10005 pNewConfig->write(mData->m_strConfigFileFull);
10006
10007 mData->pMachineConfigFile = pNewConfig;
10008 delete pOldConfig;
10009 i_commit();
10010
10011 // after saving settings, we are no longer different from the XML on disk
10012 mData->flModifications = 0;
10013 }
10014 catch (HRESULT err)
10015 {
10016 // we assume that error info is set by the thrower
10017 rc = err;
10018
10019 // restore old config
10020 delete pNewConfig;
10021 mData->pMachineConfigFile = pOldConfig;
10022 }
10023 catch (...)
10024 {
10025 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10026 }
10027
10028 if (fNeedsWrite)
10029 {
10030 /* Fire the data change event, even on failure (since we've already
10031 * committed all data). This is done only for SessionMachines because
10032 * mutable Machine instances are always not registered (i.e. private
10033 * to the client process that creates them) and thus don't need to
10034 * inform callbacks. */
10035 if (i_isSessionMachine())
10036 mParent->i_onMachineDataChange(mData->mUuid);
10037 }
10038
10039 LogFlowThisFunc(("rc=%08X\n", rc));
10040 LogFlowThisFuncLeave();
10041 return rc;
10042}
10043
10044/**
10045 * Implementation for saving the machine settings into the given
10046 * settings::MachineConfigFile instance. This copies machine extradata
10047 * from the previous machine config file in the instance data, if any.
10048 *
10049 * This gets called from two locations:
10050 *
10051 * -- Machine::i_saveSettings(), during the regular XML writing;
10052 *
10053 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10054 * exported to OVF and we write the VirtualBox proprietary XML
10055 * into a <vbox:Machine> tag.
10056 *
10057 * This routine fills all the fields in there, including snapshots, *except*
10058 * for the following:
10059 *
10060 * -- fCurrentStateModified. There is some special logic associated with that.
10061 *
10062 * The caller can then call MachineConfigFile::write() or do something else
10063 * with it.
10064 *
10065 * Caller must hold the machine lock!
10066 *
10067 * This throws XML errors and HRESULT, so the caller must have a catch block!
10068 */
10069void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10070{
10071 // deep copy extradata, being extra careful with self assignment (the STL
10072 // map assignment on Mac OS X clang based Xcode isn't checking)
10073 if (&config != mData->pMachineConfigFile)
10074 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10075
10076 config.uuid = mData->mUuid;
10077
10078 // copy name, description, OS type, teleport, UTC etc.
10079 config.machineUserData = mUserData->s;
10080
10081 if ( mData->mMachineState == MachineState_Saved
10082 || mData->mMachineState == MachineState_Restoring
10083 // when doing certain snapshot operations we may or may not have
10084 // a saved state in the current state, so keep everything as is
10085 || ( ( mData->mMachineState == MachineState_Snapshotting
10086 || mData->mMachineState == MachineState_DeletingSnapshot
10087 || mData->mMachineState == MachineState_RestoringSnapshot)
10088 && (!mSSData->strStateFilePath.isEmpty())
10089 )
10090 )
10091 {
10092 Assert(!mSSData->strStateFilePath.isEmpty());
10093 /* try to make the file name relative to the settings file dir */
10094 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10095 }
10096 else
10097 {
10098 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10099 config.strStateFile.setNull();
10100 }
10101
10102 if (mData->mCurrentSnapshot)
10103 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10104 else
10105 config.uuidCurrentSnapshot.clear();
10106
10107 config.timeLastStateChange = mData->mLastStateChange;
10108 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10109 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10110
10111 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10112 if (FAILED(rc)) throw rc;
10113
10114 // save machine's media registry if this is VirtualBox 4.0 or later
10115 if (config.canHaveOwnMediaRegistry())
10116 {
10117 // determine machine folder
10118 Utf8Str strMachineFolder = i_getSettingsFileFull();
10119 strMachineFolder.stripFilename();
10120 mParent->i_saveMediaRegistry(config.mediaRegistry,
10121 i_getId(), // only media with registry ID == machine UUID
10122 strMachineFolder);
10123 // this throws HRESULT
10124 }
10125
10126 // save snapshots
10127 rc = i_saveAllSnapshots(config);
10128 if (FAILED(rc)) throw rc;
10129}
10130
10131/**
10132 * Saves all snapshots of the machine into the given machine config file. Called
10133 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10134 * @param config
10135 * @return
10136 */
10137HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10138{
10139 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10140
10141 HRESULT rc = S_OK;
10142
10143 try
10144 {
10145 config.llFirstSnapshot.clear();
10146
10147 if (mData->mFirstSnapshot)
10148 {
10149 // the settings use a list for "the first snapshot"
10150 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10151
10152 // get reference to the snapshot on the list and work on that
10153 // element straight in the list to avoid excessive copying later
10154 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10155 if (FAILED(rc)) throw rc;
10156 }
10157
10158// if (mType == IsSessionMachine)
10159// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10160
10161 }
10162 catch (HRESULT err)
10163 {
10164 /* we assume that error info is set by the thrower */
10165 rc = err;
10166 }
10167 catch (...)
10168 {
10169 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10170 }
10171
10172 return rc;
10173}
10174
10175/**
10176 * Saves the VM hardware configuration. It is assumed that the
10177 * given node is empty.
10178 *
10179 * @param data Reference to the settings object for the hardware config.
10180 * @param pDbg Pointer to the settings object for the debugging config
10181 * which happens to live in mHWData.
10182 * @param pAutostart Pointer to the settings object for the autostart config
10183 * which happens to live in mHWData.
10184 */
10185HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10186 settings::Autostart *pAutostart)
10187{
10188 HRESULT rc = S_OK;
10189
10190 try
10191 {
10192 /* The hardware version attribute (optional).
10193 Automatically upgrade from 1 to current default hardware version
10194 when there is no saved state. (ugly!) */
10195 if ( mHWData->mHWVersion == "1"
10196 && mSSData->strStateFilePath.isEmpty()
10197 )
10198 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10199
10200 data.strVersion = mHWData->mHWVersion;
10201 data.uuid = mHWData->mHardwareUUID;
10202
10203 // CPU
10204 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10205 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10206 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10207 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10208 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10209 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10210 data.fPAE = !!mHWData->mPAEEnabled;
10211 data.enmLongMode = mHWData->mLongMode;
10212 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10213 data.fAPIC = !!mHWData->mAPIC;
10214 data.fX2APIC = !!mHWData->mX2APIC;
10215 data.cCPUs = mHWData->mCPUCount;
10216 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10217 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10218 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10219 data.strCpuProfile = mHWData->mCpuProfile;
10220
10221 data.llCpus.clear();
10222 if (data.fCpuHotPlug)
10223 {
10224 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10225 {
10226 if (mHWData->mCPUAttached[idx])
10227 {
10228 settings::Cpu cpu;
10229 cpu.ulId = idx;
10230 data.llCpus.push_back(cpu);
10231 }
10232 }
10233 }
10234
10235 /* Standard and Extended CPUID leafs. */
10236 data.llCpuIdLeafs.clear();
10237 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10238
10239 // memory
10240 data.ulMemorySizeMB = mHWData->mMemorySize;
10241 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10242
10243 // firmware
10244 data.firmwareType = mHWData->mFirmwareType;
10245
10246 // HID
10247 data.pointingHIDType = mHWData->mPointingHIDType;
10248 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10249
10250 // chipset
10251 data.chipsetType = mHWData->mChipsetType;
10252
10253 // paravirt
10254 data.paravirtProvider = mHWData->mParavirtProvider;
10255 data.strParavirtDebug = mHWData->mParavirtDebug;
10256
10257 // emulated USB card reader
10258 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10259
10260 // HPET
10261 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10262
10263 // boot order
10264 data.mapBootOrder.clear();
10265 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10266 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10267
10268 // display
10269 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10270 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10271 data.cMonitors = mHWData->mMonitorCount;
10272 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10273 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10274 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10275 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10276 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10277 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10278 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10279 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10280 {
10281 if (mHWData->maVideoCaptureScreens[i])
10282 ASMBitSet(&data.u64VideoCaptureScreens, i);
10283 else
10284 ASMBitClear(&data.u64VideoCaptureScreens, i);
10285 }
10286 /* store relative video capture file if possible */
10287 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10288 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10289
10290 /* VRDEServer settings (optional) */
10291 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10292 if (FAILED(rc)) throw rc;
10293
10294 /* BIOS (required) */
10295 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10296 if (FAILED(rc)) throw rc;
10297
10298 /* USB Controller (required) */
10299 data.usbSettings.llUSBControllers.clear();
10300 for (USBControllerList::const_iterator
10301 it = mUSBControllers->begin();
10302 it != mUSBControllers->end();
10303 ++it)
10304 {
10305 ComObjPtr<USBController> ctrl = *it;
10306 settings::USBController settingsCtrl;
10307
10308 settingsCtrl.strName = ctrl->i_getName();
10309 settingsCtrl.enmType = ctrl->i_getControllerType();
10310
10311 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10312 }
10313
10314 /* USB device filters (required) */
10315 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10316 if (FAILED(rc)) throw rc;
10317
10318 /* Network adapters (required) */
10319 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10320 data.llNetworkAdapters.clear();
10321 /* Write out only the nominal number of network adapters for this
10322 * chipset type. Since Machine::commit() hasn't been called there
10323 * may be extra NIC settings in the vector. */
10324 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10325 {
10326 settings::NetworkAdapter nic;
10327 nic.ulSlot = (uint32_t)slot;
10328 /* paranoia check... must not be NULL, but must not crash either. */
10329 if (mNetworkAdapters[slot])
10330 {
10331 if (mNetworkAdapters[slot]->i_hasDefaults())
10332 continue;
10333
10334 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10335 if (FAILED(rc)) throw rc;
10336
10337 data.llNetworkAdapters.push_back(nic);
10338 }
10339 }
10340
10341 /* Serial ports */
10342 data.llSerialPorts.clear();
10343 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10344 {
10345 if (mSerialPorts[slot]->i_hasDefaults())
10346 continue;
10347
10348 settings::SerialPort s;
10349 s.ulSlot = slot;
10350 rc = mSerialPorts[slot]->i_saveSettings(s);
10351 if (FAILED(rc)) return rc;
10352
10353 data.llSerialPorts.push_back(s);
10354 }
10355
10356 /* Parallel ports */
10357 data.llParallelPorts.clear();
10358 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10359 {
10360 if (mParallelPorts[slot]->i_hasDefaults())
10361 continue;
10362
10363 settings::ParallelPort p;
10364 p.ulSlot = slot;
10365 rc = mParallelPorts[slot]->i_saveSettings(p);
10366 if (FAILED(rc)) return rc;
10367
10368 data.llParallelPorts.push_back(p);
10369 }
10370
10371 /* Audio adapter */
10372 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10373 if (FAILED(rc)) return rc;
10374
10375 rc = i_saveStorageControllers(data.storage);
10376 if (FAILED(rc)) return rc;
10377
10378 /* Shared folders */
10379 data.llSharedFolders.clear();
10380 for (HWData::SharedFolderList::const_iterator
10381 it = mHWData->mSharedFolders.begin();
10382 it != mHWData->mSharedFolders.end();
10383 ++it)
10384 {
10385 SharedFolder *pSF = *it;
10386 AutoCaller sfCaller(pSF);
10387 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10388 settings::SharedFolder sf;
10389 sf.strName = pSF->i_getName();
10390 sf.strHostPath = pSF->i_getHostPath();
10391 sf.fWritable = !!pSF->i_isWritable();
10392 sf.fAutoMount = !!pSF->i_isAutoMounted();
10393
10394 data.llSharedFolders.push_back(sf);
10395 }
10396
10397 // clipboard
10398 data.clipboardMode = mHWData->mClipboardMode;
10399
10400 // drag'n'drop
10401 data.dndMode = mHWData->mDnDMode;
10402
10403 /* Guest */
10404 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10405
10406 // IO settings
10407 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10408 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10409
10410 /* BandwidthControl (required) */
10411 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10412 if (FAILED(rc)) throw rc;
10413
10414 /* Host PCI devices */
10415 data.pciAttachments.clear();
10416 for (HWData::PCIDeviceAssignmentList::const_iterator
10417 it = mHWData->mPCIDeviceAssignments.begin();
10418 it != mHWData->mPCIDeviceAssignments.end();
10419 ++it)
10420 {
10421 ComObjPtr<PCIDeviceAttachment> pda = *it;
10422 settings::HostPCIDeviceAttachment hpda;
10423
10424 rc = pda->i_saveSettings(hpda);
10425 if (FAILED(rc)) throw rc;
10426
10427 data.pciAttachments.push_back(hpda);
10428 }
10429
10430 // guest properties
10431 data.llGuestProperties.clear();
10432#ifdef VBOX_WITH_GUEST_PROPS
10433 for (HWData::GuestPropertyMap::const_iterator
10434 it = mHWData->mGuestProperties.begin();
10435 it != mHWData->mGuestProperties.end();
10436 ++it)
10437 {
10438 HWData::GuestProperty property = it->second;
10439
10440 /* Remove transient guest properties at shutdown unless we
10441 * are saving state. Note that restoring snapshot intentionally
10442 * keeps them, they will be removed if appropriate once the final
10443 * machine state is set (as crashes etc. need to work). */
10444 if ( ( mData->mMachineState == MachineState_PoweredOff
10445 || mData->mMachineState == MachineState_Aborted
10446 || mData->mMachineState == MachineState_Teleported)
10447 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10448 continue;
10449 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10450 prop.strName = it->first;
10451 prop.strValue = property.strValue;
10452 prop.timestamp = property.mTimestamp;
10453 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10454 GuestPropWriteFlags(property.mFlags, szFlags, sizeof(szFlags));
10455 prop.strFlags = szFlags;
10456
10457 data.llGuestProperties.push_back(prop);
10458 }
10459
10460 /* I presume this doesn't require a backup(). */
10461 mData->mGuestPropertiesModified = FALSE;
10462#endif /* VBOX_WITH_GUEST_PROPS defined */
10463
10464 *pDbg = mHWData->mDebugging;
10465 *pAutostart = mHWData->mAutostart;
10466
10467 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10468 }
10469 catch (std::bad_alloc &)
10470 {
10471 return E_OUTOFMEMORY;
10472 }
10473
10474 AssertComRC(rc);
10475 return rc;
10476}
10477
10478/**
10479 * Saves the storage controller configuration.
10480 *
10481 * @param data storage settings.
10482 */
10483HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10484{
10485 data.llStorageControllers.clear();
10486
10487 for (StorageControllerList::const_iterator
10488 it = mStorageControllers->begin();
10489 it != mStorageControllers->end();
10490 ++it)
10491 {
10492 HRESULT rc;
10493 ComObjPtr<StorageController> pCtl = *it;
10494
10495 settings::StorageController ctl;
10496 ctl.strName = pCtl->i_getName();
10497 ctl.controllerType = pCtl->i_getControllerType();
10498 ctl.storageBus = pCtl->i_getStorageBus();
10499 ctl.ulInstance = pCtl->i_getInstance();
10500 ctl.fBootable = pCtl->i_getBootable();
10501
10502 /* Save the port count. */
10503 ULONG portCount;
10504 rc = pCtl->COMGETTER(PortCount)(&portCount);
10505 ComAssertComRCRet(rc, rc);
10506 ctl.ulPortCount = portCount;
10507
10508 /* Save fUseHostIOCache */
10509 BOOL fUseHostIOCache;
10510 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10511 ComAssertComRCRet(rc, rc);
10512 ctl.fUseHostIOCache = !!fUseHostIOCache;
10513
10514 /* save the devices now. */
10515 rc = i_saveStorageDevices(pCtl, ctl);
10516 ComAssertComRCRet(rc, rc);
10517
10518 data.llStorageControllers.push_back(ctl);
10519 }
10520
10521 return S_OK;
10522}
10523
10524/**
10525 * Saves the hard disk configuration.
10526 */
10527HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10528 settings::StorageController &data)
10529{
10530 MediumAttachmentList atts;
10531
10532 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10533 if (FAILED(rc)) return rc;
10534
10535 data.llAttachedDevices.clear();
10536 for (MediumAttachmentList::const_iterator
10537 it = atts.begin();
10538 it != atts.end();
10539 ++it)
10540 {
10541 settings::AttachedDevice dev;
10542 IMediumAttachment *iA = *it;
10543 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10544 Medium *pMedium = pAttach->i_getMedium();
10545
10546 dev.deviceType = pAttach->i_getType();
10547 dev.lPort = pAttach->i_getPort();
10548 dev.lDevice = pAttach->i_getDevice();
10549 dev.fPassThrough = pAttach->i_getPassthrough();
10550 dev.fHotPluggable = pAttach->i_getHotPluggable();
10551 if (pMedium)
10552 {
10553 if (pMedium->i_isHostDrive())
10554 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10555 else
10556 dev.uuid = pMedium->i_getId();
10557 dev.fTempEject = pAttach->i_getTempEject();
10558 dev.fNonRotational = pAttach->i_getNonRotational();
10559 dev.fDiscard = pAttach->i_getDiscard();
10560 }
10561
10562 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10563
10564 data.llAttachedDevices.push_back(dev);
10565 }
10566
10567 return S_OK;
10568}
10569
10570/**
10571 * Saves machine state settings as defined by aFlags
10572 * (SaveSTS_* values).
10573 *
10574 * @param aFlags Combination of SaveSTS_* flags.
10575 *
10576 * @note Locks objects for writing.
10577 */
10578HRESULT Machine::i_saveStateSettings(int aFlags)
10579{
10580 if (aFlags == 0)
10581 return S_OK;
10582
10583 AutoCaller autoCaller(this);
10584 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10585
10586 /* This object's write lock is also necessary to serialize file access
10587 * (prevent concurrent reads and writes) */
10588 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10589
10590 HRESULT rc = S_OK;
10591
10592 Assert(mData->pMachineConfigFile);
10593
10594 try
10595 {
10596 if (aFlags & SaveSTS_CurStateModified)
10597 mData->pMachineConfigFile->fCurrentStateModified = true;
10598
10599 if (aFlags & SaveSTS_StateFilePath)
10600 {
10601 if (!mSSData->strStateFilePath.isEmpty())
10602 /* try to make the file name relative to the settings file dir */
10603 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10604 else
10605 mData->pMachineConfigFile->strStateFile.setNull();
10606 }
10607
10608 if (aFlags & SaveSTS_StateTimeStamp)
10609 {
10610 Assert( mData->mMachineState != MachineState_Aborted
10611 || mSSData->strStateFilePath.isEmpty());
10612
10613 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10614
10615 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10616/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10617 }
10618
10619 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10620 }
10621 catch (...)
10622 {
10623 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10624 }
10625
10626 return rc;
10627}
10628
10629/**
10630 * Ensures that the given medium is added to a media registry. If this machine
10631 * was created with 4.0 or later, then the machine registry is used. Otherwise
10632 * the global VirtualBox media registry is used.
10633 *
10634 * Caller must NOT hold machine lock, media tree or any medium locks!
10635 *
10636 * @param pMedium
10637 */
10638void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10639{
10640 /* Paranoia checks: do not hold machine or media tree locks. */
10641 AssertReturnVoid(!isWriteLockOnCurrentThread());
10642 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10643
10644 ComObjPtr<Medium> pBase;
10645 {
10646 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10647 pBase = pMedium->i_getBase();
10648 }
10649
10650 /* Paranoia checks: do not hold medium locks. */
10651 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10652 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10653
10654 // decide which medium registry to use now that the medium is attached:
10655 Guid uuid;
10656 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10657 // machine XML is VirtualBox 4.0 or higher:
10658 uuid = i_getId(); // machine UUID
10659 else
10660 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10661
10662 if (pMedium->i_addRegistry(uuid))
10663 mParent->i_markRegistryModified(uuid);
10664
10665 /* For more complex hard disk structures it can happen that the base
10666 * medium isn't yet associated with any medium registry. Do that now. */
10667 if (pMedium != pBase)
10668 {
10669 /* Tree lock needed by Medium::addRegistry when recursing. */
10670 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10671 if (pBase->i_addRegistryRecursive(uuid))
10672 {
10673 treeLock.release();
10674 mParent->i_markRegistryModified(uuid);
10675 }
10676 }
10677}
10678
10679/**
10680 * Creates differencing hard disks for all normal hard disks attached to this
10681 * machine and a new set of attachments to refer to created disks.
10682 *
10683 * Used when taking a snapshot or when deleting the current state. Gets called
10684 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10685 *
10686 * This method assumes that mMediumAttachments contains the original hard disk
10687 * attachments it needs to create diffs for. On success, these attachments will
10688 * be replaced with the created diffs.
10689 *
10690 * Attachments with non-normal hard disks are left as is.
10691 *
10692 * If @a aOnline is @c false then the original hard disks that require implicit
10693 * diffs will be locked for reading. Otherwise it is assumed that they are
10694 * already locked for writing (when the VM was started). Note that in the latter
10695 * case it is responsibility of the caller to lock the newly created diffs for
10696 * writing if this method succeeds.
10697 *
10698 * @param aProgress Progress object to run (must contain at least as
10699 * many operations left as the number of hard disks
10700 * attached).
10701 * @param aWeight Weight of this operation.
10702 * @param aOnline Whether the VM was online prior to this operation.
10703 *
10704 * @note The progress object is not marked as completed, neither on success nor
10705 * on failure. This is a responsibility of the caller.
10706 *
10707 * @note Locks this object and the media tree for writing.
10708 */
10709HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10710 ULONG aWeight,
10711 bool aOnline)
10712{
10713 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10714
10715 AutoCaller autoCaller(this);
10716 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10717
10718 AutoMultiWriteLock2 alock(this->lockHandle(),
10719 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10720
10721 /* must be in a protective state because we release the lock below */
10722 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10723 || mData->mMachineState == MachineState_OnlineSnapshotting
10724 || mData->mMachineState == MachineState_LiveSnapshotting
10725 || mData->mMachineState == MachineState_RestoringSnapshot
10726 || mData->mMachineState == MachineState_DeletingSnapshot
10727 , E_FAIL);
10728
10729 HRESULT rc = S_OK;
10730
10731 // use appropriate locked media map (online or offline)
10732 MediumLockListMap lockedMediaOffline;
10733 MediumLockListMap *lockedMediaMap;
10734 if (aOnline)
10735 lockedMediaMap = &mData->mSession.mLockedMedia;
10736 else
10737 lockedMediaMap = &lockedMediaOffline;
10738
10739 try
10740 {
10741 if (!aOnline)
10742 {
10743 /* lock all attached hard disks early to detect "in use"
10744 * situations before creating actual diffs */
10745 for (MediumAttachmentList::const_iterator
10746 it = mMediumAttachments->begin();
10747 it != mMediumAttachments->end();
10748 ++it)
10749 {
10750 MediumAttachment *pAtt = *it;
10751 if (pAtt->i_getType() == DeviceType_HardDisk)
10752 {
10753 Medium *pMedium = pAtt->i_getMedium();
10754 Assert(pMedium);
10755
10756 MediumLockList *pMediumLockList(new MediumLockList());
10757 alock.release();
10758 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10759 NULL /* pToLockWrite */,
10760 false /* fMediumLockWriteAll */,
10761 NULL,
10762 *pMediumLockList);
10763 alock.acquire();
10764 if (FAILED(rc))
10765 {
10766 delete pMediumLockList;
10767 throw rc;
10768 }
10769 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10770 if (FAILED(rc))
10771 {
10772 throw setError(rc,
10773 tr("Collecting locking information for all attached media failed"));
10774 }
10775 }
10776 }
10777
10778 /* Now lock all media. If this fails, nothing is locked. */
10779 alock.release();
10780 rc = lockedMediaMap->Lock();
10781 alock.acquire();
10782 if (FAILED(rc))
10783 {
10784 throw setError(rc,
10785 tr("Locking of attached media failed"));
10786 }
10787 }
10788
10789 /* remember the current list (note that we don't use backup() since
10790 * mMediumAttachments may be already backed up) */
10791 MediumAttachmentList atts = *mMediumAttachments.data();
10792
10793 /* start from scratch */
10794 mMediumAttachments->clear();
10795
10796 /* go through remembered attachments and create diffs for normal hard
10797 * disks and attach them */
10798 for (MediumAttachmentList::const_iterator
10799 it = atts.begin();
10800 it != atts.end();
10801 ++it)
10802 {
10803 MediumAttachment *pAtt = *it;
10804
10805 DeviceType_T devType = pAtt->i_getType();
10806 Medium *pMedium = pAtt->i_getMedium();
10807
10808 if ( devType != DeviceType_HardDisk
10809 || pMedium == NULL
10810 || pMedium->i_getType() != MediumType_Normal)
10811 {
10812 /* copy the attachment as is */
10813
10814 /** @todo the progress object created in SessionMachine::TakeSnaphot
10815 * only expects operations for hard disks. Later other
10816 * device types need to show up in the progress as well. */
10817 if (devType == DeviceType_HardDisk)
10818 {
10819 if (pMedium == NULL)
10820 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10821 aWeight); // weight
10822 else
10823 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10824 pMedium->i_getBase()->i_getName().c_str()).raw(),
10825 aWeight); // weight
10826 }
10827
10828 mMediumAttachments->push_back(pAtt);
10829 continue;
10830 }
10831
10832 /* need a diff */
10833 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10834 pMedium->i_getBase()->i_getName().c_str()).raw(),
10835 aWeight); // weight
10836
10837 Utf8Str strFullSnapshotFolder;
10838 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10839
10840 ComObjPtr<Medium> diff;
10841 diff.createObject();
10842 // store the diff in the same registry as the parent
10843 // (this cannot fail here because we can't create implicit diffs for
10844 // unregistered images)
10845 Guid uuidRegistryParent;
10846 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10847 Assert(fInRegistry); NOREF(fInRegistry);
10848 rc = diff->init(mParent,
10849 pMedium->i_getPreferredDiffFormat(),
10850 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10851 uuidRegistryParent,
10852 DeviceType_HardDisk);
10853 if (FAILED(rc)) throw rc;
10854
10855 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10856 * the push_back? Looks like we're going to release medium with the
10857 * wrong kind of lock (general issue with if we fail anywhere at all)
10858 * and an orphaned VDI in the snapshots folder. */
10859
10860 /* update the appropriate lock list */
10861 MediumLockList *pMediumLockList;
10862 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10863 AssertComRCThrowRC(rc);
10864 if (aOnline)
10865 {
10866 alock.release();
10867 /* The currently attached medium will be read-only, change
10868 * the lock type to read. */
10869 rc = pMediumLockList->Update(pMedium, false);
10870 alock.acquire();
10871 AssertComRCThrowRC(rc);
10872 }
10873
10874 /* release the locks before the potentially lengthy operation */
10875 alock.release();
10876 rc = pMedium->i_createDiffStorage(diff,
10877 pMedium->i_getPreferredDiffVariant(),
10878 pMediumLockList,
10879 NULL /* aProgress */,
10880 true /* aWait */);
10881 alock.acquire();
10882 if (FAILED(rc)) throw rc;
10883
10884 /* actual lock list update is done in Machine::i_commitMedia */
10885
10886 rc = diff->i_addBackReference(mData->mUuid);
10887 AssertComRCThrowRC(rc);
10888
10889 /* add a new attachment */
10890 ComObjPtr<MediumAttachment> attachment;
10891 attachment.createObject();
10892 rc = attachment->init(this,
10893 diff,
10894 pAtt->i_getControllerName(),
10895 pAtt->i_getPort(),
10896 pAtt->i_getDevice(),
10897 DeviceType_HardDisk,
10898 true /* aImplicit */,
10899 false /* aPassthrough */,
10900 false /* aTempEject */,
10901 pAtt->i_getNonRotational(),
10902 pAtt->i_getDiscard(),
10903 pAtt->i_getHotPluggable(),
10904 pAtt->i_getBandwidthGroup());
10905 if (FAILED(rc)) throw rc;
10906
10907 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10908 AssertComRCThrowRC(rc);
10909 mMediumAttachments->push_back(attachment);
10910 }
10911 }
10912 catch (HRESULT aRC) { rc = aRC; }
10913
10914 /* unlock all hard disks we locked when there is no VM */
10915 if (!aOnline)
10916 {
10917 ErrorInfoKeeper eik;
10918
10919 HRESULT rc1 = lockedMediaMap->Clear();
10920 AssertComRC(rc1);
10921 }
10922
10923 return rc;
10924}
10925
10926/**
10927 * Deletes implicit differencing hard disks created either by
10928 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10929 * mMediumAttachments.
10930 *
10931 * Note that to delete hard disks created by #attachDevice() this method is
10932 * called from #i_rollbackMedia() when the changes are rolled back.
10933 *
10934 * @note Locks this object and the media tree for writing.
10935 */
10936HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10937{
10938 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10939
10940 AutoCaller autoCaller(this);
10941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10942
10943 AutoMultiWriteLock2 alock(this->lockHandle(),
10944 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10945
10946 /* We absolutely must have backed up state. */
10947 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10948
10949 /* Check if there are any implicitly created diff images. */
10950 bool fImplicitDiffs = false;
10951 for (MediumAttachmentList::const_iterator
10952 it = mMediumAttachments->begin();
10953 it != mMediumAttachments->end();
10954 ++it)
10955 {
10956 const ComObjPtr<MediumAttachment> &pAtt = *it;
10957 if (pAtt->i_isImplicit())
10958 {
10959 fImplicitDiffs = true;
10960 break;
10961 }
10962 }
10963 /* If there is nothing to do, leave early. This saves lots of image locking
10964 * effort. It also avoids a MachineStateChanged event without real reason.
10965 * This is important e.g. when loading a VM config, because there should be
10966 * no events. Otherwise API clients can become thoroughly confused for
10967 * inaccessible VMs (the code for loading VM configs uses this method for
10968 * cleanup if the config makes no sense), as they take such events as an
10969 * indication that the VM is alive, and they would force the VM config to
10970 * be reread, leading to an endless loop. */
10971 if (!fImplicitDiffs)
10972 return S_OK;
10973
10974 HRESULT rc = S_OK;
10975 MachineState_T oldState = mData->mMachineState;
10976
10977 /* will release the lock before the potentially lengthy operation,
10978 * so protect with the special state (unless already protected) */
10979 if ( oldState != MachineState_Snapshotting
10980 && oldState != MachineState_OnlineSnapshotting
10981 && oldState != MachineState_LiveSnapshotting
10982 && oldState != MachineState_RestoringSnapshot
10983 && oldState != MachineState_DeletingSnapshot
10984 && oldState != MachineState_DeletingSnapshotOnline
10985 && oldState != MachineState_DeletingSnapshotPaused
10986 )
10987 i_setMachineState(MachineState_SettingUp);
10988
10989 // use appropriate locked media map (online or offline)
10990 MediumLockListMap lockedMediaOffline;
10991 MediumLockListMap *lockedMediaMap;
10992 if (aOnline)
10993 lockedMediaMap = &mData->mSession.mLockedMedia;
10994 else
10995 lockedMediaMap = &lockedMediaOffline;
10996
10997 try
10998 {
10999 if (!aOnline)
11000 {
11001 /* lock all attached hard disks early to detect "in use"
11002 * situations before deleting actual diffs */
11003 for (MediumAttachmentList::const_iterator
11004 it = mMediumAttachments->begin();
11005 it != mMediumAttachments->end();
11006 ++it)
11007 {
11008 MediumAttachment *pAtt = *it;
11009 if (pAtt->i_getType() == DeviceType_HardDisk)
11010 {
11011 Medium *pMedium = pAtt->i_getMedium();
11012 Assert(pMedium);
11013
11014 MediumLockList *pMediumLockList(new MediumLockList());
11015 alock.release();
11016 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11017 NULL /* pToLockWrite */,
11018 false /* fMediumLockWriteAll */,
11019 NULL,
11020 *pMediumLockList);
11021 alock.acquire();
11022
11023 if (FAILED(rc))
11024 {
11025 delete pMediumLockList;
11026 throw rc;
11027 }
11028
11029 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11030 if (FAILED(rc))
11031 throw rc;
11032 }
11033 }
11034
11035 if (FAILED(rc))
11036 throw rc;
11037 } // end of offline
11038
11039 /* Lock lists are now up to date and include implicitly created media */
11040
11041 /* Go through remembered attachments and delete all implicitly created
11042 * diffs and fix up the attachment information */
11043 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11044 MediumAttachmentList implicitAtts;
11045 for (MediumAttachmentList::const_iterator
11046 it = mMediumAttachments->begin();
11047 it != mMediumAttachments->end();
11048 ++it)
11049 {
11050 ComObjPtr<MediumAttachment> pAtt = *it;
11051 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11052 if (pMedium.isNull())
11053 continue;
11054
11055 // Implicit attachments go on the list for deletion and back references are removed.
11056 if (pAtt->i_isImplicit())
11057 {
11058 /* Deassociate and mark for deletion */
11059 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11060 rc = pMedium->i_removeBackReference(mData->mUuid);
11061 if (FAILED(rc))
11062 throw rc;
11063 implicitAtts.push_back(pAtt);
11064 continue;
11065 }
11066
11067 /* Was this medium attached before? */
11068 if (!i_findAttachment(oldAtts, pMedium))
11069 {
11070 /* no: de-associate */
11071 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11072 rc = pMedium->i_removeBackReference(mData->mUuid);
11073 if (FAILED(rc))
11074 throw rc;
11075 continue;
11076 }
11077 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11078 }
11079
11080 /* If there are implicit attachments to delete, throw away the lock
11081 * map contents (which will unlock all media) since the medium
11082 * attachments will be rolled back. Below we need to completely
11083 * recreate the lock map anyway since it is infinitely complex to
11084 * do this incrementally (would need reconstructing each attachment
11085 * change, which would be extremely hairy). */
11086 if (implicitAtts.size() != 0)
11087 {
11088 ErrorInfoKeeper eik;
11089
11090 HRESULT rc1 = lockedMediaMap->Clear();
11091 AssertComRC(rc1);
11092 }
11093
11094 /* rollback hard disk changes */
11095 mMediumAttachments.rollback();
11096
11097 MultiResult mrc(S_OK);
11098
11099 // Delete unused implicit diffs.
11100 if (implicitAtts.size() != 0)
11101 {
11102 alock.release();
11103
11104 for (MediumAttachmentList::const_iterator
11105 it = implicitAtts.begin();
11106 it != implicitAtts.end();
11107 ++it)
11108 {
11109 // Remove medium associated with this attachment.
11110 ComObjPtr<MediumAttachment> pAtt = *it;
11111 Assert(pAtt);
11112 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11113 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11114 Assert(pMedium);
11115
11116 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11117 // continue on delete failure, just collect error messages
11118 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11119 pMedium->i_getLocationFull().c_str() ));
11120 mrc = rc;
11121 }
11122 // Clear the list of deleted implicit attachments now, while not
11123 // holding the lock, as it will ultimately trigger Medium::uninit()
11124 // calls which assume that the media tree lock isn't held.
11125 implicitAtts.clear();
11126
11127 alock.acquire();
11128
11129 /* if there is a VM recreate media lock map as mentioned above,
11130 * otherwise it is a waste of time and we leave things unlocked */
11131 if (aOnline)
11132 {
11133 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11134 /* must never be NULL, but better safe than sorry */
11135 if (!pMachine.isNull())
11136 {
11137 alock.release();
11138 rc = mData->mSession.mMachine->i_lockMedia();
11139 alock.acquire();
11140 if (FAILED(rc))
11141 throw rc;
11142 }
11143 }
11144 }
11145 }
11146 catch (HRESULT aRC) {rc = aRC;}
11147
11148 if (mData->mMachineState == MachineState_SettingUp)
11149 i_setMachineState(oldState);
11150
11151 /* unlock all hard disks we locked when there is no VM */
11152 if (!aOnline)
11153 {
11154 ErrorInfoKeeper eik;
11155
11156 HRESULT rc1 = lockedMediaMap->Clear();
11157 AssertComRC(rc1);
11158 }
11159
11160 return rc;
11161}
11162
11163
11164/**
11165 * Looks through the given list of media attachments for one with the given parameters
11166 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11167 * can be searched as well if needed.
11168 *
11169 * @param ll
11170 * @param aControllerName
11171 * @param aControllerPort
11172 * @param aDevice
11173 * @return
11174 */
11175MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11176 const Utf8Str &aControllerName,
11177 LONG aControllerPort,
11178 LONG aDevice)
11179{
11180 for (MediumAttachmentList::const_iterator
11181 it = ll.begin();
11182 it != ll.end();
11183 ++it)
11184 {
11185 MediumAttachment *pAttach = *it;
11186 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11187 return pAttach;
11188 }
11189
11190 return NULL;
11191}
11192
11193/**
11194 * Looks through the given list of media attachments for one with the given parameters
11195 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11196 * can be searched as well if needed.
11197 *
11198 * @param ll
11199 * @param pMedium
11200 * @return
11201 */
11202MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11203 ComObjPtr<Medium> pMedium)
11204{
11205 for (MediumAttachmentList::const_iterator
11206 it = ll.begin();
11207 it != ll.end();
11208 ++it)
11209 {
11210 MediumAttachment *pAttach = *it;
11211 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11212 if (pMediumThis == pMedium)
11213 return pAttach;
11214 }
11215
11216 return NULL;
11217}
11218
11219/**
11220 * Looks through the given list of media attachments for one with the given parameters
11221 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11222 * can be searched as well if needed.
11223 *
11224 * @param ll
11225 * @param id
11226 * @return
11227 */
11228MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11229 Guid &id)
11230{
11231 for (MediumAttachmentList::const_iterator
11232 it = ll.begin();
11233 it != ll.end();
11234 ++it)
11235 {
11236 MediumAttachment *pAttach = *it;
11237 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11238 if (pMediumThis->i_getId() == id)
11239 return pAttach;
11240 }
11241
11242 return NULL;
11243}
11244
11245/**
11246 * Main implementation for Machine::DetachDevice. This also gets called
11247 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11248 *
11249 * @param pAttach Medium attachment to detach.
11250 * @param writeLock Machine write lock which the caller must have locked once.
11251 * This may be released temporarily in here.
11252 * @param pSnapshot If NULL, then the detachment is for the current machine.
11253 * Otherwise this is for a SnapshotMachine, and this must be
11254 * its snapshot.
11255 * @return
11256 */
11257HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11258 AutoWriteLock &writeLock,
11259 Snapshot *pSnapshot)
11260{
11261 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11262 DeviceType_T mediumType = pAttach->i_getType();
11263
11264 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11265
11266 if (pAttach->i_isImplicit())
11267 {
11268 /* attempt to implicitly delete the implicitly created diff */
11269
11270 /// @todo move the implicit flag from MediumAttachment to Medium
11271 /// and forbid any hard disk operation when it is implicit. Or maybe
11272 /// a special media state for it to make it even more simple.
11273
11274 Assert(mMediumAttachments.isBackedUp());
11275
11276 /* will release the lock before the potentially lengthy operation, so
11277 * protect with the special state */
11278 MachineState_T oldState = mData->mMachineState;
11279 i_setMachineState(MachineState_SettingUp);
11280
11281 writeLock.release();
11282
11283 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11284 true /*aWait*/);
11285
11286 writeLock.acquire();
11287
11288 i_setMachineState(oldState);
11289
11290 if (FAILED(rc)) return rc;
11291 }
11292
11293 i_setModified(IsModified_Storage);
11294 mMediumAttachments.backup();
11295 mMediumAttachments->remove(pAttach);
11296
11297 if (!oldmedium.isNull())
11298 {
11299 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11300 if (pSnapshot)
11301 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11302 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11303 else if (mediumType != DeviceType_HardDisk)
11304 oldmedium->i_removeBackReference(mData->mUuid);
11305 }
11306
11307 return S_OK;
11308}
11309
11310/**
11311 * Goes thru all media of the given list and
11312 *
11313 * 1) calls i_detachDevice() on each of them for this machine and
11314 * 2) adds all Medium objects found in the process to the given list,
11315 * depending on cleanupMode.
11316 *
11317 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11318 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11319 * media to the list.
11320 *
11321 * This gets called from Machine::Unregister, both for the actual Machine and
11322 * the SnapshotMachine objects that might be found in the snapshots.
11323 *
11324 * Requires caller and locking. The machine lock must be passed in because it
11325 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11326 *
11327 * @param writeLock Machine lock from top-level caller; this gets passed to
11328 * i_detachDevice.
11329 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11330 * object if called for a SnapshotMachine.
11331 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11332 * added to llMedia; if Full, then all media get added;
11333 * otherwise no media get added.
11334 * @param llMedia Caller's list to receive Medium objects which got detached so
11335 * caller can close() them, depending on cleanupMode.
11336 * @return
11337 */
11338HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11339 Snapshot *pSnapshot,
11340 CleanupMode_T cleanupMode,
11341 MediaList &llMedia)
11342{
11343 Assert(isWriteLockOnCurrentThread());
11344
11345 HRESULT rc;
11346
11347 // make a temporary list because i_detachDevice invalidates iterators into
11348 // mMediumAttachments
11349 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11350
11351 for (MediumAttachmentList::iterator
11352 it = llAttachments2.begin();
11353 it != llAttachments2.end();
11354 ++it)
11355 {
11356 ComObjPtr<MediumAttachment> &pAttach = *it;
11357 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11358
11359 if (!pMedium.isNull())
11360 {
11361 AutoCaller mac(pMedium);
11362 if (FAILED(mac.rc())) return mac.rc();
11363 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11364 DeviceType_T devType = pMedium->i_getDeviceType();
11365 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11366 && devType == DeviceType_HardDisk)
11367 || (cleanupMode == CleanupMode_Full)
11368 )
11369 {
11370 llMedia.push_back(pMedium);
11371 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11372 /* Not allowed to keep this lock as below we need the parent
11373 * medium lock, and the lock order is parent to child. */
11374 lock.release();
11375 /*
11376 * Search for medias which are not attached to any machine, but
11377 * in the chain to an attached disk. Mediums are only consided
11378 * if they are:
11379 * - have only one child
11380 * - no references to any machines
11381 * - are of normal medium type
11382 */
11383 while (!pParent.isNull())
11384 {
11385 AutoCaller mac1(pParent);
11386 if (FAILED(mac1.rc())) return mac1.rc();
11387 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11388 if (pParent->i_getChildren().size() == 1)
11389 {
11390 if ( pParent->i_getMachineBackRefCount() == 0
11391 && pParent->i_getType() == MediumType_Normal
11392 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11393 llMedia.push_back(pParent);
11394 }
11395 else
11396 break;
11397 pParent = pParent->i_getParent();
11398 }
11399 }
11400 }
11401
11402 // real machine: then we need to use the proper method
11403 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11404
11405 if (FAILED(rc))
11406 return rc;
11407 }
11408
11409 return S_OK;
11410}
11411
11412/**
11413 * Perform deferred hard disk detachments.
11414 *
11415 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11416 * changed (not backed up).
11417 *
11418 * If @a aOnline is @c true then this method will also unlock the old hard
11419 * disks for which the new implicit diffs were created and will lock these new
11420 * diffs for writing.
11421 *
11422 * @param aOnline Whether the VM was online prior to this operation.
11423 *
11424 * @note Locks this object for writing!
11425 */
11426void Machine::i_commitMedia(bool aOnline /*= false*/)
11427{
11428 AutoCaller autoCaller(this);
11429 AssertComRCReturnVoid(autoCaller.rc());
11430
11431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11432
11433 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11434
11435 HRESULT rc = S_OK;
11436
11437 /* no attach/detach operations -- nothing to do */
11438 if (!mMediumAttachments.isBackedUp())
11439 return;
11440
11441 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11442 bool fMediaNeedsLocking = false;
11443
11444 /* enumerate new attachments */
11445 for (MediumAttachmentList::const_iterator
11446 it = mMediumAttachments->begin();
11447 it != mMediumAttachments->end();
11448 ++it)
11449 {
11450 MediumAttachment *pAttach = *it;
11451
11452 pAttach->i_commit();
11453
11454 Medium *pMedium = pAttach->i_getMedium();
11455 bool fImplicit = pAttach->i_isImplicit();
11456
11457 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11458 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11459 fImplicit));
11460
11461 /** @todo convert all this Machine-based voodoo to MediumAttachment
11462 * based commit logic. */
11463 if (fImplicit)
11464 {
11465 /* convert implicit attachment to normal */
11466 pAttach->i_setImplicit(false);
11467
11468 if ( aOnline
11469 && pMedium
11470 && pAttach->i_getType() == DeviceType_HardDisk
11471 )
11472 {
11473 /* update the appropriate lock list */
11474 MediumLockList *pMediumLockList;
11475 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11476 AssertComRC(rc);
11477 if (pMediumLockList)
11478 {
11479 /* unlock if there's a need to change the locking */
11480 if (!fMediaNeedsLocking)
11481 {
11482 rc = mData->mSession.mLockedMedia.Unlock();
11483 AssertComRC(rc);
11484 fMediaNeedsLocking = true;
11485 }
11486 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11487 AssertComRC(rc);
11488 rc = pMediumLockList->Append(pMedium, true);
11489 AssertComRC(rc);
11490 }
11491 }
11492
11493 continue;
11494 }
11495
11496 if (pMedium)
11497 {
11498 /* was this medium attached before? */
11499 for (MediumAttachmentList::iterator
11500 oldIt = oldAtts.begin();
11501 oldIt != oldAtts.end();
11502 ++oldIt)
11503 {
11504 MediumAttachment *pOldAttach = *oldIt;
11505 if (pOldAttach->i_getMedium() == pMedium)
11506 {
11507 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11508
11509 /* yes: remove from old to avoid de-association */
11510 oldAtts.erase(oldIt);
11511 break;
11512 }
11513 }
11514 }
11515 }
11516
11517 /* enumerate remaining old attachments and de-associate from the
11518 * current machine state */
11519 for (MediumAttachmentList::const_iterator
11520 it = oldAtts.begin();
11521 it != oldAtts.end();
11522 ++it)
11523 {
11524 MediumAttachment *pAttach = *it;
11525 Medium *pMedium = pAttach->i_getMedium();
11526
11527 /* Detach only hard disks, since DVD/floppy media is detached
11528 * instantly in MountMedium. */
11529 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11530 {
11531 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11532
11533 /* now de-associate from the current machine state */
11534 rc = pMedium->i_removeBackReference(mData->mUuid);
11535 AssertComRC(rc);
11536
11537 if (aOnline)
11538 {
11539 /* unlock since medium is not used anymore */
11540 MediumLockList *pMediumLockList;
11541 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11542 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11543 {
11544 /* this happens for online snapshots, there the attachment
11545 * is changing, but only to a diff image created under
11546 * the old one, so there is no separate lock list */
11547 Assert(!pMediumLockList);
11548 }
11549 else
11550 {
11551 AssertComRC(rc);
11552 if (pMediumLockList)
11553 {
11554 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11555 AssertComRC(rc);
11556 }
11557 }
11558 }
11559 }
11560 }
11561
11562 /* take media locks again so that the locking state is consistent */
11563 if (fMediaNeedsLocking)
11564 {
11565 Assert(aOnline);
11566 rc = mData->mSession.mLockedMedia.Lock();
11567 AssertComRC(rc);
11568 }
11569
11570 /* commit the hard disk changes */
11571 mMediumAttachments.commit();
11572
11573 if (i_isSessionMachine())
11574 {
11575 /*
11576 * Update the parent machine to point to the new owner.
11577 * This is necessary because the stored parent will point to the
11578 * session machine otherwise and cause crashes or errors later
11579 * when the session machine gets invalid.
11580 */
11581 /** @todo Change the MediumAttachment class to behave like any other
11582 * class in this regard by creating peer MediumAttachment
11583 * objects for session machines and share the data with the peer
11584 * machine.
11585 */
11586 for (MediumAttachmentList::const_iterator
11587 it = mMediumAttachments->begin();
11588 it != mMediumAttachments->end();
11589 ++it)
11590 (*it)->i_updateParentMachine(mPeer);
11591
11592 /* attach new data to the primary machine and reshare it */
11593 mPeer->mMediumAttachments.attach(mMediumAttachments);
11594 }
11595
11596 return;
11597}
11598
11599/**
11600 * Perform deferred deletion of implicitly created diffs.
11601 *
11602 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11603 * changed (not backed up).
11604 *
11605 * @note Locks this object for writing!
11606 */
11607void Machine::i_rollbackMedia()
11608{
11609 AutoCaller autoCaller(this);
11610 AssertComRCReturnVoid(autoCaller.rc());
11611
11612 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11613 LogFlowThisFunc(("Entering rollbackMedia\n"));
11614
11615 HRESULT rc = S_OK;
11616
11617 /* no attach/detach operations -- nothing to do */
11618 if (!mMediumAttachments.isBackedUp())
11619 return;
11620
11621 /* enumerate new attachments */
11622 for (MediumAttachmentList::const_iterator
11623 it = mMediumAttachments->begin();
11624 it != mMediumAttachments->end();
11625 ++it)
11626 {
11627 MediumAttachment *pAttach = *it;
11628 /* Fix up the backrefs for DVD/floppy media. */
11629 if (pAttach->i_getType() != DeviceType_HardDisk)
11630 {
11631 Medium *pMedium = pAttach->i_getMedium();
11632 if (pMedium)
11633 {
11634 rc = pMedium->i_removeBackReference(mData->mUuid);
11635 AssertComRC(rc);
11636 }
11637 }
11638
11639 (*it)->i_rollback();
11640
11641 pAttach = *it;
11642 /* Fix up the backrefs for DVD/floppy media. */
11643 if (pAttach->i_getType() != DeviceType_HardDisk)
11644 {
11645 Medium *pMedium = pAttach->i_getMedium();
11646 if (pMedium)
11647 {
11648 rc = pMedium->i_addBackReference(mData->mUuid);
11649 AssertComRC(rc);
11650 }
11651 }
11652 }
11653
11654 /** @todo convert all this Machine-based voodoo to MediumAttachment
11655 * based rollback logic. */
11656 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11657
11658 return;
11659}
11660
11661/**
11662 * Returns true if the settings file is located in the directory named exactly
11663 * as the machine; this means, among other things, that the machine directory
11664 * should be auto-renamed.
11665 *
11666 * @param aSettingsDir if not NULL, the full machine settings file directory
11667 * name will be assigned there.
11668 *
11669 * @note Doesn't lock anything.
11670 * @note Not thread safe (must be called from this object's lock).
11671 */
11672bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11673{
11674 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11675 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11676 if (aSettingsDir)
11677 *aSettingsDir = strMachineDirName;
11678 strMachineDirName.stripPath(); // vmname
11679 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11680 strConfigFileOnly.stripPath() // vmname.vbox
11681 .stripSuffix(); // vmname
11682 /** @todo hack, make somehow use of ComposeMachineFilename */
11683 if (mUserData->s.fDirectoryIncludesUUID)
11684 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11685
11686 AssertReturn(!strMachineDirName.isEmpty(), false);
11687 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11688
11689 return strMachineDirName == strConfigFileOnly;
11690}
11691
11692/**
11693 * Discards all changes to machine settings.
11694 *
11695 * @param aNotify Whether to notify the direct session about changes or not.
11696 *
11697 * @note Locks objects for writing!
11698 */
11699void Machine::i_rollback(bool aNotify)
11700{
11701 AutoCaller autoCaller(this);
11702 AssertComRCReturn(autoCaller.rc(), (void)0);
11703
11704 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11705
11706 if (!mStorageControllers.isNull())
11707 {
11708 if (mStorageControllers.isBackedUp())
11709 {
11710 /* unitialize all new devices (absent in the backed up list). */
11711 StorageControllerList *backedList = mStorageControllers.backedUpData();
11712 for (StorageControllerList::const_iterator
11713 it = mStorageControllers->begin();
11714 it != mStorageControllers->end();
11715 ++it)
11716 {
11717 if ( std::find(backedList->begin(), backedList->end(), *it)
11718 == backedList->end()
11719 )
11720 {
11721 (*it)->uninit();
11722 }
11723 }
11724
11725 /* restore the list */
11726 mStorageControllers.rollback();
11727 }
11728
11729 /* rollback any changes to devices after restoring the list */
11730 if (mData->flModifications & IsModified_Storage)
11731 {
11732 for (StorageControllerList::const_iterator
11733 it = mStorageControllers->begin();
11734 it != mStorageControllers->end();
11735 ++it)
11736 {
11737 (*it)->i_rollback();
11738 }
11739 }
11740 }
11741
11742 if (!mUSBControllers.isNull())
11743 {
11744 if (mUSBControllers.isBackedUp())
11745 {
11746 /* unitialize all new devices (absent in the backed up list). */
11747 USBControllerList *backedList = mUSBControllers.backedUpData();
11748 for (USBControllerList::const_iterator
11749 it = mUSBControllers->begin();
11750 it != mUSBControllers->end();
11751 ++it)
11752 {
11753 if ( std::find(backedList->begin(), backedList->end(), *it)
11754 == backedList->end()
11755 )
11756 {
11757 (*it)->uninit();
11758 }
11759 }
11760
11761 /* restore the list */
11762 mUSBControllers.rollback();
11763 }
11764
11765 /* rollback any changes to devices after restoring the list */
11766 if (mData->flModifications & IsModified_USB)
11767 {
11768 for (USBControllerList::const_iterator
11769 it = mUSBControllers->begin();
11770 it != mUSBControllers->end();
11771 ++it)
11772 {
11773 (*it)->i_rollback();
11774 }
11775 }
11776 }
11777
11778 mUserData.rollback();
11779
11780 mHWData.rollback();
11781
11782 if (mData->flModifications & IsModified_Storage)
11783 i_rollbackMedia();
11784
11785 if (mBIOSSettings)
11786 mBIOSSettings->i_rollback();
11787
11788 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11789 mVRDEServer->i_rollback();
11790
11791 if (mAudioAdapter)
11792 mAudioAdapter->i_rollback();
11793
11794 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11795 mUSBDeviceFilters->i_rollback();
11796
11797 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11798 mBandwidthControl->i_rollback();
11799
11800 if (!mHWData.isNull())
11801 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11802 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11803 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11804 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11805
11806 if (mData->flModifications & IsModified_NetworkAdapters)
11807 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11808 if ( mNetworkAdapters[slot]
11809 && mNetworkAdapters[slot]->i_isModified())
11810 {
11811 mNetworkAdapters[slot]->i_rollback();
11812 networkAdapters[slot] = mNetworkAdapters[slot];
11813 }
11814
11815 if (mData->flModifications & IsModified_SerialPorts)
11816 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11817 if ( mSerialPorts[slot]
11818 && mSerialPorts[slot]->i_isModified())
11819 {
11820 mSerialPorts[slot]->i_rollback();
11821 serialPorts[slot] = mSerialPorts[slot];
11822 }
11823
11824 if (mData->flModifications & IsModified_ParallelPorts)
11825 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11826 if ( mParallelPorts[slot]
11827 && mParallelPorts[slot]->i_isModified())
11828 {
11829 mParallelPorts[slot]->i_rollback();
11830 parallelPorts[slot] = mParallelPorts[slot];
11831 }
11832
11833 if (aNotify)
11834 {
11835 /* inform the direct session about changes */
11836
11837 ComObjPtr<Machine> that = this;
11838 uint32_t flModifications = mData->flModifications;
11839 alock.release();
11840
11841 if (flModifications & IsModified_SharedFolders)
11842 that->i_onSharedFolderChange();
11843
11844 if (flModifications & IsModified_VRDEServer)
11845 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11846 if (flModifications & IsModified_USB)
11847 that->i_onUSBControllerChange();
11848
11849 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11850 if (networkAdapters[slot])
11851 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11852 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11853 if (serialPorts[slot])
11854 that->i_onSerialPortChange(serialPorts[slot]);
11855 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11856 if (parallelPorts[slot])
11857 that->i_onParallelPortChange(parallelPorts[slot]);
11858
11859 if (flModifications & IsModified_Storage)
11860 that->i_onStorageControllerChange();
11861
11862#if 0
11863 if (flModifications & IsModified_BandwidthControl)
11864 that->onBandwidthControlChange();
11865#endif
11866 }
11867}
11868
11869/**
11870 * Commits all the changes to machine settings.
11871 *
11872 * Note that this operation is supposed to never fail.
11873 *
11874 * @note Locks this object and children for writing.
11875 */
11876void Machine::i_commit()
11877{
11878 AutoCaller autoCaller(this);
11879 AssertComRCReturnVoid(autoCaller.rc());
11880
11881 AutoCaller peerCaller(mPeer);
11882 AssertComRCReturnVoid(peerCaller.rc());
11883
11884 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11885
11886 /*
11887 * use safe commit to ensure Snapshot machines (that share mUserData)
11888 * will still refer to a valid memory location
11889 */
11890 mUserData.commitCopy();
11891
11892 mHWData.commit();
11893
11894 if (mMediumAttachments.isBackedUp())
11895 i_commitMedia(Global::IsOnline(mData->mMachineState));
11896
11897 mBIOSSettings->i_commit();
11898 mVRDEServer->i_commit();
11899 mAudioAdapter->i_commit();
11900 mUSBDeviceFilters->i_commit();
11901 mBandwidthControl->i_commit();
11902
11903 /* Since mNetworkAdapters is a list which might have been changed (resized)
11904 * without using the Backupable<> template we need to handle the copying
11905 * of the list entries manually, including the creation of peers for the
11906 * new objects. */
11907 bool commitNetworkAdapters = false;
11908 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11909 if (mPeer)
11910 {
11911 /* commit everything, even the ones which will go away */
11912 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11913 mNetworkAdapters[slot]->i_commit();
11914 /* copy over the new entries, creating a peer and uninit the original */
11915 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11916 for (size_t slot = 0; slot < newSize; slot++)
11917 {
11918 /* look if this adapter has a peer device */
11919 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11920 if (!peer)
11921 {
11922 /* no peer means the adapter is a newly created one;
11923 * create a peer owning data this data share it with */
11924 peer.createObject();
11925 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11926 }
11927 mPeer->mNetworkAdapters[slot] = peer;
11928 }
11929 /* uninit any no longer needed network adapters */
11930 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11931 mNetworkAdapters[slot]->uninit();
11932 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11933 {
11934 if (mPeer->mNetworkAdapters[slot])
11935 mPeer->mNetworkAdapters[slot]->uninit();
11936 }
11937 /* Keep the original network adapter count until this point, so that
11938 * discarding a chipset type change will not lose settings. */
11939 mNetworkAdapters.resize(newSize);
11940 mPeer->mNetworkAdapters.resize(newSize);
11941 }
11942 else
11943 {
11944 /* we have no peer (our parent is the newly created machine);
11945 * just commit changes to the network adapters */
11946 commitNetworkAdapters = true;
11947 }
11948 if (commitNetworkAdapters)
11949 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11950 mNetworkAdapters[slot]->i_commit();
11951
11952 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11953 mSerialPorts[slot]->i_commit();
11954 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11955 mParallelPorts[slot]->i_commit();
11956
11957 bool commitStorageControllers = false;
11958
11959 if (mStorageControllers.isBackedUp())
11960 {
11961 mStorageControllers.commit();
11962
11963 if (mPeer)
11964 {
11965 /* Commit all changes to new controllers (this will reshare data with
11966 * peers for those who have peers) */
11967 StorageControllerList *newList = new StorageControllerList();
11968 for (StorageControllerList::const_iterator
11969 it = mStorageControllers->begin();
11970 it != mStorageControllers->end();
11971 ++it)
11972 {
11973 (*it)->i_commit();
11974
11975 /* look if this controller has a peer device */
11976 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11977 if (!peer)
11978 {
11979 /* no peer means the device is a newly created one;
11980 * create a peer owning data this device share it with */
11981 peer.createObject();
11982 peer->init(mPeer, *it, true /* aReshare */);
11983 }
11984 else
11985 {
11986 /* remove peer from the old list */
11987 mPeer->mStorageControllers->remove(peer);
11988 }
11989 /* and add it to the new list */
11990 newList->push_back(peer);
11991 }
11992
11993 /* uninit old peer's controllers that are left */
11994 for (StorageControllerList::const_iterator
11995 it = mPeer->mStorageControllers->begin();
11996 it != mPeer->mStorageControllers->end();
11997 ++it)
11998 {
11999 (*it)->uninit();
12000 }
12001
12002 /* attach new list of controllers to our peer */
12003 mPeer->mStorageControllers.attach(newList);
12004 }
12005 else
12006 {
12007 /* we have no peer (our parent is the newly created machine);
12008 * just commit changes to devices */
12009 commitStorageControllers = true;
12010 }
12011 }
12012 else
12013 {
12014 /* the list of controllers itself is not changed,
12015 * just commit changes to controllers themselves */
12016 commitStorageControllers = true;
12017 }
12018
12019 if (commitStorageControllers)
12020 {
12021 for (StorageControllerList::const_iterator
12022 it = mStorageControllers->begin();
12023 it != mStorageControllers->end();
12024 ++it)
12025 {
12026 (*it)->i_commit();
12027 }
12028 }
12029
12030 bool commitUSBControllers = false;
12031
12032 if (mUSBControllers.isBackedUp())
12033 {
12034 mUSBControllers.commit();
12035
12036 if (mPeer)
12037 {
12038 /* Commit all changes to new controllers (this will reshare data with
12039 * peers for those who have peers) */
12040 USBControllerList *newList = new USBControllerList();
12041 for (USBControllerList::const_iterator
12042 it = mUSBControllers->begin();
12043 it != mUSBControllers->end();
12044 ++it)
12045 {
12046 (*it)->i_commit();
12047
12048 /* look if this controller has a peer device */
12049 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12050 if (!peer)
12051 {
12052 /* no peer means the device is a newly created one;
12053 * create a peer owning data this device share it with */
12054 peer.createObject();
12055 peer->init(mPeer, *it, true /* aReshare */);
12056 }
12057 else
12058 {
12059 /* remove peer from the old list */
12060 mPeer->mUSBControllers->remove(peer);
12061 }
12062 /* and add it to the new list */
12063 newList->push_back(peer);
12064 }
12065
12066 /* uninit old peer's controllers that are left */
12067 for (USBControllerList::const_iterator
12068 it = mPeer->mUSBControllers->begin();
12069 it != mPeer->mUSBControllers->end();
12070 ++it)
12071 {
12072 (*it)->uninit();
12073 }
12074
12075 /* attach new list of controllers to our peer */
12076 mPeer->mUSBControllers.attach(newList);
12077 }
12078 else
12079 {
12080 /* we have no peer (our parent is the newly created machine);
12081 * just commit changes to devices */
12082 commitUSBControllers = true;
12083 }
12084 }
12085 else
12086 {
12087 /* the list of controllers itself is not changed,
12088 * just commit changes to controllers themselves */
12089 commitUSBControllers = true;
12090 }
12091
12092 if (commitUSBControllers)
12093 {
12094 for (USBControllerList::const_iterator
12095 it = mUSBControllers->begin();
12096 it != mUSBControllers->end();
12097 ++it)
12098 {
12099 (*it)->i_commit();
12100 }
12101 }
12102
12103 if (i_isSessionMachine())
12104 {
12105 /* attach new data to the primary machine and reshare it */
12106 mPeer->mUserData.attach(mUserData);
12107 mPeer->mHWData.attach(mHWData);
12108 /* mmMediumAttachments is reshared by fixupMedia */
12109 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12110 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12111 }
12112}
12113
12114/**
12115 * Copies all the hardware data from the given machine.
12116 *
12117 * Currently, only called when the VM is being restored from a snapshot. In
12118 * particular, this implies that the VM is not running during this method's
12119 * call.
12120 *
12121 * @note This method must be called from under this object's lock.
12122 *
12123 * @note This method doesn't call #i_commit(), so all data remains backed up and
12124 * unsaved.
12125 */
12126void Machine::i_copyFrom(Machine *aThat)
12127{
12128 AssertReturnVoid(!i_isSnapshotMachine());
12129 AssertReturnVoid(aThat->i_isSnapshotMachine());
12130
12131 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12132
12133 mHWData.assignCopy(aThat->mHWData);
12134
12135 // create copies of all shared folders (mHWData after attaching a copy
12136 // contains just references to original objects)
12137 for (HWData::SharedFolderList::iterator
12138 it = mHWData->mSharedFolders.begin();
12139 it != mHWData->mSharedFolders.end();
12140 ++it)
12141 {
12142 ComObjPtr<SharedFolder> folder;
12143 folder.createObject();
12144 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12145 AssertComRC(rc);
12146 *it = folder;
12147 }
12148
12149 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12150 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12151 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12152 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12153 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12154
12155 /* create private copies of all controllers */
12156 mStorageControllers.backup();
12157 mStorageControllers->clear();
12158 for (StorageControllerList::const_iterator
12159 it = aThat->mStorageControllers->begin();
12160 it != aThat->mStorageControllers->end();
12161 ++it)
12162 {
12163 ComObjPtr<StorageController> ctrl;
12164 ctrl.createObject();
12165 ctrl->initCopy(this, *it);
12166 mStorageControllers->push_back(ctrl);
12167 }
12168
12169 /* create private copies of all USB controllers */
12170 mUSBControllers.backup();
12171 mUSBControllers->clear();
12172 for (USBControllerList::const_iterator
12173 it = aThat->mUSBControllers->begin();
12174 it != aThat->mUSBControllers->end();
12175 ++it)
12176 {
12177 ComObjPtr<USBController> ctrl;
12178 ctrl.createObject();
12179 ctrl->initCopy(this, *it);
12180 mUSBControllers->push_back(ctrl);
12181 }
12182
12183 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12184 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12185 {
12186 if (mNetworkAdapters[slot].isNotNull())
12187 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12188 else
12189 {
12190 unconst(mNetworkAdapters[slot]).createObject();
12191 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12192 }
12193 }
12194 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12195 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12196 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12197 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12198}
12199
12200/**
12201 * Returns whether the given storage controller is hotplug capable.
12202 *
12203 * @returns true if the controller supports hotplugging
12204 * false otherwise.
12205 * @param enmCtrlType The controller type to check for.
12206 */
12207bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12208{
12209 ComPtr<ISystemProperties> systemProperties;
12210 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12211 if (FAILED(rc))
12212 return false;
12213
12214 BOOL aHotplugCapable = FALSE;
12215 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12216
12217 return RT_BOOL(aHotplugCapable);
12218}
12219
12220#ifdef VBOX_WITH_RESOURCE_USAGE_API
12221
12222void Machine::i_getDiskList(MediaList &list)
12223{
12224 for (MediumAttachmentList::const_iterator
12225 it = mMediumAttachments->begin();
12226 it != mMediumAttachments->end();
12227 ++it)
12228 {
12229 MediumAttachment *pAttach = *it;
12230 /* just in case */
12231 AssertContinue(pAttach);
12232
12233 AutoCaller localAutoCallerA(pAttach);
12234 if (FAILED(localAutoCallerA.rc())) continue;
12235
12236 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12237
12238 if (pAttach->i_getType() == DeviceType_HardDisk)
12239 list.push_back(pAttach->i_getMedium());
12240 }
12241}
12242
12243void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12244{
12245 AssertReturnVoid(isWriteLockOnCurrentThread());
12246 AssertPtrReturnVoid(aCollector);
12247
12248 pm::CollectorHAL *hal = aCollector->getHAL();
12249 /* Create sub metrics */
12250 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12251 "Percentage of processor time spent in user mode by the VM process.");
12252 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12253 "Percentage of processor time spent in kernel mode by the VM process.");
12254 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12255 "Size of resident portion of VM process in memory.");
12256 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12257 "Actual size of all VM disks combined.");
12258 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12259 "Network receive rate.");
12260 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12261 "Network transmit rate.");
12262 /* Create and register base metrics */
12263 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12264 cpuLoadUser, cpuLoadKernel);
12265 aCollector->registerBaseMetric(cpuLoad);
12266 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12267 ramUsageUsed);
12268 aCollector->registerBaseMetric(ramUsage);
12269 MediaList disks;
12270 i_getDiskList(disks);
12271 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12272 diskUsageUsed);
12273 aCollector->registerBaseMetric(diskUsage);
12274
12275 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12276 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12277 new pm::AggregateAvg()));
12278 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12279 new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12281 new pm::AggregateMax()));
12282 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12283 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12284 new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12286 new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12288 new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12291 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12292 new pm::AggregateAvg()));
12293 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12294 new pm::AggregateMin()));
12295 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12296 new pm::AggregateMax()));
12297
12298 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12299 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12300 new pm::AggregateAvg()));
12301 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12302 new pm::AggregateMin()));
12303 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12304 new pm::AggregateMax()));
12305
12306
12307 /* Guest metrics collector */
12308 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12309 aCollector->registerGuest(mCollectorGuest);
12310 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12311
12312 /* Create sub metrics */
12313 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12314 "Percentage of processor time spent in user mode as seen by the guest.");
12315 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12316 "Percentage of processor time spent in kernel mode as seen by the guest.");
12317 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12318 "Percentage of processor time spent idling as seen by the guest.");
12319
12320 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12321 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12322 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12323 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12324 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12325 pm::SubMetric *guestMemCache = new pm::SubMetric(
12326 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12327
12328 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12329 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12330
12331 /* Create and register base metrics */
12332 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12333 machineNetRx, machineNetTx);
12334 aCollector->registerBaseMetric(machineNetRate);
12335
12336 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12337 guestLoadUser, guestLoadKernel, guestLoadIdle);
12338 aCollector->registerBaseMetric(guestCpuLoad);
12339
12340 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12341 guestMemTotal, guestMemFree,
12342 guestMemBalloon, guestMemShared,
12343 guestMemCache, guestPagedTotal);
12344 aCollector->registerBaseMetric(guestCpuMem);
12345
12346 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12347 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12348 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12349 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12350
12351 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12352 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12353 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12354 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12355
12356 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12357 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12358 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12360
12361 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12362 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12363 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12365
12366 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12367 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12368 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12370
12371 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12375
12376 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12377 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12378 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12379 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12380
12381 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12382 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12383 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12384 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12385
12386 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12387 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12388 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12389 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12390
12391 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12392 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12393 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12394 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12395
12396 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12397 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12398 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12399 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12400}
12401
12402void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12403{
12404 AssertReturnVoid(isWriteLockOnCurrentThread());
12405
12406 if (aCollector)
12407 {
12408 aCollector->unregisterMetricsFor(aMachine);
12409 aCollector->unregisterBaseMetricsFor(aMachine);
12410 }
12411}
12412
12413#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12414
12415
12416////////////////////////////////////////////////////////////////////////////////
12417
12418DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12419
12420HRESULT SessionMachine::FinalConstruct()
12421{
12422 LogFlowThisFunc(("\n"));
12423
12424 mClientToken = NULL;
12425
12426 return BaseFinalConstruct();
12427}
12428
12429void SessionMachine::FinalRelease()
12430{
12431 LogFlowThisFunc(("\n"));
12432
12433 Assert(!mClientToken);
12434 /* paranoia, should not hang around any more */
12435 if (mClientToken)
12436 {
12437 delete mClientToken;
12438 mClientToken = NULL;
12439 }
12440
12441 uninit(Uninit::Unexpected);
12442
12443 BaseFinalRelease();
12444}
12445
12446/**
12447 * @note Must be called only by Machine::LockMachine() from its own write lock.
12448 */
12449HRESULT SessionMachine::init(Machine *aMachine)
12450{
12451 LogFlowThisFuncEnter();
12452 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12453
12454 AssertReturn(aMachine, E_INVALIDARG);
12455
12456 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12457
12458 /* Enclose the state transition NotReady->InInit->Ready */
12459 AutoInitSpan autoInitSpan(this);
12460 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12461
12462 HRESULT rc = S_OK;
12463
12464 RT_ZERO(mAuthLibCtx);
12465
12466 /* create the machine client token */
12467 try
12468 {
12469 mClientToken = new ClientToken(aMachine, this);
12470 if (!mClientToken->isReady())
12471 {
12472 delete mClientToken;
12473 mClientToken = NULL;
12474 rc = E_FAIL;
12475 }
12476 }
12477 catch (std::bad_alloc &)
12478 {
12479 rc = E_OUTOFMEMORY;
12480 }
12481 if (FAILED(rc))
12482 return rc;
12483
12484 /* memorize the peer Machine */
12485 unconst(mPeer) = aMachine;
12486 /* share the parent pointer */
12487 unconst(mParent) = aMachine->mParent;
12488
12489 /* take the pointers to data to share */
12490 mData.share(aMachine->mData);
12491 mSSData.share(aMachine->mSSData);
12492
12493 mUserData.share(aMachine->mUserData);
12494 mHWData.share(aMachine->mHWData);
12495 mMediumAttachments.share(aMachine->mMediumAttachments);
12496
12497 mStorageControllers.allocate();
12498 for (StorageControllerList::const_iterator
12499 it = aMachine->mStorageControllers->begin();
12500 it != aMachine->mStorageControllers->end();
12501 ++it)
12502 {
12503 ComObjPtr<StorageController> ctl;
12504 ctl.createObject();
12505 ctl->init(this, *it);
12506 mStorageControllers->push_back(ctl);
12507 }
12508
12509 mUSBControllers.allocate();
12510 for (USBControllerList::const_iterator
12511 it = aMachine->mUSBControllers->begin();
12512 it != aMachine->mUSBControllers->end();
12513 ++it)
12514 {
12515 ComObjPtr<USBController> ctl;
12516 ctl.createObject();
12517 ctl->init(this, *it);
12518 mUSBControllers->push_back(ctl);
12519 }
12520
12521 unconst(mBIOSSettings).createObject();
12522 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12523 /* create another VRDEServer object that will be mutable */
12524 unconst(mVRDEServer).createObject();
12525 mVRDEServer->init(this, aMachine->mVRDEServer);
12526 /* create another audio adapter object that will be mutable */
12527 unconst(mAudioAdapter).createObject();
12528 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12529 /* create a list of serial ports that will be mutable */
12530 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12531 {
12532 unconst(mSerialPorts[slot]).createObject();
12533 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12534 }
12535 /* create a list of parallel ports that will be mutable */
12536 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12537 {
12538 unconst(mParallelPorts[slot]).createObject();
12539 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12540 }
12541
12542 /* create another USB device filters object that will be mutable */
12543 unconst(mUSBDeviceFilters).createObject();
12544 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12545
12546 /* create a list of network adapters that will be mutable */
12547 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12548 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12549 {
12550 unconst(mNetworkAdapters[slot]).createObject();
12551 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12552 }
12553
12554 /* create another bandwidth control object that will be mutable */
12555 unconst(mBandwidthControl).createObject();
12556 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12557
12558 /* default is to delete saved state on Saved -> PoweredOff transition */
12559 mRemoveSavedState = true;
12560
12561 /* Confirm a successful initialization when it's the case */
12562 autoInitSpan.setSucceeded();
12563
12564 miNATNetworksStarted = 0;
12565
12566 LogFlowThisFuncLeave();
12567 return rc;
12568}
12569
12570/**
12571 * Uninitializes this session object. If the reason is other than
12572 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12573 * or the client watcher code.
12574 *
12575 * @param aReason uninitialization reason
12576 *
12577 * @note Locks mParent + this object for writing.
12578 */
12579void SessionMachine::uninit(Uninit::Reason aReason)
12580{
12581 LogFlowThisFuncEnter();
12582 LogFlowThisFunc(("reason=%d\n", aReason));
12583
12584 /*
12585 * Strongly reference ourselves to prevent this object deletion after
12586 * mData->mSession.mMachine.setNull() below (which can release the last
12587 * reference and call the destructor). Important: this must be done before
12588 * accessing any members (and before AutoUninitSpan that does it as well).
12589 * This self reference will be released as the very last step on return.
12590 */
12591 ComObjPtr<SessionMachine> selfRef;
12592 if (aReason != Uninit::Unexpected)
12593 selfRef = this;
12594
12595 /* Enclose the state transition Ready->InUninit->NotReady */
12596 AutoUninitSpan autoUninitSpan(this);
12597 if (autoUninitSpan.uninitDone())
12598 {
12599 LogFlowThisFunc(("Already uninitialized\n"));
12600 LogFlowThisFuncLeave();
12601 return;
12602 }
12603
12604 if (autoUninitSpan.initFailed())
12605 {
12606 /* We've been called by init() because it's failed. It's not really
12607 * necessary (nor it's safe) to perform the regular uninit sequence
12608 * below, the following is enough.
12609 */
12610 LogFlowThisFunc(("Initialization failed.\n"));
12611 /* destroy the machine client token */
12612 if (mClientToken)
12613 {
12614 delete mClientToken;
12615 mClientToken = NULL;
12616 }
12617 uninitDataAndChildObjects();
12618 mData.free();
12619 unconst(mParent) = NULL;
12620 unconst(mPeer) = NULL;
12621 LogFlowThisFuncLeave();
12622 return;
12623 }
12624
12625 MachineState_T lastState;
12626 {
12627 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12628 lastState = mData->mMachineState;
12629 }
12630 NOREF(lastState);
12631
12632#ifdef VBOX_WITH_USB
12633 // release all captured USB devices, but do this before requesting the locks below
12634 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12635 {
12636 /* Console::captureUSBDevices() is called in the VM process only after
12637 * setting the machine state to Starting or Restoring.
12638 * Console::detachAllUSBDevices() will be called upon successful
12639 * termination. So, we need to release USB devices only if there was
12640 * an abnormal termination of a running VM.
12641 *
12642 * This is identical to SessionMachine::DetachAllUSBDevices except
12643 * for the aAbnormal argument. */
12644 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12645 AssertComRC(rc);
12646 NOREF(rc);
12647
12648 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12649 if (service)
12650 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12651 }
12652#endif /* VBOX_WITH_USB */
12653
12654 // we need to lock this object in uninit() because the lock is shared
12655 // with mPeer (as well as data we modify below). mParent lock is needed
12656 // by several calls to it.
12657 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12658
12659#ifdef VBOX_WITH_RESOURCE_USAGE_API
12660 /*
12661 * It is safe to call Machine::i_unregisterMetrics() here because
12662 * PerformanceCollector::samplerCallback no longer accesses guest methods
12663 * holding the lock.
12664 */
12665 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12666 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12667 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12668 if (mCollectorGuest)
12669 {
12670 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12671 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12672 mCollectorGuest = NULL;
12673 }
12674#endif
12675
12676 if (aReason == Uninit::Abnormal)
12677 {
12678 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12679
12680 /* reset the state to Aborted */
12681 if (mData->mMachineState != MachineState_Aborted)
12682 i_setMachineState(MachineState_Aborted);
12683 }
12684
12685 // any machine settings modified?
12686 if (mData->flModifications)
12687 {
12688 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12689 i_rollback(false /* aNotify */);
12690 }
12691
12692 mData->mSession.mPID = NIL_RTPROCESS;
12693
12694 if (aReason == Uninit::Unexpected)
12695 {
12696 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12697 * client watcher thread to update the set of machines that have open
12698 * sessions. */
12699 mParent->i_updateClientWatcher();
12700 }
12701
12702 /* uninitialize all remote controls */
12703 if (mData->mSession.mRemoteControls.size())
12704 {
12705 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12706 mData->mSession.mRemoteControls.size()));
12707
12708 /* Always restart a the beginning, since the iterator is invalidated
12709 * by using erase(). */
12710 for (Data::Session::RemoteControlList::iterator
12711 it = mData->mSession.mRemoteControls.begin();
12712 it != mData->mSession.mRemoteControls.end();
12713 it = mData->mSession.mRemoteControls.begin())
12714 {
12715 ComPtr<IInternalSessionControl> pControl = *it;
12716 mData->mSession.mRemoteControls.erase(it);
12717 multilock.release();
12718 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12719 HRESULT rc = pControl->Uninitialize();
12720 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12721 if (FAILED(rc))
12722 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12723 multilock.acquire();
12724 }
12725 mData->mSession.mRemoteControls.clear();
12726 }
12727
12728 /* Remove all references to the NAT network service. The service will stop
12729 * if all references (also from other VMs) are removed. */
12730 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12731 {
12732 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12733 {
12734 BOOL enabled;
12735 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12736 if ( FAILED(hrc)
12737 || !enabled)
12738 continue;
12739
12740 NetworkAttachmentType_T type;
12741 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12742 if ( SUCCEEDED(hrc)
12743 && type == NetworkAttachmentType_NATNetwork)
12744 {
12745 Bstr name;
12746 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12747 if (SUCCEEDED(hrc))
12748 {
12749 multilock.release();
12750 Utf8Str strName(name);
12751 LogRel(("VM '%s' stops using NAT network '%s'\n",
12752 mUserData->s.strName.c_str(), strName.c_str()));
12753 mParent->i_natNetworkRefDec(strName);
12754 multilock.acquire();
12755 }
12756 }
12757 }
12758 }
12759
12760 /*
12761 * An expected uninitialization can come only from #i_checkForDeath().
12762 * Otherwise it means that something's gone really wrong (for example,
12763 * the Session implementation has released the VirtualBox reference
12764 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12765 * etc). However, it's also possible, that the client releases the IPC
12766 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12767 * but the VirtualBox release event comes first to the server process.
12768 * This case is practically possible, so we should not assert on an
12769 * unexpected uninit, just log a warning.
12770 */
12771
12772 if (aReason == Uninit::Unexpected)
12773 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12774
12775 if (aReason != Uninit::Normal)
12776 {
12777 mData->mSession.mDirectControl.setNull();
12778 }
12779 else
12780 {
12781 /* this must be null here (see #OnSessionEnd()) */
12782 Assert(mData->mSession.mDirectControl.isNull());
12783 Assert(mData->mSession.mState == SessionState_Unlocking);
12784 Assert(!mData->mSession.mProgress.isNull());
12785 }
12786 if (mData->mSession.mProgress)
12787 {
12788 if (aReason == Uninit::Normal)
12789 mData->mSession.mProgress->i_notifyComplete(S_OK);
12790 else
12791 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12792 COM_IIDOF(ISession),
12793 getComponentName(),
12794 tr("The VM session was aborted"));
12795 mData->mSession.mProgress.setNull();
12796 }
12797
12798 if (mConsoleTaskData.mProgress)
12799 {
12800 Assert(aReason == Uninit::Abnormal);
12801 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12802 COM_IIDOF(ISession),
12803 getComponentName(),
12804 tr("The VM session was aborted"));
12805 mConsoleTaskData.mProgress.setNull();
12806 }
12807
12808 /* remove the association between the peer machine and this session machine */
12809 Assert( (SessionMachine*)mData->mSession.mMachine == this
12810 || aReason == Uninit::Unexpected);
12811
12812 /* reset the rest of session data */
12813 mData->mSession.mLockType = LockType_Null;
12814 mData->mSession.mMachine.setNull();
12815 mData->mSession.mState = SessionState_Unlocked;
12816 mData->mSession.mName.setNull();
12817
12818 /* destroy the machine client token before leaving the exclusive lock */
12819 if (mClientToken)
12820 {
12821 delete mClientToken;
12822 mClientToken = NULL;
12823 }
12824
12825 /* fire an event */
12826 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12827
12828 uninitDataAndChildObjects();
12829
12830 /* free the essential data structure last */
12831 mData.free();
12832
12833 /* release the exclusive lock before setting the below two to NULL */
12834 multilock.release();
12835
12836 unconst(mParent) = NULL;
12837 unconst(mPeer) = NULL;
12838
12839 AuthLibUnload(&mAuthLibCtx);
12840
12841 LogFlowThisFuncLeave();
12842}
12843
12844// util::Lockable interface
12845////////////////////////////////////////////////////////////////////////////////
12846
12847/**
12848 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12849 * with the primary Machine instance (mPeer).
12850 */
12851RWLockHandle *SessionMachine::lockHandle() const
12852{
12853 AssertReturn(mPeer != NULL, NULL);
12854 return mPeer->lockHandle();
12855}
12856
12857// IInternalMachineControl methods
12858////////////////////////////////////////////////////////////////////////////////
12859
12860/**
12861 * Passes collected guest statistics to performance collector object
12862 */
12863HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12864 ULONG aCpuKernel, ULONG aCpuIdle,
12865 ULONG aMemTotal, ULONG aMemFree,
12866 ULONG aMemBalloon, ULONG aMemShared,
12867 ULONG aMemCache, ULONG aPageTotal,
12868 ULONG aAllocVMM, ULONG aFreeVMM,
12869 ULONG aBalloonedVMM, ULONG aSharedVMM,
12870 ULONG aVmNetRx, ULONG aVmNetTx)
12871{
12872#ifdef VBOX_WITH_RESOURCE_USAGE_API
12873 if (mCollectorGuest)
12874 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12875 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12876 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12877 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12878
12879 return S_OK;
12880#else
12881 NOREF(aValidStats);
12882 NOREF(aCpuUser);
12883 NOREF(aCpuKernel);
12884 NOREF(aCpuIdle);
12885 NOREF(aMemTotal);
12886 NOREF(aMemFree);
12887 NOREF(aMemBalloon);
12888 NOREF(aMemShared);
12889 NOREF(aMemCache);
12890 NOREF(aPageTotal);
12891 NOREF(aAllocVMM);
12892 NOREF(aFreeVMM);
12893 NOREF(aBalloonedVMM);
12894 NOREF(aSharedVMM);
12895 NOREF(aVmNetRx);
12896 NOREF(aVmNetTx);
12897 return E_NOTIMPL;
12898#endif
12899}
12900
12901////////////////////////////////////////////////////////////////////////////////
12902//
12903// SessionMachine task records
12904//
12905////////////////////////////////////////////////////////////////////////////////
12906
12907/**
12908 * Task record for saving the machine state.
12909 */
12910class SessionMachine::SaveStateTask
12911 : public Machine::Task
12912{
12913public:
12914 SaveStateTask(SessionMachine *m,
12915 Progress *p,
12916 const Utf8Str &t,
12917 Reason_T enmReason,
12918 const Utf8Str &strStateFilePath)
12919 : Task(m, p, t),
12920 m_enmReason(enmReason),
12921 m_strStateFilePath(strStateFilePath)
12922 {}
12923
12924private:
12925 void handler()
12926 {
12927 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12928 }
12929
12930 Reason_T m_enmReason;
12931 Utf8Str m_strStateFilePath;
12932
12933 friend class SessionMachine;
12934};
12935
12936/**
12937 * Task thread implementation for SessionMachine::SaveState(), called from
12938 * SessionMachine::taskHandler().
12939 *
12940 * @note Locks this object for writing.
12941 *
12942 * @param task
12943 * @return
12944 */
12945void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12946{
12947 LogFlowThisFuncEnter();
12948
12949 AutoCaller autoCaller(this);
12950 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12951 if (FAILED(autoCaller.rc()))
12952 {
12953 /* we might have been uninitialized because the session was accidentally
12954 * closed by the client, so don't assert */
12955 HRESULT rc = setError(E_FAIL,
12956 tr("The session has been accidentally closed"));
12957 task.m_pProgress->i_notifyComplete(rc);
12958 LogFlowThisFuncLeave();
12959 return;
12960 }
12961
12962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12963
12964 HRESULT rc = S_OK;
12965
12966 try
12967 {
12968 ComPtr<IInternalSessionControl> directControl;
12969 if (mData->mSession.mLockType == LockType_VM)
12970 directControl = mData->mSession.mDirectControl;
12971 if (directControl.isNull())
12972 throw setError(VBOX_E_INVALID_VM_STATE,
12973 tr("Trying to save state without a running VM"));
12974 alock.release();
12975 BOOL fSuspendedBySave;
12976 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12977 Assert(!fSuspendedBySave);
12978 alock.acquire();
12979
12980 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12981 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12982 throw E_FAIL);
12983
12984 if (SUCCEEDED(rc))
12985 {
12986 mSSData->strStateFilePath = task.m_strStateFilePath;
12987
12988 /* save all VM settings */
12989 rc = i_saveSettings(NULL);
12990 // no need to check whether VirtualBox.xml needs saving also since
12991 // we can't have a name change pending at this point
12992 }
12993 else
12994 {
12995 // On failure, set the state to the state we had at the beginning.
12996 i_setMachineState(task.m_machineStateBackup);
12997 i_updateMachineStateOnClient();
12998
12999 // Delete the saved state file (might have been already created).
13000 // No need to check whether this is shared with a snapshot here
13001 // because we certainly created a fresh saved state file here.
13002 RTFileDelete(task.m_strStateFilePath.c_str());
13003 }
13004 }
13005 catch (HRESULT aRC) { rc = aRC; }
13006
13007 task.m_pProgress->i_notifyComplete(rc);
13008
13009 LogFlowThisFuncLeave();
13010}
13011
13012/**
13013 * @note Locks this object for writing.
13014 */
13015HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13016{
13017 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13018}
13019
13020HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13021{
13022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13023
13024 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13025 if (FAILED(rc)) return rc;
13026
13027 if ( mData->mMachineState != MachineState_Running
13028 && mData->mMachineState != MachineState_Paused
13029 )
13030 return setError(VBOX_E_INVALID_VM_STATE,
13031 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13032 Global::stringifyMachineState(mData->mMachineState));
13033
13034 ComObjPtr<Progress> pProgress;
13035 pProgress.createObject();
13036 rc = pProgress->init(i_getVirtualBox(),
13037 static_cast<IMachine *>(this) /* aInitiator */,
13038 tr("Saving the execution state of the virtual machine"),
13039 FALSE /* aCancelable */);
13040 if (FAILED(rc))
13041 return rc;
13042
13043 Utf8Str strStateFilePath;
13044 i_composeSavedStateFilename(strStateFilePath);
13045
13046 /* create and start the task on a separate thread (note that it will not
13047 * start working until we release alock) */
13048 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13049 rc = pTask->createThread();
13050 if (FAILED(rc))
13051 return rc;
13052
13053 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13054 i_setMachineState(MachineState_Saving);
13055 i_updateMachineStateOnClient();
13056
13057 pProgress.queryInterfaceTo(aProgress.asOutParam());
13058
13059 return S_OK;
13060}
13061
13062/**
13063 * @note Locks this object for writing.
13064 */
13065HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13066{
13067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13068
13069 HRESULT rc = i_checkStateDependency(MutableStateDep);
13070 if (FAILED(rc)) return rc;
13071
13072 if ( mData->mMachineState != MachineState_PoweredOff
13073 && mData->mMachineState != MachineState_Teleported
13074 && mData->mMachineState != MachineState_Aborted
13075 )
13076 return setError(VBOX_E_INVALID_VM_STATE,
13077 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13078 Global::stringifyMachineState(mData->mMachineState));
13079
13080 com::Utf8Str stateFilePathFull;
13081 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13082 if (RT_FAILURE(vrc))
13083 return setError(VBOX_E_FILE_ERROR,
13084 tr("Invalid saved state file path '%s' (%Rrc)"),
13085 aSavedStateFile.c_str(),
13086 vrc);
13087
13088 mSSData->strStateFilePath = stateFilePathFull;
13089
13090 /* The below i_setMachineState() will detect the state transition and will
13091 * update the settings file */
13092
13093 return i_setMachineState(MachineState_Saved);
13094}
13095
13096/**
13097 * @note Locks this object for writing.
13098 */
13099HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13100{
13101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13102
13103 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13104 if (FAILED(rc)) return rc;
13105
13106 if (mData->mMachineState != MachineState_Saved)
13107 return setError(VBOX_E_INVALID_VM_STATE,
13108 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13109 Global::stringifyMachineState(mData->mMachineState));
13110
13111 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13112
13113 /*
13114 * Saved -> PoweredOff transition will be detected in the SessionMachine
13115 * and properly handled.
13116 */
13117 rc = i_setMachineState(MachineState_PoweredOff);
13118 return rc;
13119}
13120
13121
13122/**
13123 * @note Locks the same as #i_setMachineState() does.
13124 */
13125HRESULT SessionMachine::updateState(MachineState_T aState)
13126{
13127 return i_setMachineState(aState);
13128}
13129
13130/**
13131 * @note Locks this object for writing.
13132 */
13133HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13134{
13135 IProgress *pProgress(aProgress);
13136
13137 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13138
13139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13140
13141 if (mData->mSession.mState != SessionState_Locked)
13142 return VBOX_E_INVALID_OBJECT_STATE;
13143
13144 if (!mData->mSession.mProgress.isNull())
13145 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13146
13147 /* If we didn't reference the NAT network service yet, add a reference to
13148 * force a start */
13149 if (miNATNetworksStarted < 1)
13150 {
13151 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13152 {
13153 BOOL enabled;
13154 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13155 if ( FAILED(hrc)
13156 || !enabled)
13157 continue;
13158
13159 NetworkAttachmentType_T type;
13160 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13161 if ( SUCCEEDED(hrc)
13162 && type == NetworkAttachmentType_NATNetwork)
13163 {
13164 Bstr name;
13165 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13166 if (SUCCEEDED(hrc))
13167 {
13168 Utf8Str strName(name);
13169 LogRel(("VM '%s' starts using NAT network '%s'\n",
13170 mUserData->s.strName.c_str(), strName.c_str()));
13171 mPeer->lockHandle()->unlockWrite();
13172 mParent->i_natNetworkRefInc(strName);
13173#ifdef RT_LOCK_STRICT
13174 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13175#else
13176 mPeer->lockHandle()->lockWrite();
13177#endif
13178 }
13179 }
13180 }
13181 miNATNetworksStarted++;
13182 }
13183
13184 LogFlowThisFunc(("returns S_OK.\n"));
13185 return S_OK;
13186}
13187
13188/**
13189 * @note Locks this object for writing.
13190 */
13191HRESULT SessionMachine::endPowerUp(LONG aResult)
13192{
13193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13194
13195 if (mData->mSession.mState != SessionState_Locked)
13196 return VBOX_E_INVALID_OBJECT_STATE;
13197
13198 /* Finalize the LaunchVMProcess progress object. */
13199 if (mData->mSession.mProgress)
13200 {
13201 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13202 mData->mSession.mProgress.setNull();
13203 }
13204
13205 if (SUCCEEDED((HRESULT)aResult))
13206 {
13207#ifdef VBOX_WITH_RESOURCE_USAGE_API
13208 /* The VM has been powered up successfully, so it makes sense
13209 * now to offer the performance metrics for a running machine
13210 * object. Doing it earlier wouldn't be safe. */
13211 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13212 mData->mSession.mPID);
13213#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13214 }
13215
13216 return S_OK;
13217}
13218
13219/**
13220 * @note Locks this object for writing.
13221 */
13222HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13223{
13224 LogFlowThisFuncEnter();
13225
13226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13227
13228 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13229 E_FAIL);
13230
13231 /* create a progress object to track operation completion */
13232 ComObjPtr<Progress> pProgress;
13233 pProgress.createObject();
13234 pProgress->init(i_getVirtualBox(),
13235 static_cast<IMachine *>(this) /* aInitiator */,
13236 tr("Stopping the virtual machine"),
13237 FALSE /* aCancelable */);
13238
13239 /* fill in the console task data */
13240 mConsoleTaskData.mLastState = mData->mMachineState;
13241 mConsoleTaskData.mProgress = pProgress;
13242
13243 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13244 i_setMachineState(MachineState_Stopping);
13245
13246 pProgress.queryInterfaceTo(aProgress.asOutParam());
13247
13248 return S_OK;
13249}
13250
13251/**
13252 * @note Locks this object for writing.
13253 */
13254HRESULT SessionMachine::endPoweringDown(LONG aResult,
13255 const com::Utf8Str &aErrMsg)
13256{
13257 LogFlowThisFuncEnter();
13258
13259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13260
13261 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13262 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13263 && mConsoleTaskData.mLastState != MachineState_Null,
13264 E_FAIL);
13265
13266 /*
13267 * On failure, set the state to the state we had when BeginPoweringDown()
13268 * was called (this is expected by Console::PowerDown() and the associated
13269 * task). On success the VM process already changed the state to
13270 * MachineState_PoweredOff, so no need to do anything.
13271 */
13272 if (FAILED(aResult))
13273 i_setMachineState(mConsoleTaskData.mLastState);
13274
13275 /* notify the progress object about operation completion */
13276 Assert(mConsoleTaskData.mProgress);
13277 if (SUCCEEDED(aResult))
13278 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13279 else
13280 {
13281 if (aErrMsg.length())
13282 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13283 COM_IIDOF(ISession),
13284 getComponentName(),
13285 aErrMsg.c_str());
13286 else
13287 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13288 }
13289
13290 /* clear out the temporary saved state data */
13291 mConsoleTaskData.mLastState = MachineState_Null;
13292 mConsoleTaskData.mProgress.setNull();
13293
13294 LogFlowThisFuncLeave();
13295 return S_OK;
13296}
13297
13298
13299/**
13300 * Goes through the USB filters of the given machine to see if the given
13301 * device matches any filter or not.
13302 *
13303 * @note Locks the same as USBController::hasMatchingFilter() does.
13304 */
13305HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13306 BOOL *aMatched,
13307 ULONG *aMaskedInterfaces)
13308{
13309 LogFlowThisFunc(("\n"));
13310
13311#ifdef VBOX_WITH_USB
13312 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13313#else
13314 NOREF(aDevice);
13315 NOREF(aMaskedInterfaces);
13316 *aMatched = FALSE;
13317#endif
13318
13319 return S_OK;
13320}
13321
13322/**
13323 * @note Locks the same as Host::captureUSBDevice() does.
13324 */
13325HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13326{
13327 LogFlowThisFunc(("\n"));
13328
13329#ifdef VBOX_WITH_USB
13330 /* if captureDeviceForVM() fails, it must have set extended error info */
13331 clearError();
13332 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13333 if (FAILED(rc)) return rc;
13334
13335 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13336 AssertReturn(service, E_FAIL);
13337 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13338#else
13339 NOREF(aId);
13340 return E_NOTIMPL;
13341#endif
13342}
13343
13344/**
13345 * @note Locks the same as Host::detachUSBDevice() does.
13346 */
13347HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13348 BOOL aDone)
13349{
13350 LogFlowThisFunc(("\n"));
13351
13352#ifdef VBOX_WITH_USB
13353 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13354 AssertReturn(service, E_FAIL);
13355 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13356#else
13357 NOREF(aId);
13358 NOREF(aDone);
13359 return E_NOTIMPL;
13360#endif
13361}
13362
13363/**
13364 * Inserts all machine filters to the USB proxy service and then calls
13365 * Host::autoCaptureUSBDevices().
13366 *
13367 * Called by Console from the VM process upon VM startup.
13368 *
13369 * @note Locks what called methods lock.
13370 */
13371HRESULT SessionMachine::autoCaptureUSBDevices()
13372{
13373 LogFlowThisFunc(("\n"));
13374
13375#ifdef VBOX_WITH_USB
13376 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13377 AssertComRC(rc);
13378 NOREF(rc);
13379
13380 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13381 AssertReturn(service, E_FAIL);
13382 return service->autoCaptureDevicesForVM(this);
13383#else
13384 return S_OK;
13385#endif
13386}
13387
13388/**
13389 * Removes all machine filters from the USB proxy service and then calls
13390 * Host::detachAllUSBDevices().
13391 *
13392 * Called by Console from the VM process upon normal VM termination or by
13393 * SessionMachine::uninit() upon abnormal VM termination (from under the
13394 * Machine/SessionMachine lock).
13395 *
13396 * @note Locks what called methods lock.
13397 */
13398HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13399{
13400 LogFlowThisFunc(("\n"));
13401
13402#ifdef VBOX_WITH_USB
13403 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13404 AssertComRC(rc);
13405 NOREF(rc);
13406
13407 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13408 AssertReturn(service, E_FAIL);
13409 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13410#else
13411 NOREF(aDone);
13412 return S_OK;
13413#endif
13414}
13415
13416/**
13417 * @note Locks this object for writing.
13418 */
13419HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13420 ComPtr<IProgress> &aProgress)
13421{
13422 LogFlowThisFuncEnter();
13423
13424 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13425 /*
13426 * We don't assert below because it might happen that a non-direct session
13427 * informs us it is closed right after we've been uninitialized -- it's ok.
13428 */
13429
13430 /* get IInternalSessionControl interface */
13431 ComPtr<IInternalSessionControl> control(aSession);
13432
13433 ComAssertRet(!control.isNull(), E_INVALIDARG);
13434
13435 /* Creating a Progress object requires the VirtualBox lock, and
13436 * thus locking it here is required by the lock order rules. */
13437 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13438
13439 if (control == mData->mSession.mDirectControl)
13440 {
13441 /* The direct session is being normally closed by the client process
13442 * ----------------------------------------------------------------- */
13443
13444 /* go to the closing state (essential for all open*Session() calls and
13445 * for #i_checkForDeath()) */
13446 Assert(mData->mSession.mState == SessionState_Locked);
13447 mData->mSession.mState = SessionState_Unlocking;
13448
13449 /* set direct control to NULL to release the remote instance */
13450 mData->mSession.mDirectControl.setNull();
13451 LogFlowThisFunc(("Direct control is set to NULL\n"));
13452
13453 if (mData->mSession.mProgress)
13454 {
13455 /* finalize the progress, someone might wait if a frontend
13456 * closes the session before powering on the VM. */
13457 mData->mSession.mProgress->notifyComplete(E_FAIL,
13458 COM_IIDOF(ISession),
13459 getComponentName(),
13460 tr("The VM session was closed before any attempt to power it on"));
13461 mData->mSession.mProgress.setNull();
13462 }
13463
13464 /* Create the progress object the client will use to wait until
13465 * #i_checkForDeath() is called to uninitialize this session object after
13466 * it releases the IPC semaphore.
13467 * Note! Because we're "reusing" mProgress here, this must be a proxy
13468 * object just like for LaunchVMProcess. */
13469 Assert(mData->mSession.mProgress.isNull());
13470 ComObjPtr<ProgressProxy> progress;
13471 progress.createObject();
13472 ComPtr<IUnknown> pPeer(mPeer);
13473 progress->init(mParent, pPeer,
13474 Bstr(tr("Closing session")).raw(),
13475 FALSE /* aCancelable */);
13476 progress.queryInterfaceTo(aProgress.asOutParam());
13477 mData->mSession.mProgress = progress;
13478 }
13479 else
13480 {
13481 /* the remote session is being normally closed */
13482 bool found = false;
13483 for (Data::Session::RemoteControlList::iterator
13484 it = mData->mSession.mRemoteControls.begin();
13485 it != mData->mSession.mRemoteControls.end();
13486 ++it)
13487 {
13488 if (control == *it)
13489 {
13490 found = true;
13491 // This MUST be erase(it), not remove(*it) as the latter
13492 // triggers a very nasty use after free due to the place where
13493 // the value "lives".
13494 mData->mSession.mRemoteControls.erase(it);
13495 break;
13496 }
13497 }
13498 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13499 E_INVALIDARG);
13500 }
13501
13502 /* signal the client watcher thread, because the client is going away */
13503 mParent->i_updateClientWatcher();
13504
13505 LogFlowThisFuncLeave();
13506 return S_OK;
13507}
13508
13509HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13510 std::vector<com::Utf8Str> &aValues,
13511 std::vector<LONG64> &aTimestamps,
13512 std::vector<com::Utf8Str> &aFlags)
13513{
13514 LogFlowThisFunc(("\n"));
13515
13516#ifdef VBOX_WITH_GUEST_PROPS
13517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13518
13519 size_t cEntries = mHWData->mGuestProperties.size();
13520 aNames.resize(cEntries);
13521 aValues.resize(cEntries);
13522 aTimestamps.resize(cEntries);
13523 aFlags.resize(cEntries);
13524
13525 size_t i = 0;
13526 for (HWData::GuestPropertyMap::const_iterator
13527 it = mHWData->mGuestProperties.begin();
13528 it != mHWData->mGuestProperties.end();
13529 ++it, ++i)
13530 {
13531 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13532 aNames[i] = it->first;
13533 aValues[i] = it->second.strValue;
13534 aTimestamps[i] = it->second.mTimestamp;
13535
13536 /* If it is NULL, keep it NULL. */
13537 if (it->second.mFlags)
13538 {
13539 GuestPropWriteFlags(it->second.mFlags, szFlags, sizeof(szFlags));
13540 aFlags[i] = szFlags;
13541 }
13542 else
13543 aFlags[i] = "";
13544 }
13545 return S_OK;
13546#else
13547 ReturnComNotImplemented();
13548#endif
13549}
13550
13551HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13552 const com::Utf8Str &aValue,
13553 LONG64 aTimestamp,
13554 const com::Utf8Str &aFlags)
13555{
13556 LogFlowThisFunc(("\n"));
13557
13558#ifdef VBOX_WITH_GUEST_PROPS
13559 try
13560 {
13561 /*
13562 * Convert input up front.
13563 */
13564 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13565 if (aFlags.length())
13566 {
13567 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13568 AssertRCReturn(vrc, E_INVALIDARG);
13569 }
13570
13571 /*
13572 * Now grab the object lock, validate the state and do the update.
13573 */
13574
13575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13576
13577 if (!Global::IsOnline(mData->mMachineState))
13578 {
13579 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13580 VBOX_E_INVALID_VM_STATE);
13581 }
13582
13583 i_setModified(IsModified_MachineData);
13584 mHWData.backup();
13585
13586 bool fDelete = !aValue.length();
13587 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13588 if (it != mHWData->mGuestProperties.end())
13589 {
13590 if (!fDelete)
13591 {
13592 it->second.strValue = aValue;
13593 it->second.mTimestamp = aTimestamp;
13594 it->second.mFlags = fFlags;
13595 }
13596 else
13597 mHWData->mGuestProperties.erase(it);
13598
13599 mData->mGuestPropertiesModified = TRUE;
13600 }
13601 else if (!fDelete)
13602 {
13603 HWData::GuestProperty prop;
13604 prop.strValue = aValue;
13605 prop.mTimestamp = aTimestamp;
13606 prop.mFlags = fFlags;
13607
13608 mHWData->mGuestProperties[aName] = prop;
13609 mData->mGuestPropertiesModified = TRUE;
13610 }
13611
13612 alock.release();
13613
13614 mParent->i_onGuestPropertyChange(mData->mUuid,
13615 Bstr(aName).raw(),
13616 Bstr(aValue).raw(),
13617 Bstr(aFlags).raw());
13618 }
13619 catch (...)
13620 {
13621 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13622 }
13623 return S_OK;
13624#else
13625 ReturnComNotImplemented();
13626#endif
13627}
13628
13629
13630HRESULT SessionMachine::lockMedia()
13631{
13632 AutoMultiWriteLock2 alock(this->lockHandle(),
13633 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13634
13635 AssertReturn( mData->mMachineState == MachineState_Starting
13636 || mData->mMachineState == MachineState_Restoring
13637 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13638
13639 clearError();
13640 alock.release();
13641 return i_lockMedia();
13642}
13643
13644HRESULT SessionMachine::unlockMedia()
13645{
13646 HRESULT hrc = i_unlockMedia();
13647 return hrc;
13648}
13649
13650HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13651 ComPtr<IMediumAttachment> &aNewAttachment)
13652{
13653 // request the host lock first, since might be calling Host methods for getting host drives;
13654 // next, protect the media tree all the while we're in here, as well as our member variables
13655 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13656 this->lockHandle(),
13657 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13658
13659 IMediumAttachment *iAttach = aAttachment;
13660 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13661
13662 Utf8Str ctrlName;
13663 LONG lPort;
13664 LONG lDevice;
13665 bool fTempEject;
13666 {
13667 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13668
13669 /* Need to query the details first, as the IMediumAttachment reference
13670 * might be to the original settings, which we are going to change. */
13671 ctrlName = pAttach->i_getControllerName();
13672 lPort = pAttach->i_getPort();
13673 lDevice = pAttach->i_getDevice();
13674 fTempEject = pAttach->i_getTempEject();
13675 }
13676
13677 if (!fTempEject)
13678 {
13679 /* Remember previously mounted medium. The medium before taking the
13680 * backup is not necessarily the same thing. */
13681 ComObjPtr<Medium> oldmedium;
13682 oldmedium = pAttach->i_getMedium();
13683
13684 i_setModified(IsModified_Storage);
13685 mMediumAttachments.backup();
13686
13687 // The backup operation makes the pAttach reference point to the
13688 // old settings. Re-get the correct reference.
13689 pAttach = i_findAttachment(*mMediumAttachments.data(),
13690 ctrlName,
13691 lPort,
13692 lDevice);
13693
13694 {
13695 AutoCaller autoAttachCaller(this);
13696 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13697
13698 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13699 if (!oldmedium.isNull())
13700 oldmedium->i_removeBackReference(mData->mUuid);
13701
13702 pAttach->i_updateMedium(NULL);
13703 pAttach->i_updateEjected();
13704 }
13705
13706 i_setModified(IsModified_Storage);
13707 }
13708 else
13709 {
13710 {
13711 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13712 pAttach->i_updateEjected();
13713 }
13714 }
13715
13716 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13717
13718 return S_OK;
13719}
13720
13721HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13722 com::Utf8Str &aResult)
13723{
13724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13725
13726 HRESULT hr = S_OK;
13727
13728 if (!mAuthLibCtx.hAuthLibrary)
13729 {
13730 /* Load the external authentication library. */
13731 Bstr authLibrary;
13732 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13733
13734 Utf8Str filename = authLibrary;
13735
13736 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13737 if (RT_FAILURE(rc))
13738 {
13739 hr = setError(E_FAIL,
13740 tr("Could not load the external authentication library '%s' (%Rrc)"),
13741 filename.c_str(), rc);
13742 }
13743 }
13744
13745 /* The auth library might need the machine lock. */
13746 alock.release();
13747
13748 if (FAILED(hr))
13749 return hr;
13750
13751 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13752 {
13753 enum VRDEAuthParams
13754 {
13755 parmUuid = 1,
13756 parmGuestJudgement,
13757 parmUser,
13758 parmPassword,
13759 parmDomain,
13760 parmClientId
13761 };
13762
13763 AuthResult result = AuthResultAccessDenied;
13764
13765 Guid uuid(aAuthParams[parmUuid]);
13766 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13767 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13768
13769 result = AuthLibAuthenticate(&mAuthLibCtx,
13770 uuid.raw(), guestJudgement,
13771 aAuthParams[parmUser].c_str(),
13772 aAuthParams[parmPassword].c_str(),
13773 aAuthParams[parmDomain].c_str(),
13774 u32ClientId);
13775
13776 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13777 size_t cbPassword = aAuthParams[parmPassword].length();
13778 if (cbPassword)
13779 {
13780 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13781 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13782 }
13783
13784 if (result == AuthResultAccessGranted)
13785 aResult = "granted";
13786 else
13787 aResult = "denied";
13788
13789 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13790 aAuthParams[parmUser].c_str(), aResult.c_str()));
13791 }
13792 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13793 {
13794 enum VRDEAuthDisconnectParams
13795 {
13796 parmUuid = 1,
13797 parmClientId
13798 };
13799
13800 Guid uuid(aAuthParams[parmUuid]);
13801 uint32_t u32ClientId = 0;
13802 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13803 }
13804 else
13805 {
13806 hr = E_INVALIDARG;
13807 }
13808
13809 return hr;
13810}
13811
13812// public methods only for internal purposes
13813/////////////////////////////////////////////////////////////////////////////
13814
13815#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13816/**
13817 * Called from the client watcher thread to check for expected or unexpected
13818 * death of the client process that has a direct session to this machine.
13819 *
13820 * On Win32 and on OS/2, this method is called only when we've got the
13821 * mutex (i.e. the client has either died or terminated normally) so it always
13822 * returns @c true (the client is terminated, the session machine is
13823 * uninitialized).
13824 *
13825 * On other platforms, the method returns @c true if the client process has
13826 * terminated normally or abnormally and the session machine was uninitialized,
13827 * and @c false if the client process is still alive.
13828 *
13829 * @note Locks this object for writing.
13830 */
13831bool SessionMachine::i_checkForDeath()
13832{
13833 Uninit::Reason reason;
13834 bool terminated = false;
13835
13836 /* Enclose autoCaller with a block because calling uninit() from under it
13837 * will deadlock. */
13838 {
13839 AutoCaller autoCaller(this);
13840 if (!autoCaller.isOk())
13841 {
13842 /* return true if not ready, to cause the client watcher to exclude
13843 * the corresponding session from watching */
13844 LogFlowThisFunc(("Already uninitialized!\n"));
13845 return true;
13846 }
13847
13848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13849
13850 /* Determine the reason of death: if the session state is Closing here,
13851 * everything is fine. Otherwise it means that the client did not call
13852 * OnSessionEnd() before it released the IPC semaphore. This may happen
13853 * either because the client process has abnormally terminated, or
13854 * because it simply forgot to call ISession::Close() before exiting. We
13855 * threat the latter also as an abnormal termination (see
13856 * Session::uninit() for details). */
13857 reason = mData->mSession.mState == SessionState_Unlocking ?
13858 Uninit::Normal :
13859 Uninit::Abnormal;
13860
13861 if (mClientToken)
13862 terminated = mClientToken->release();
13863 } /* AutoCaller block */
13864
13865 if (terminated)
13866 uninit(reason);
13867
13868 return terminated;
13869}
13870
13871void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13872{
13873 LogFlowThisFunc(("\n"));
13874
13875 strTokenId.setNull();
13876
13877 AutoCaller autoCaller(this);
13878 AssertComRCReturnVoid(autoCaller.rc());
13879
13880 Assert(mClientToken);
13881 if (mClientToken)
13882 mClientToken->getId(strTokenId);
13883}
13884#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13885IToken *SessionMachine::i_getToken()
13886{
13887 LogFlowThisFunc(("\n"));
13888
13889 AutoCaller autoCaller(this);
13890 AssertComRCReturn(autoCaller.rc(), NULL);
13891
13892 Assert(mClientToken);
13893 if (mClientToken)
13894 return mClientToken->getToken();
13895 else
13896 return NULL;
13897}
13898#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13899
13900Machine::ClientToken *SessionMachine::i_getClientToken()
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 AutoCaller autoCaller(this);
13905 AssertComRCReturn(autoCaller.rc(), NULL);
13906
13907 return mClientToken;
13908}
13909
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13915{
13916 LogFlowThisFunc(("\n"));
13917
13918 AutoCaller autoCaller(this);
13919 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13920
13921 ComPtr<IInternalSessionControl> directControl;
13922 {
13923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13924 if (mData->mSession.mLockType == LockType_VM)
13925 directControl = mData->mSession.mDirectControl;
13926 }
13927
13928 /* ignore notifications sent after #OnSessionEnd() is called */
13929 if (!directControl)
13930 return S_OK;
13931
13932 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13933}
13934
13935/**
13936 * @note Locks this object for reading.
13937 */
13938HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13939 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13940 IN_BSTR aGuestIp, LONG aGuestPort)
13941{
13942 LogFlowThisFunc(("\n"));
13943
13944 AutoCaller autoCaller(this);
13945 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13946
13947 ComPtr<IInternalSessionControl> directControl;
13948 {
13949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13950 if (mData->mSession.mLockType == LockType_VM)
13951 directControl = mData->mSession.mDirectControl;
13952 }
13953
13954 /* ignore notifications sent after #OnSessionEnd() is called */
13955 if (!directControl)
13956 return S_OK;
13957 /*
13958 * instead acting like callback we ask IVirtualBox deliver corresponding event
13959 */
13960
13961 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13962 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13963 return S_OK;
13964}
13965
13966/**
13967 * @note Locks this object for reading.
13968 */
13969HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 AutoCaller autoCaller(this);
13974 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13975
13976 ComPtr<IInternalSessionControl> directControl;
13977 {
13978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13979 if (mData->mSession.mLockType == LockType_VM)
13980 directControl = mData->mSession.mDirectControl;
13981 }
13982
13983 /* ignore notifications sent after #OnSessionEnd() is called */
13984 if (!directControl)
13985 return S_OK;
13986
13987 return directControl->OnAudioAdapterChange(audioAdapter);
13988}
13989
13990/**
13991 * @note Locks this object for reading.
13992 */
13993HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13999
14000 ComPtr<IInternalSessionControl> directControl;
14001 {
14002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14003 if (mData->mSession.mLockType == LockType_VM)
14004 directControl = mData->mSession.mDirectControl;
14005 }
14006
14007 /* ignore notifications sent after #OnSessionEnd() is called */
14008 if (!directControl)
14009 return S_OK;
14010
14011 return directControl->OnSerialPortChange(serialPort);
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 /* ignore notifications sent after #OnSessionEnd() is called */
14032 if (!directControl)
14033 return S_OK;
14034
14035 return directControl->OnParallelPortChange(parallelPort);
14036}
14037
14038/**
14039 * @note Locks this object for reading.
14040 */
14041HRESULT SessionMachine::i_onStorageControllerChange()
14042{
14043 LogFlowThisFunc(("\n"));
14044
14045 AutoCaller autoCaller(this);
14046 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14047
14048 ComPtr<IInternalSessionControl> directControl;
14049 {
14050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14051 if (mData->mSession.mLockType == LockType_VM)
14052 directControl = mData->mSession.mDirectControl;
14053 }
14054
14055 /* ignore notifications sent after #OnSessionEnd() is called */
14056 if (!directControl)
14057 return S_OK;
14058
14059 return directControl->OnStorageControllerChange();
14060}
14061
14062/**
14063 * @note Locks this object for reading.
14064 */
14065HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14066{
14067 LogFlowThisFunc(("\n"));
14068
14069 AutoCaller autoCaller(this);
14070 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14071
14072 ComPtr<IInternalSessionControl> directControl;
14073 {
14074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14075 if (mData->mSession.mLockType == LockType_VM)
14076 directControl = mData->mSession.mDirectControl;
14077 }
14078
14079 /* ignore notifications sent after #OnSessionEnd() is called */
14080 if (!directControl)
14081 return S_OK;
14082
14083 return directControl->OnMediumChange(aAttachment, aForce);
14084}
14085
14086/**
14087 * @note Locks this object for reading.
14088 */
14089HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14090{
14091 LogFlowThisFunc(("\n"));
14092
14093 AutoCaller autoCaller(this);
14094 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14095
14096 ComPtr<IInternalSessionControl> directControl;
14097 {
14098 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14099 if (mData->mSession.mLockType == LockType_VM)
14100 directControl = mData->mSession.mDirectControl;
14101 }
14102
14103 /* ignore notifications sent after #OnSessionEnd() is called */
14104 if (!directControl)
14105 return S_OK;
14106
14107 return directControl->OnCPUChange(aCPU, aRemove);
14108}
14109
14110HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14111{
14112 LogFlowThisFunc(("\n"));
14113
14114 AutoCaller autoCaller(this);
14115 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14116
14117 ComPtr<IInternalSessionControl> directControl;
14118 {
14119 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14120 if (mData->mSession.mLockType == LockType_VM)
14121 directControl = mData->mSession.mDirectControl;
14122 }
14123
14124 /* ignore notifications sent after #OnSessionEnd() is called */
14125 if (!directControl)
14126 return S_OK;
14127
14128 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14129}
14130
14131/**
14132 * @note Locks this object for reading.
14133 */
14134HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14135{
14136 LogFlowThisFunc(("\n"));
14137
14138 AutoCaller autoCaller(this);
14139 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14140
14141 ComPtr<IInternalSessionControl> directControl;
14142 {
14143 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14144 if (mData->mSession.mLockType == LockType_VM)
14145 directControl = mData->mSession.mDirectControl;
14146 }
14147
14148 /* ignore notifications sent after #OnSessionEnd() is called */
14149 if (!directControl)
14150 return S_OK;
14151
14152 return directControl->OnVRDEServerChange(aRestart);
14153}
14154
14155/**
14156 * @note Locks this object for reading.
14157 */
14158HRESULT SessionMachine::i_onVideoCaptureChange()
14159{
14160 LogFlowThisFunc(("\n"));
14161
14162 AutoCaller autoCaller(this);
14163 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14164
14165 ComPtr<IInternalSessionControl> directControl;
14166 {
14167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14168 if (mData->mSession.mLockType == LockType_VM)
14169 directControl = mData->mSession.mDirectControl;
14170 }
14171
14172 /* ignore notifications sent after #OnSessionEnd() is called */
14173 if (!directControl)
14174 return S_OK;
14175
14176 return directControl->OnVideoCaptureChange();
14177}
14178
14179/**
14180 * @note Locks this object for reading.
14181 */
14182HRESULT SessionMachine::i_onUSBControllerChange()
14183{
14184 LogFlowThisFunc(("\n"));
14185
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 ComPtr<IInternalSessionControl> directControl;
14190 {
14191 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14192 if (mData->mSession.mLockType == LockType_VM)
14193 directControl = mData->mSession.mDirectControl;
14194 }
14195
14196 /* ignore notifications sent after #OnSessionEnd() is called */
14197 if (!directControl)
14198 return S_OK;
14199
14200 return directControl->OnUSBControllerChange();
14201}
14202
14203/**
14204 * @note Locks this object for reading.
14205 */
14206HRESULT SessionMachine::i_onSharedFolderChange()
14207{
14208 LogFlowThisFunc(("\n"));
14209
14210 AutoCaller autoCaller(this);
14211 AssertComRCReturnRC(autoCaller.rc());
14212
14213 ComPtr<IInternalSessionControl> directControl;
14214 {
14215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14216 if (mData->mSession.mLockType == LockType_VM)
14217 directControl = mData->mSession.mDirectControl;
14218 }
14219
14220 /* ignore notifications sent after #OnSessionEnd() is called */
14221 if (!directControl)
14222 return S_OK;
14223
14224 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14225}
14226
14227/**
14228 * @note Locks this object for reading.
14229 */
14230HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14231{
14232 LogFlowThisFunc(("\n"));
14233
14234 AutoCaller autoCaller(this);
14235 AssertComRCReturnRC(autoCaller.rc());
14236
14237 ComPtr<IInternalSessionControl> directControl;
14238 {
14239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14240 if (mData->mSession.mLockType == LockType_VM)
14241 directControl = mData->mSession.mDirectControl;
14242 }
14243
14244 /* ignore notifications sent after #OnSessionEnd() is called */
14245 if (!directControl)
14246 return S_OK;
14247
14248 return directControl->OnClipboardModeChange(aClipboardMode);
14249}
14250
14251/**
14252 * @note Locks this object for reading.
14253 */
14254HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14255{
14256 LogFlowThisFunc(("\n"));
14257
14258 AutoCaller autoCaller(this);
14259 AssertComRCReturnRC(autoCaller.rc());
14260
14261 ComPtr<IInternalSessionControl> directControl;
14262 {
14263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14264 if (mData->mSession.mLockType == LockType_VM)
14265 directControl = mData->mSession.mDirectControl;
14266 }
14267
14268 /* ignore notifications sent after #OnSessionEnd() is called */
14269 if (!directControl)
14270 return S_OK;
14271
14272 return directControl->OnDnDModeChange(aDnDMode);
14273}
14274
14275/**
14276 * @note Locks this object for reading.
14277 */
14278HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14279{
14280 LogFlowThisFunc(("\n"));
14281
14282 AutoCaller autoCaller(this);
14283 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14284
14285 ComPtr<IInternalSessionControl> directControl;
14286 {
14287 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14288 if (mData->mSession.mLockType == LockType_VM)
14289 directControl = mData->mSession.mDirectControl;
14290 }
14291
14292 /* ignore notifications sent after #OnSessionEnd() is called */
14293 if (!directControl)
14294 return S_OK;
14295
14296 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14297}
14298
14299/**
14300 * @note Locks this object for reading.
14301 */
14302HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14303{
14304 LogFlowThisFunc(("\n"));
14305
14306 AutoCaller autoCaller(this);
14307 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14308
14309 ComPtr<IInternalSessionControl> directControl;
14310 {
14311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14312 if (mData->mSession.mLockType == LockType_VM)
14313 directControl = mData->mSession.mDirectControl;
14314 }
14315
14316 /* ignore notifications sent after #OnSessionEnd() is called */
14317 if (!directControl)
14318 return S_OK;
14319
14320 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14321}
14322
14323/**
14324 * Returns @c true if this machine's USB controller reports it has a matching
14325 * filter for the given USB device and @c false otherwise.
14326 *
14327 * @note locks this object for reading.
14328 */
14329bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14330{
14331 AutoCaller autoCaller(this);
14332 /* silently return if not ready -- this method may be called after the
14333 * direct machine session has been called */
14334 if (!autoCaller.isOk())
14335 return false;
14336
14337#ifdef VBOX_WITH_USB
14338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14339
14340 switch (mData->mMachineState)
14341 {
14342 case MachineState_Starting:
14343 case MachineState_Restoring:
14344 case MachineState_TeleportingIn:
14345 case MachineState_Paused:
14346 case MachineState_Running:
14347 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14348 * elsewhere... */
14349 alock.release();
14350 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14351 default: break;
14352 }
14353#else
14354 NOREF(aDevice);
14355 NOREF(aMaskedIfs);
14356#endif
14357 return false;
14358}
14359
14360/**
14361 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14362 */
14363HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14364 IVirtualBoxErrorInfo *aError,
14365 ULONG aMaskedIfs,
14366 const com::Utf8Str &aCaptureFilename)
14367{
14368 LogFlowThisFunc(("\n"));
14369
14370 AutoCaller autoCaller(this);
14371
14372 /* This notification may happen after the machine object has been
14373 * uninitialized (the session was closed), so don't assert. */
14374 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14375
14376 ComPtr<IInternalSessionControl> directControl;
14377 {
14378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14379 if (mData->mSession.mLockType == LockType_VM)
14380 directControl = mData->mSession.mDirectControl;
14381 }
14382
14383 /* fail on notifications sent after #OnSessionEnd() is called, it is
14384 * expected by the caller */
14385 if (!directControl)
14386 return E_FAIL;
14387
14388 /* No locks should be held at this point. */
14389 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14390 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14391
14392 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14393}
14394
14395/**
14396 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14397 */
14398HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14399 IVirtualBoxErrorInfo *aError)
14400{
14401 LogFlowThisFunc(("\n"));
14402
14403 AutoCaller autoCaller(this);
14404
14405 /* This notification may happen after the machine object has been
14406 * uninitialized (the session was closed), so don't assert. */
14407 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14408
14409 ComPtr<IInternalSessionControl> directControl;
14410 {
14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14412 if (mData->mSession.mLockType == LockType_VM)
14413 directControl = mData->mSession.mDirectControl;
14414 }
14415
14416 /* fail on notifications sent after #OnSessionEnd() is called, it is
14417 * expected by the caller */
14418 if (!directControl)
14419 return E_FAIL;
14420
14421 /* No locks should be held at this point. */
14422 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14423 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14424
14425 return directControl->OnUSBDeviceDetach(aId, aError);
14426}
14427
14428// protected methods
14429/////////////////////////////////////////////////////////////////////////////
14430
14431/**
14432 * Deletes the given file if it is no longer in use by either the current machine state
14433 * (if the machine is "saved") or any of the machine's snapshots.
14434 *
14435 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14436 * but is different for each SnapshotMachine. When calling this, the order of calling this
14437 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14438 * is therefore critical. I know, it's all rather messy.
14439 *
14440 * @param strStateFile
14441 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14442 * the test for whether the saved state file is in use.
14443 */
14444void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14445 Snapshot *pSnapshotToIgnore)
14446{
14447 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14448 if ( (strStateFile.isNotEmpty())
14449 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14450 )
14451 // ... and it must also not be shared with other snapshots
14452 if ( !mData->mFirstSnapshot
14453 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14454 // this checks the SnapshotMachine's state file paths
14455 )
14456 RTFileDelete(strStateFile.c_str());
14457}
14458
14459/**
14460 * Locks the attached media.
14461 *
14462 * All attached hard disks are locked for writing and DVD/floppy are locked for
14463 * reading. Parents of attached hard disks (if any) are locked for reading.
14464 *
14465 * This method also performs accessibility check of all media it locks: if some
14466 * media is inaccessible, the method will return a failure and a bunch of
14467 * extended error info objects per each inaccessible medium.
14468 *
14469 * Note that this method is atomic: if it returns a success, all media are
14470 * locked as described above; on failure no media is locked at all (all
14471 * succeeded individual locks will be undone).
14472 *
14473 * The caller is responsible for doing the necessary state sanity checks.
14474 *
14475 * The locks made by this method must be undone by calling #unlockMedia() when
14476 * no more needed.
14477 */
14478HRESULT SessionMachine::i_lockMedia()
14479{
14480 AutoCaller autoCaller(this);
14481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14482
14483 AutoMultiWriteLock2 alock(this->lockHandle(),
14484 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14485
14486 /* bail out if trying to lock things with already set up locking */
14487 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14488
14489 MultiResult mrc(S_OK);
14490
14491 /* Collect locking information for all medium objects attached to the VM. */
14492 for (MediumAttachmentList::const_iterator
14493 it = mMediumAttachments->begin();
14494 it != mMediumAttachments->end();
14495 ++it)
14496 {
14497 MediumAttachment *pAtt = *it;
14498 DeviceType_T devType = pAtt->i_getType();
14499 Medium *pMedium = pAtt->i_getMedium();
14500
14501 MediumLockList *pMediumLockList(new MediumLockList());
14502 // There can be attachments without a medium (floppy/dvd), and thus
14503 // it's impossible to create a medium lock list. It still makes sense
14504 // to have the empty medium lock list in the map in case a medium is
14505 // attached later.
14506 if (pMedium != NULL)
14507 {
14508 MediumType_T mediumType = pMedium->i_getType();
14509 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14510 || mediumType == MediumType_Shareable;
14511 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14512
14513 alock.release();
14514 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14515 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14516 false /* fMediumLockWriteAll */,
14517 NULL,
14518 *pMediumLockList);
14519 alock.acquire();
14520 if (FAILED(mrc))
14521 {
14522 delete pMediumLockList;
14523 mData->mSession.mLockedMedia.Clear();
14524 break;
14525 }
14526 }
14527
14528 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14529 if (FAILED(rc))
14530 {
14531 mData->mSession.mLockedMedia.Clear();
14532 mrc = setError(rc,
14533 tr("Collecting locking information for all attached media failed"));
14534 break;
14535 }
14536 }
14537
14538 if (SUCCEEDED(mrc))
14539 {
14540 /* Now lock all media. If this fails, nothing is locked. */
14541 alock.release();
14542 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14543 alock.acquire();
14544 if (FAILED(rc))
14545 {
14546 mrc = setError(rc,
14547 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14548 }
14549 }
14550
14551 return mrc;
14552}
14553
14554/**
14555 * Undoes the locks made by by #lockMedia().
14556 */
14557HRESULT SessionMachine::i_unlockMedia()
14558{
14559 AutoCaller autoCaller(this);
14560 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14561
14562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14563
14564 /* we may be holding important error info on the current thread;
14565 * preserve it */
14566 ErrorInfoKeeper eik;
14567
14568 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14569 AssertComRC(rc);
14570 return rc;
14571}
14572
14573/**
14574 * Helper to change the machine state (reimplementation).
14575 *
14576 * @note Locks this object for writing.
14577 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14578 * it can cause crashes in random places due to unexpectedly committing
14579 * the current settings. The caller is responsible for that. The call
14580 * to saveStateSettings is fine, because this method does not commit.
14581 */
14582HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14583{
14584 LogFlowThisFuncEnter();
14585 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14586
14587 AutoCaller autoCaller(this);
14588 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14589
14590 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14591
14592 MachineState_T oldMachineState = mData->mMachineState;
14593
14594 AssertMsgReturn(oldMachineState != aMachineState,
14595 ("oldMachineState=%s, aMachineState=%s\n",
14596 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14597 E_FAIL);
14598
14599 HRESULT rc = S_OK;
14600
14601 int stsFlags = 0;
14602 bool deleteSavedState = false;
14603
14604 /* detect some state transitions */
14605
14606 if ( ( oldMachineState == MachineState_Saved
14607 && aMachineState == MachineState_Restoring)
14608 || ( ( oldMachineState == MachineState_PoweredOff
14609 || oldMachineState == MachineState_Teleported
14610 || oldMachineState == MachineState_Aborted
14611 )
14612 && ( aMachineState == MachineState_TeleportingIn
14613 || aMachineState == MachineState_Starting
14614 )
14615 )
14616 )
14617 {
14618 /* The EMT thread is about to start */
14619
14620 /* Nothing to do here for now... */
14621
14622 /// @todo NEWMEDIA don't let mDVDDrive and other children
14623 /// change anything when in the Starting/Restoring state
14624 }
14625 else if ( ( oldMachineState == MachineState_Running
14626 || oldMachineState == MachineState_Paused
14627 || oldMachineState == MachineState_Teleporting
14628 || oldMachineState == MachineState_OnlineSnapshotting
14629 || oldMachineState == MachineState_LiveSnapshotting
14630 || oldMachineState == MachineState_Stuck
14631 || oldMachineState == MachineState_Starting
14632 || oldMachineState == MachineState_Stopping
14633 || oldMachineState == MachineState_Saving
14634 || oldMachineState == MachineState_Restoring
14635 || oldMachineState == MachineState_TeleportingPausedVM
14636 || oldMachineState == MachineState_TeleportingIn
14637 )
14638 && ( aMachineState == MachineState_PoweredOff
14639 || aMachineState == MachineState_Saved
14640 || aMachineState == MachineState_Teleported
14641 || aMachineState == MachineState_Aborted
14642 )
14643 )
14644 {
14645 /* The EMT thread has just stopped, unlock attached media. Note that as
14646 * opposed to locking that is done from Console, we do unlocking here
14647 * because the VM process may have aborted before having a chance to
14648 * properly unlock all media it locked. */
14649
14650 unlockMedia();
14651 }
14652
14653 if (oldMachineState == MachineState_Restoring)
14654 {
14655 if (aMachineState != MachineState_Saved)
14656 {
14657 /*
14658 * delete the saved state file once the machine has finished
14659 * restoring from it (note that Console sets the state from
14660 * Restoring to Saved if the VM couldn't restore successfully,
14661 * to give the user an ability to fix an error and retry --
14662 * we keep the saved state file in this case)
14663 */
14664 deleteSavedState = true;
14665 }
14666 }
14667 else if ( oldMachineState == MachineState_Saved
14668 && ( aMachineState == MachineState_PoweredOff
14669 || aMachineState == MachineState_Aborted
14670 || aMachineState == MachineState_Teleported
14671 )
14672 )
14673 {
14674 /*
14675 * delete the saved state after SessionMachine::ForgetSavedState() is called
14676 * or if the VM process (owning a direct VM session) crashed while the
14677 * VM was Saved
14678 */
14679
14680 /// @todo (dmik)
14681 // Not sure that deleting the saved state file just because of the
14682 // client death before it attempted to restore the VM is a good
14683 // thing. But when it crashes we need to go to the Aborted state
14684 // which cannot have the saved state file associated... The only
14685 // way to fix this is to make the Aborted condition not a VM state
14686 // but a bool flag: i.e., when a crash occurs, set it to true and
14687 // change the state to PoweredOff or Saved depending on the
14688 // saved state presence.
14689
14690 deleteSavedState = true;
14691 mData->mCurrentStateModified = TRUE;
14692 stsFlags |= SaveSTS_CurStateModified;
14693 }
14694
14695 if ( aMachineState == MachineState_Starting
14696 || aMachineState == MachineState_Restoring
14697 || aMachineState == MachineState_TeleportingIn
14698 )
14699 {
14700 /* set the current state modified flag to indicate that the current
14701 * state is no more identical to the state in the
14702 * current snapshot */
14703 if (!mData->mCurrentSnapshot.isNull())
14704 {
14705 mData->mCurrentStateModified = TRUE;
14706 stsFlags |= SaveSTS_CurStateModified;
14707 }
14708 }
14709
14710 if (deleteSavedState)
14711 {
14712 if (mRemoveSavedState)
14713 {
14714 Assert(!mSSData->strStateFilePath.isEmpty());
14715
14716 // it is safe to delete the saved state file if ...
14717 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14718 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14719 // ... none of the snapshots share the saved state file
14720 )
14721 RTFileDelete(mSSData->strStateFilePath.c_str());
14722 }
14723
14724 mSSData->strStateFilePath.setNull();
14725 stsFlags |= SaveSTS_StateFilePath;
14726 }
14727
14728 /* redirect to the underlying peer machine */
14729 mPeer->i_setMachineState(aMachineState);
14730
14731 if ( oldMachineState != MachineState_RestoringSnapshot
14732 && ( aMachineState == MachineState_PoweredOff
14733 || aMachineState == MachineState_Teleported
14734 || aMachineState == MachineState_Aborted
14735 || aMachineState == MachineState_Saved))
14736 {
14737 /* the machine has stopped execution
14738 * (or the saved state file was adopted) */
14739 stsFlags |= SaveSTS_StateTimeStamp;
14740 }
14741
14742 if ( ( oldMachineState == MachineState_PoweredOff
14743 || oldMachineState == MachineState_Aborted
14744 || oldMachineState == MachineState_Teleported
14745 )
14746 && aMachineState == MachineState_Saved)
14747 {
14748 /* the saved state file was adopted */
14749 Assert(!mSSData->strStateFilePath.isEmpty());
14750 stsFlags |= SaveSTS_StateFilePath;
14751 }
14752
14753#ifdef VBOX_WITH_GUEST_PROPS
14754 if ( aMachineState == MachineState_PoweredOff
14755 || aMachineState == MachineState_Aborted
14756 || aMachineState == MachineState_Teleported)
14757 {
14758 /* Make sure any transient guest properties get removed from the
14759 * property store on shutdown. */
14760 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14761
14762 /* remove it from the settings representation */
14763 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14764 for (settings::GuestPropertiesList::iterator
14765 it = llGuestProperties.begin();
14766 it != llGuestProperties.end();
14767 /*nothing*/)
14768 {
14769 const settings::GuestProperty &prop = *it;
14770 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14771 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14772 {
14773 it = llGuestProperties.erase(it);
14774 fNeedsSaving = true;
14775 }
14776 else
14777 {
14778 ++it;
14779 }
14780 }
14781
14782 /* Additionally remove it from the HWData representation. Required to
14783 * keep everything in sync, as this is what the API keeps using. */
14784 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14785 for (HWData::GuestPropertyMap::iterator
14786 it = llHWGuestProperties.begin();
14787 it != llHWGuestProperties.end();
14788 /*nothing*/)
14789 {
14790 uint32_t fFlags = it->second.mFlags;
14791 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14792 {
14793 /* iterator where we need to continue after the erase call
14794 * (C++03 is a fact still, and it doesn't return the iterator
14795 * which would allow continuing) */
14796 HWData::GuestPropertyMap::iterator it2 = it;
14797 ++it2;
14798 llHWGuestProperties.erase(it);
14799 it = it2;
14800 fNeedsSaving = true;
14801 }
14802 else
14803 {
14804 ++it;
14805 }
14806 }
14807
14808 if (fNeedsSaving)
14809 {
14810 mData->mCurrentStateModified = TRUE;
14811 stsFlags |= SaveSTS_CurStateModified;
14812 }
14813 }
14814#endif /* VBOX_WITH_GUEST_PROPS */
14815
14816 rc = i_saveStateSettings(stsFlags);
14817
14818 if ( ( oldMachineState != MachineState_PoweredOff
14819 && oldMachineState != MachineState_Aborted
14820 && oldMachineState != MachineState_Teleported
14821 )
14822 && ( aMachineState == MachineState_PoweredOff
14823 || aMachineState == MachineState_Aborted
14824 || aMachineState == MachineState_Teleported
14825 )
14826 )
14827 {
14828 /* we've been shut down for any reason */
14829 /* no special action so far */
14830 }
14831
14832 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14833 LogFlowThisFuncLeave();
14834 return rc;
14835}
14836
14837/**
14838 * Sends the current machine state value to the VM process.
14839 *
14840 * @note Locks this object for reading, then calls a client process.
14841 */
14842HRESULT SessionMachine::i_updateMachineStateOnClient()
14843{
14844 AutoCaller autoCaller(this);
14845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14846
14847 ComPtr<IInternalSessionControl> directControl;
14848 {
14849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14850 AssertReturn(!!mData, E_FAIL);
14851 if (mData->mSession.mLockType == LockType_VM)
14852 directControl = mData->mSession.mDirectControl;
14853
14854 /* directControl may be already set to NULL here in #OnSessionEnd()
14855 * called too early by the direct session process while there is still
14856 * some operation (like deleting the snapshot) in progress. The client
14857 * process in this case is waiting inside Session::close() for the
14858 * "end session" process object to complete, while #uninit() called by
14859 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14860 * operation to complete. For now, we accept this inconsistent behavior
14861 * and simply do nothing here. */
14862
14863 if (mData->mSession.mState == SessionState_Unlocking)
14864 return S_OK;
14865 }
14866
14867 /* ignore notifications sent after #OnSessionEnd() is called */
14868 if (!directControl)
14869 return S_OK;
14870
14871 return directControl->UpdateMachineState(mData->mMachineState);
14872}
14873
14874
14875/*static*/
14876HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14877{
14878 va_list args;
14879 va_start(args, pcszMsg);
14880 HRESULT rc = setErrorInternal(aResultCode,
14881 getStaticClassIID(),
14882 getStaticComponentName(),
14883 Utf8Str(pcszMsg, args),
14884 false /* aWarning */,
14885 true /* aLogIt */);
14886 va_end(args);
14887 return rc;
14888}
14889
14890
14891HRESULT Machine::updateState(MachineState_T aState)
14892{
14893 NOREF(aState);
14894 ReturnComNotImplemented();
14895}
14896
14897HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14898{
14899 NOREF(aProgress);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::endPowerUp(LONG aResult)
14904{
14905 NOREF(aResult);
14906 ReturnComNotImplemented();
14907}
14908
14909HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14910{
14911 NOREF(aProgress);
14912 ReturnComNotImplemented();
14913}
14914
14915HRESULT Machine::endPoweringDown(LONG aResult,
14916 const com::Utf8Str &aErrMsg)
14917{
14918 NOREF(aResult);
14919 NOREF(aErrMsg);
14920 ReturnComNotImplemented();
14921}
14922
14923HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14924 BOOL *aMatched,
14925 ULONG *aMaskedInterfaces)
14926{
14927 NOREF(aDevice);
14928 NOREF(aMatched);
14929 NOREF(aMaskedInterfaces);
14930 ReturnComNotImplemented();
14931
14932}
14933
14934HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14935{
14936 NOREF(aId); NOREF(aCaptureFilename);
14937 ReturnComNotImplemented();
14938}
14939
14940HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14941 BOOL aDone)
14942{
14943 NOREF(aId);
14944 NOREF(aDone);
14945 ReturnComNotImplemented();
14946}
14947
14948HRESULT Machine::autoCaptureUSBDevices()
14949{
14950 ReturnComNotImplemented();
14951}
14952
14953HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14954{
14955 NOREF(aDone);
14956 ReturnComNotImplemented();
14957}
14958
14959HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14960 ComPtr<IProgress> &aProgress)
14961{
14962 NOREF(aSession);
14963 NOREF(aProgress);
14964 ReturnComNotImplemented();
14965}
14966
14967HRESULT Machine::finishOnlineMergeMedium()
14968{
14969 ReturnComNotImplemented();
14970}
14971
14972HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14973 std::vector<com::Utf8Str> &aValues,
14974 std::vector<LONG64> &aTimestamps,
14975 std::vector<com::Utf8Str> &aFlags)
14976{
14977 NOREF(aNames);
14978 NOREF(aValues);
14979 NOREF(aTimestamps);
14980 NOREF(aFlags);
14981 ReturnComNotImplemented();
14982}
14983
14984HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14985 const com::Utf8Str &aValue,
14986 LONG64 aTimestamp,
14987 const com::Utf8Str &aFlags)
14988{
14989 NOREF(aName);
14990 NOREF(aValue);
14991 NOREF(aTimestamp);
14992 NOREF(aFlags);
14993 ReturnComNotImplemented();
14994}
14995
14996HRESULT Machine::lockMedia()
14997{
14998 ReturnComNotImplemented();
14999}
15000
15001HRESULT Machine::unlockMedia()
15002{
15003 ReturnComNotImplemented();
15004}
15005
15006HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15007 ComPtr<IMediumAttachment> &aNewAttachment)
15008{
15009 NOREF(aAttachment);
15010 NOREF(aNewAttachment);
15011 ReturnComNotImplemented();
15012}
15013
15014HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15015 ULONG aCpuUser,
15016 ULONG aCpuKernel,
15017 ULONG aCpuIdle,
15018 ULONG aMemTotal,
15019 ULONG aMemFree,
15020 ULONG aMemBalloon,
15021 ULONG aMemShared,
15022 ULONG aMemCache,
15023 ULONG aPagedTotal,
15024 ULONG aMemAllocTotal,
15025 ULONG aMemFreeTotal,
15026 ULONG aMemBalloonTotal,
15027 ULONG aMemSharedTotal,
15028 ULONG aVmNetRx,
15029 ULONG aVmNetTx)
15030{
15031 NOREF(aValidStats);
15032 NOREF(aCpuUser);
15033 NOREF(aCpuKernel);
15034 NOREF(aCpuIdle);
15035 NOREF(aMemTotal);
15036 NOREF(aMemFree);
15037 NOREF(aMemBalloon);
15038 NOREF(aMemShared);
15039 NOREF(aMemCache);
15040 NOREF(aPagedTotal);
15041 NOREF(aMemAllocTotal);
15042 NOREF(aMemFreeTotal);
15043 NOREF(aMemBalloonTotal);
15044 NOREF(aMemSharedTotal);
15045 NOREF(aVmNetRx);
15046 NOREF(aVmNetTx);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15051 com::Utf8Str &aResult)
15052{
15053 NOREF(aAuthParams);
15054 NOREF(aResult);
15055 ReturnComNotImplemented();
15056}
15057
15058HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15059{
15060 NOREF(aFlags);
15061 ReturnComNotImplemented();
15062}
15063
15064/* This isn't handled entirely by the wrapper generator yet. */
15065#ifdef VBOX_WITH_XPCOM
15066NS_DECL_CLASSINFO(SessionMachine)
15067NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15068
15069NS_DECL_CLASSINFO(SnapshotMachine)
15070NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15071#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