VirtualBox

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

Last change on this file since 67738 was 67738, checked in by vboxsync, 8 years ago

Machine.h: Avoid dragging in UnattendedImpl.h as it causes most of VBoxSVC to be recompiled each time its modified.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 519.2 KB
Line 
1/* $Id: MachineImpl.cpp 67738 2017-06-30 14:49:40Z 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#ifdef VBOX_WITH_UNATTENDED
47# include "UnattendedImpl.h"
48#endif
49#include "SystemPropertiesImpl.h"
50
51// generated header
52#include "VBoxEvents.h"
53
54#ifdef VBOX_WITH_USB
55# include "USBProxyService.h"
56#endif
57
58#include "AutoCaller.h"
59#include "HashedPw.h"
60#include "Performance.h"
61
62#include <iprt/asm.h>
63#include <iprt/path.h>
64#include <iprt/dir.h>
65#include <iprt/env.h>
66#include <iprt/lockvalidator.h>
67#include <iprt/process.h>
68#include <iprt/cpp/utils.h>
69#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
70#include <iprt/sha.h>
71#include <iprt/string.h>
72
73#include <VBox/com/array.h>
74#include <VBox/com/list.h>
75
76#include <VBox/err.h>
77#include <VBox/param.h>
78#include <VBox/settings.h>
79#include <VBox/vmm/ssm.h>
80
81#ifdef VBOX_WITH_GUEST_PROPS
82# include <VBox/HostServices/GuestPropertySvc.h>
83# include <VBox/com/array.h>
84#endif
85
86#include "VBox/com/MultiResult.h"
87
88#include <algorithm>
89
90#ifdef VBOX_WITH_DTRACE_R3_MAIN
91# include "dtrace/VBoxAPI.h"
92#endif
93
94#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
95# define HOSTSUFF_EXE ".exe"
96#else /* !RT_OS_WINDOWS */
97# define HOSTSUFF_EXE ""
98#endif /* !RT_OS_WINDOWS */
99
100// defines / prototypes
101/////////////////////////////////////////////////////////////////////////////
102
103/////////////////////////////////////////////////////////////////////////////
104// Machine::Data structure
105/////////////////////////////////////////////////////////////////////////////
106
107Machine::Data::Data()
108{
109 mRegistered = FALSE;
110 pMachineConfigFile = NULL;
111 /* Contains hints on what has changed when the user is using the VM (config
112 * changes, running the VM, ...). This is used to decide if a config needs
113 * to be written to disk. */
114 flModifications = 0;
115 /* VM modification usually also trigger setting the current state to
116 * "Modified". Although this is not always the case. An e.g. is the VM
117 * initialization phase or when snapshot related data is changed. The
118 * actually behavior is controlled by the following flag. */
119 m_fAllowStateModification = false;
120 mAccessible = FALSE;
121 /* mUuid is initialized in Machine::init() */
122
123 mMachineState = MachineState_PoweredOff;
124 RTTimeNow(&mLastStateChange);
125
126 mMachineStateDeps = 0;
127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
128 mMachineStateChangePending = 0;
129
130 mCurrentStateModified = TRUE;
131 mGuestPropertiesModified = FALSE;
132
133 mSession.mPID = NIL_RTPROCESS;
134 mSession.mLockType = LockType_Null;
135 mSession.mState = SessionState_Unlocked;
136}
137
138Machine::Data::~Data()
139{
140 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
141 {
142 RTSemEventMultiDestroy(mMachineStateDepsSem);
143 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
144 }
145 if (pMachineConfigFile)
146 {
147 delete pMachineConfigFile;
148 pMachineConfigFile = NULL;
149 }
150}
151
152/////////////////////////////////////////////////////////////////////////////
153// Machine::HWData structure
154/////////////////////////////////////////////////////////////////////////////
155
156Machine::HWData::HWData()
157{
158 /* default values for a newly created machine */
159 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
160 mMemorySize = 128;
161 mCPUCount = 1;
162 mCPUHotPlugEnabled = false;
163 mMemoryBalloonSize = 0;
164 mPageFusionEnabled = false;
165 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
166 mVRAMSize = 8;
167 mAccelerate3DEnabled = false;
168 mAccelerate2DVideoEnabled = false;
169 mMonitorCount = 1;
170 mVideoCaptureWidth = 1024;
171 mVideoCaptureHeight = 768;
172 mVideoCaptureRate = 512;
173 mVideoCaptureFPS = 25;
174 mVideoCaptureMaxTime = 0;
175 mVideoCaptureMaxFileSize = 0;
176 mVideoCaptureEnabled = false;
177 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
178 maVideoCaptureScreens[i] = true;
179
180 mHWVirtExEnabled = true;
181 mHWVirtExNestedPagingEnabled = true;
182#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
183 mHWVirtExLargePagesEnabled = true;
184#else
185 /* Not supported on 32 bits hosts. */
186 mHWVirtExLargePagesEnabled = false;
187#endif
188 mHWVirtExVPIDEnabled = true;
189 mHWVirtExUXEnabled = true;
190 mHWVirtExForceEnabled = false;
191#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
192 mPAEEnabled = true;
193#else
194 mPAEEnabled = false;
195#endif
196 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
197 mTripleFaultReset = false;
198 mAPIC = true;
199 mX2APIC = false;
200 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mDnDMode = DnDMode_Disabled;
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mParavirtProvider = ParavirtProvider_Default;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227}
228
229Machine::HWData::~HWData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine class
235/////////////////////////////////////////////////////////////////////////////
236
237// constructor / destructor
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::Machine() :
241#ifdef VBOX_WITH_RESOURCE_USAGE_API
242 mCollectorGuest(NULL),
243#endif
244 mPeer(NULL),
245 mParent(NULL),
246 mSerialPorts(),
247 mParallelPorts(),
248 uRegistryNeedsSaving(0)
249{}
250
251Machine::~Machine()
252{}
253
254HRESULT Machine::FinalConstruct()
255{
256 LogFlowThisFunc(("\n"));
257 return BaseFinalConstruct();
258}
259
260void Machine::FinalRelease()
261{
262 LogFlowThisFunc(("\n"));
263 uninit();
264 BaseFinalRelease();
265}
266
267/**
268 * Initializes a new machine instance; this init() variant creates a new, empty machine.
269 * This gets called from VirtualBox::CreateMachine().
270 *
271 * @param aParent Associated parent object
272 * @param strConfigFile Local file system path to the VM settings file (can
273 * be relative to the VirtualBox config directory).
274 * @param strName name for the machine
275 * @param llGroups list of groups for the machine
276 * @param aOsType OS Type of this machine or NULL.
277 * @param aId UUID for the new machine.
278 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
279 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
280 * scheme (includes the UUID).
281 *
282 * @return Success indicator. if not S_OK, the machine object is invalid
283 */
284HRESULT Machine::init(VirtualBox *aParent,
285 const Utf8Str &strConfigFile,
286 const Utf8Str &strName,
287 const StringsList &llGroups,
288 GuestOSType *aOsType,
289 const Guid &aId,
290 bool fForceOverwrite,
291 bool fDirectoryIncludesUUID)
292{
293 LogFlowThisFuncEnter();
294 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
295
296 /* Enclose the state transition NotReady->InInit->Ready */
297 AutoInitSpan autoInitSpan(this);
298 AssertReturn(autoInitSpan.isOk(), E_FAIL);
299
300 HRESULT rc = initImpl(aParent, strConfigFile);
301 if (FAILED(rc)) return rc;
302
303 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
304 if (FAILED(rc)) return rc;
305
306 if (SUCCEEDED(rc))
307 {
308 // create an empty machine config
309 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
310
311 rc = initDataAndChildObjects();
312 }
313
314 if (SUCCEEDED(rc))
315 {
316 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
317 mData->mAccessible = TRUE;
318
319 unconst(mData->mUuid) = aId;
320
321 mUserData->s.strName = strName;
322
323 mUserData->s.llGroups = llGroups;
324
325 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = i_isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->i_id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->i_applyDefaults(aOsType);
342
343 /* Let the OS type select 64-bit ness. */
344 mHWData->mLongMode = aOsType->i_is64Bit()
345 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
346
347 /* Let the OS type enable the X2APIC */
348 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
349 }
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Apply parallel port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
361 mParallelPorts[slot]->i_applyDefaults();
362
363 /* At this point the changing of the current state modification
364 * flag is allowed. */
365 i_allowStateModification();
366
367 /* commit all changes made during the initialization */
368 i_commit();
369 }
370
371 /* Confirm a successful initialization when it's the case */
372 if (SUCCEEDED(rc))
373 {
374 if (mData->mAccessible)
375 autoInitSpan.setSucceeded();
376 else
377 autoInitSpan.setLimited();
378 }
379
380 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
381 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
382 mData->mRegistered,
383 mData->mAccessible,
384 rc));
385
386 LogFlowThisFuncLeave();
387
388 return rc;
389}
390
391/**
392 * Initializes a new instance with data from machine XML (formerly Init_Registered).
393 * Gets called in two modes:
394 *
395 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
396 * UUID is specified and we mark the machine as "registered";
397 *
398 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
399 * and the machine remains unregistered until RegisterMachine() is called.
400 *
401 * @param aParent Associated parent object
402 * @param strConfigFile Local file system path to the VM settings file (can
403 * be relative to the VirtualBox config directory).
404 * @param aId UUID of the machine or NULL (see above).
405 *
406 * @return Success indicator. if not S_OK, the machine object is invalid
407 */
408HRESULT Machine::initFromSettings(VirtualBox *aParent,
409 const Utf8Str &strConfigFile,
410 const Guid *aId)
411{
412 LogFlowThisFuncEnter();
413 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
414
415 /* Enclose the state transition NotReady->InInit->Ready */
416 AutoInitSpan autoInitSpan(this);
417 AssertReturn(autoInitSpan.isOk(), E_FAIL);
418
419 HRESULT rc = initImpl(aParent, strConfigFile);
420 if (FAILED(rc)) return rc;
421
422 if (aId)
423 {
424 // loading a registered VM:
425 unconst(mData->mUuid) = *aId;
426 mData->mRegistered = TRUE;
427 // now load the settings from XML:
428 rc = i_registeredInit();
429 // this calls initDataAndChildObjects() and loadSettings()
430 }
431 else
432 {
433 // opening an unregistered VM (VirtualBox::OpenMachine()):
434 rc = initDataAndChildObjects();
435
436 if (SUCCEEDED(rc))
437 {
438 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
439 mData->mAccessible = TRUE;
440
441 try
442 {
443 // load and parse machine XML; this will throw on XML or logic errors
444 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
445
446 // reject VM UUID duplicates, they can happen if someone
447 // tries to register an already known VM config again
448 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
449 true /* fPermitInaccessible */,
450 false /* aDoSetError */,
451 NULL) != VBOX_E_OBJECT_NOT_FOUND)
452 {
453 throw setError(E_FAIL,
454 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
455 mData->m_strConfigFile.c_str());
456 }
457
458 // use UUID from machine config
459 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
460
461 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
462 NULL /* puuidRegistry */);
463 if (FAILED(rc)) throw rc;
464
465 /* At this point the changing of the current state modification
466 * flag is allowed. */
467 i_allowStateModification();
468
469 i_commit();
470 }
471 catch (HRESULT err)
472 {
473 /* we assume that error info is set by the thrower */
474 rc = err;
475 }
476 catch (...)
477 {
478 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
479 }
480 }
481 }
482
483 /* Confirm a successful initialization when it's the case */
484 if (SUCCEEDED(rc))
485 {
486 if (mData->mAccessible)
487 autoInitSpan.setSucceeded();
488 else
489 {
490 autoInitSpan.setLimited();
491
492 // uninit media from this machine's media registry, or else
493 // reloading the settings will fail
494 mParent->i_unregisterMachineMedia(i_getId());
495 }
496 }
497
498 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
499 "rc=%08X\n",
500 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
501 mData->mRegistered, mData->mAccessible, rc));
502
503 LogFlowThisFuncLeave();
504
505 return rc;
506}
507
508/**
509 * Initializes a new instance from a machine config that is already in memory
510 * (import OVF case). Since we are importing, the UUID in the machine
511 * config is ignored and we always generate a fresh one.
512 *
513 * @param aParent Associated parent object.
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent The VirtualBox object.
607 * @param strConfigFile Settings file.
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it has a
834 * VirtualBox caller for the duration of the
835 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from i_registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator
1051 it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end();
1053 ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComObjPtr<GuestOSType> pGuestOSType;
1091 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1092 pGuestOSType);
1093 if (FAILED(rc)) return rc;
1094
1095 /* when setting, always use the "etalon" value for consistency -- lookup
1096 * by ID is case-insensitive and the input value may have different case */
1097 Utf8Str osTypeId = pGuestOSType->i_id();
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 aParavirtDebug = mHWData->mParavirtDebug;
1226 return S_OK;
1227}
1228
1229HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1230{
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = i_checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 /** @todo Parse/validate options? */
1237 if (aParavirtDebug != mHWData->mParavirtDebug)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtDebug = aParavirtDebug;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252
1253 return S_OK;
1254}
1255
1256HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1257{
1258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 HRESULT rc = i_checkStateDependency(MutableStateDep);
1261 if (FAILED(rc)) return rc;
1262
1263 if (aParavirtProvider != mHWData->mParavirtProvider)
1264 {
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mParavirtProvider = aParavirtProvider;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 *aParavirtProvider = mHWData->mParavirtProvider;
1278 switch (mHWData->mParavirtProvider)
1279 {
1280 case ParavirtProvider_None:
1281 case ParavirtProvider_HyperV:
1282 case ParavirtProvider_KVM:
1283 case ParavirtProvider_Minimal:
1284 break;
1285
1286 /* Resolve dynamic provider types to the effective types. */
1287 default:
1288 {
1289 ComObjPtr<GuestOSType> pGuestOSType;
1290 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1291 pGuestOSType);
1292 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1293
1294 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1295 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1296
1297 switch (mHWData->mParavirtProvider)
1298 {
1299 case ParavirtProvider_Legacy:
1300 {
1301 if (fOsXGuest)
1302 *aParavirtProvider = ParavirtProvider_Minimal;
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 case ParavirtProvider_Default:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else if ( mUserData->s.strOsType == "Windows10"
1313 || mUserData->s.strOsType == "Windows10_64"
1314 || mUserData->s.strOsType == "Windows81"
1315 || mUserData->s.strOsType == "Windows81_64"
1316 || mUserData->s.strOsType == "Windows8"
1317 || mUserData->s.strOsType == "Windows8_64"
1318 || mUserData->s.strOsType == "Windows7"
1319 || mUserData->s.strOsType == "Windows7_64"
1320 || mUserData->s.strOsType == "WindowsVista"
1321 || mUserData->s.strOsType == "WindowsVista_64"
1322 || mUserData->s.strOsType == "Windows2012"
1323 || mUserData->s.strOsType == "Windows2012_64"
1324 || mUserData->s.strOsType == "Windows2008"
1325 || mUserData->s.strOsType == "Windows2008_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_HyperV;
1328 }
1329 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1330 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1331 || mUserData->s.strOsType == "Linux"
1332 || mUserData->s.strOsType == "Linux_64"
1333 || mUserData->s.strOsType == "ArchLinux"
1334 || mUserData->s.strOsType == "ArchLinux_64"
1335 || mUserData->s.strOsType == "Debian"
1336 || mUserData->s.strOsType == "Debian_64"
1337 || mUserData->s.strOsType == "Fedora"
1338 || mUserData->s.strOsType == "Fedora_64"
1339 || mUserData->s.strOsType == "Gentoo"
1340 || mUserData->s.strOsType == "Gentoo_64"
1341 || mUserData->s.strOsType == "Mandriva"
1342 || mUserData->s.strOsType == "Mandriva_64"
1343 || mUserData->s.strOsType == "OpenSUSE"
1344 || mUserData->s.strOsType == "OpenSUSE_64"
1345 || mUserData->s.strOsType == "Oracle"
1346 || mUserData->s.strOsType == "Oracle_64"
1347 || mUserData->s.strOsType == "RedHat"
1348 || mUserData->s.strOsType == "RedHat_64"
1349 || mUserData->s.strOsType == "Turbolinux"
1350 || mUserData->s.strOsType == "Turbolinux_64"
1351 || mUserData->s.strOsType == "Ubuntu"
1352 || mUserData->s.strOsType == "Ubuntu_64"
1353 || mUserData->s.strOsType == "Xandros"
1354 || mUserData->s.strOsType == "Xandros_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_KVM;
1357 }
1358 else
1359 *aParavirtProvider = ParavirtProvider_None;
1360 break;
1361 }
1362
1363 default: AssertFailedBreak(); /* Shut up MSC. */
1364 }
1365 break;
1366 }
1367 }
1368
1369 Assert( *aParavirtProvider == ParavirtProvider_None
1370 || *aParavirtProvider == ParavirtProvider_Minimal
1371 || *aParavirtProvider == ParavirtProvider_HyperV
1372 || *aParavirtProvider == ParavirtProvider_KVM);
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aHardwareVersion = mHWData->mHWVersion;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1386{
1387 /* check known version */
1388 Utf8Str hwVersion = aHardwareVersion;
1389 if ( hwVersion.compare("1") != 0
1390 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1391 return setError(E_INVALIDARG,
1392 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mHWVersion = aHardwareVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 if (!mHWData->mHardwareUUID.isZero())
1411 aHardwareUUID = mHWData->mHardwareUUID;
1412 else
1413 aHardwareUUID = mData->mUuid;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1419{
1420 if (!aHardwareUUID.isValid())
1421 return E_INVALIDARG;
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 if (aHardwareUUID == mData->mUuid)
1431 mHWData->mHardwareUUID.clear();
1432 else
1433 mHWData->mHardwareUUID = aHardwareUUID;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aMemorySize = mHWData->mMemorySize;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setMemorySize(ULONG aMemorySize)
1448{
1449 /* check RAM limits */
1450 if ( aMemorySize < MM_RAM_MIN_IN_MB
1451 || aMemorySize > MM_RAM_MAX_IN_MB
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1455 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 HRESULT rc = i_checkStateDependency(MutableStateDep);
1460 if (FAILED(rc)) return rc;
1461
1462 i_setModified(IsModified_MachineData);
1463 mHWData.backup();
1464 mHWData->mMemorySize = aMemorySize;
1465
1466 return S_OK;
1467}
1468
1469HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 *aCPUCount = mHWData->mCPUCount;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setCPUCount(ULONG aCPUCount)
1479{
1480 /* check CPU limits */
1481 if ( aCPUCount < SchemaDefs::MinCPUCount
1482 || aCPUCount > SchemaDefs::MaxCPUCount
1483 )
1484 return setError(E_INVALIDARG,
1485 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1486 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1491 if (mHWData->mCPUHotPlugEnabled)
1492 {
1493 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1494 {
1495 if (mHWData->mCPUAttached[idx])
1496 return setError(E_INVALIDARG,
1497 tr("There is still a CPU attached to socket %lu."
1498 "Detach the CPU before removing the socket"),
1499 aCPUCount, idx+1);
1500 }
1501 }
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mCPUCount = aCPUCount;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1523{
1524 HRESULT rc = S_OK;
1525
1526 /* check throttle limits */
1527 if ( aCPUExecutionCap < 1
1528 || aCPUExecutionCap > 100
1529 )
1530 return setError(E_INVALIDARG,
1531 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1532 aCPUExecutionCap, 1, 100);
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 alock.release();
1537 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1538 alock.acquire();
1539 if (FAILED(rc)) return rc;
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1544
1545 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1546 if (Global::IsOnline(mData->mMachineState))
1547 i_saveSettings(NULL);
1548
1549 return S_OK;
1550}
1551
1552HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1553{
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1562{
1563 HRESULT rc = S_OK;
1564
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1571 {
1572 if (aCPUHotPlugEnabled)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576
1577 /* Add the amount of CPUs currently attached */
1578 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1579 mHWData->mCPUAttached[i] = true;
1580 }
1581 else
1582 {
1583 /*
1584 * We can disable hotplug only if the amount of maximum CPUs is equal
1585 * to the amount of attached CPUs
1586 */
1587 unsigned cCpusAttached = 0;
1588 unsigned iHighestId = 0;
1589
1590 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1591 {
1592 if (mHWData->mCPUAttached[i])
1593 {
1594 cCpusAttached++;
1595 iHighestId = i;
1596 }
1597 }
1598
1599 if ( (cCpusAttached != mHWData->mCPUCount)
1600 || (iHighestId >= mHWData->mCPUCount))
1601 return setError(E_INVALIDARG,
1602 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606 }
1607 }
1608
1609 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1610
1611 return rc;
1612}
1613
1614HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1615{
1616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1619
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1624{
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1628 if (SUCCEEDED(hrc))
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1633 }
1634 return hrc;
1635}
1636
1637HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1638{
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640 aCPUProfile = mHWData->mCpuProfile;
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 /* Empty equals 'host'. */
1653 if (aCPUProfile.isNotEmpty())
1654 mHWData->mCpuProfile = aCPUProfile;
1655 else
1656 mHWData->mCpuProfile = "host";
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1662{
1663#ifdef VBOX_WITH_USB_CARDREADER
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEmulatedUSBCardReaderEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1681 if (FAILED(rc)) return rc;
1682
1683 i_setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1686
1687 return S_OK;
1688#else
1689 NOREF(aEmulatedUSBCardReaderEnabled);
1690 return E_NOTIMPL;
1691#endif
1692}
1693
1694HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aHPETEnabled = mHWData->mHPETEnabled;
1699
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1704{
1705 HRESULT rc = S_OK;
1706
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 rc = i_checkStateDependency(MutableStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714
1715 mHWData->mHPETEnabled = aHPETEnabled;
1716
1717 return rc;
1718}
1719
1720HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1729{
1730 HRESULT rc = S_OK;
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1737
1738 alock.release();
1739 rc = i_onVideoCaptureChange();
1740 alock.acquire();
1741 if (FAILED(rc))
1742 {
1743 /*
1744 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1745 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1746 * determine if it should start or stop capturing. Therefore we need to manually
1747 * undo change.
1748 */
1749 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1750 return rc;
1751 }
1752
1753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1754 if (Global::IsOnline(mData->mMachineState))
1755 i_saveSettings(NULL);
1756
1757 return rc;
1758}
1759
1760HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1764 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1765 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = i_onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 i_setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 i_saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1805 else
1806 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1811{
1812 Utf8Str strFile(aVideoCaptureFile);
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 i_getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1863 return S_OK;
1864}
1865
1866HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1867{
1868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 if ( Global::IsOnline(mData->mMachineState)
1871 && mHWData->mVideoCaptureEnabled)
1872 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1885 return S_OK;
1886}
1887
1888HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1889{
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 i_setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1907 return S_OK;
1908}
1909
1910HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1911{
1912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 if ( Global::IsOnline(mData->mMachineState)
1915 && mHWData->mVideoCaptureEnabled)
1916 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1917
1918 i_setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1921
1922 return S_OK;
1923}
1924
1925HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1933{
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 if ( Global::IsOnline(mData->mMachineState)
1937 && mHWData->mVideoCaptureEnabled)
1938 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 if ( Global::IsOnline(mData->mMachineState)
1959 && mHWData->mVideoCaptureEnabled)
1960 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1974 return S_OK;
1975}
1976
1977HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1978{
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 if ( Global::IsOnline(mData->mMachineState)
1982 && mHWData->mVideoCaptureEnabled)
1983 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1984
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2002{
2003 switch (aGraphicsControllerType)
2004 {
2005 case GraphicsControllerType_Null:
2006 case GraphicsControllerType_VBoxVGA:
2007#ifdef VBOX_WITH_VMSVGA
2008 case GraphicsControllerType_VMSVGA:
2009#endif
2010 break;
2011 default:
2012 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2013 }
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = i_checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aVRAMSize = mHWData->mVRAMSize;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2037{
2038 /* check VRAM limits */
2039 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2040 return setError(E_INVALIDARG,
2041 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = i_checkStateDependency(MutableStateDep);
2047 if (FAILED(rc)) return rc;
2048
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mVRAMSize = aVRAMSize;
2052
2053 return S_OK;
2054}
2055
2056/** @todo this method should not be public */
2057HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2062
2063 return S_OK;
2064}
2065
2066/**
2067 * Set the memory balloon size.
2068 *
2069 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2070 * we have to make sure that we never call IGuest from here.
2071 */
2072HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2073{
2074 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2075#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2076 /* check limits */
2077 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2078 return setError(E_INVALIDARG,
2079 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2080 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2087
2088 return S_OK;
2089#else
2090 NOREF(aMemoryBalloonSize);
2091 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2096{
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2104{
2105#ifdef VBOX_WITH_PAGE_SHARING
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 i_setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aPageFusionEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2129{
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = i_checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 /** @todo check validity! */
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2140
2141 return S_OK;
2142}
2143
2144
2145HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 /** @todo check validity! */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2170{
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *aMonitorCount = mHWData->mMonitorCount;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2179{
2180 /* make sure monitor count is a sensible number */
2181 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2182 return setError(E_INVALIDARG,
2183 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2184 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = i_checkStateDependency(MutableStateDep);
2189 if (FAILED(rc)) return rc;
2190
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 mHWData->mMonitorCount = aMonitorCount;
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2199{
2200 /* mBIOSSettings is constant during life time, no need to lock */
2201 aBIOSSettings = mBIOSSettings;
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2207{
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 switch (aProperty)
2211 {
2212 case CPUPropertyType_PAE:
2213 *aValue = mHWData->mPAEEnabled;
2214 break;
2215
2216 case CPUPropertyType_LongMode:
2217 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2218 *aValue = TRUE;
2219 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2220 *aValue = FALSE;
2221#if HC_ARCH_BITS == 64
2222 else
2223 *aValue = TRUE;
2224#else
2225 else
2226 {
2227 *aValue = FALSE;
2228
2229 ComObjPtr<GuestOSType> pGuestOSType;
2230 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2231 pGuestOSType);
2232 if (SUCCEEDED(hrc2))
2233 {
2234 if (pGuestOSType->i_is64Bit())
2235 {
2236 ComObjPtr<Host> pHost = mParent->i_host();
2237 alock.release();
2238
2239 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2240 if (FAILED(hrc2))
2241 *aValue = FALSE;
2242 }
2243 }
2244 }
2245#endif
2246 break;
2247
2248 case CPUPropertyType_TripleFaultReset:
2249 *aValue = mHWData->mTripleFaultReset;
2250 break;
2251
2252 case CPUPropertyType_APIC:
2253 *aValue = mHWData->mAPIC;
2254 break;
2255
2256 case CPUPropertyType_X2APIC:
2257 *aValue = mHWData->mX2APIC;
2258 break;
2259
2260 default:
2261 return E_INVALIDARG;
2262 }
2263 return S_OK;
2264}
2265
2266HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2267{
2268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2269
2270 HRESULT rc = i_checkStateDependency(MutableStateDep);
2271 if (FAILED(rc)) return rc;
2272
2273 switch (aProperty)
2274 {
2275 case CPUPropertyType_PAE:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mPAEEnabled = !!aValue;
2279 break;
2280
2281 case CPUPropertyType_LongMode:
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2285 break;
2286
2287 case CPUPropertyType_TripleFaultReset:
2288 i_setModified(IsModified_MachineData);
2289 mHWData.backup();
2290 mHWData->mTripleFaultReset = !!aValue;
2291 break;
2292
2293 case CPUPropertyType_APIC:
2294 if (mHWData->mX2APIC)
2295 aValue = TRUE;
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mAPIC = !!aValue;
2299 break;
2300
2301 case CPUPropertyType_X2APIC:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mX2APIC = !!aValue;
2305 if (aValue)
2306 mHWData->mAPIC = !!aValue;
2307 break;
2308
2309 default:
2310 return E_INVALIDARG;
2311 }
2312 return S_OK;
2313}
2314
2315HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2316{
2317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2318
2319 switch(aId)
2320 {
2321 case 0x0:
2322 case 0x1:
2323 case 0x2:
2324 case 0x3:
2325 case 0x4:
2326 case 0x5:
2327 case 0x6:
2328 case 0x7:
2329 case 0x8:
2330 case 0x9:
2331 case 0xA:
2332 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2333 return E_INVALIDARG;
2334
2335 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2336 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2337 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2338 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2339 break;
2340
2341 case 0x80000000:
2342 case 0x80000001:
2343 case 0x80000002:
2344 case 0x80000003:
2345 case 0x80000004:
2346 case 0x80000005:
2347 case 0x80000006:
2348 case 0x80000007:
2349 case 0x80000008:
2350 case 0x80000009:
2351 case 0x8000000A:
2352 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2353 return E_INVALIDARG;
2354
2355 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2356 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2357 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2358 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2359 break;
2360
2361 default:
2362 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2363 }
2364 return S_OK;
2365}
2366
2367
2368HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2369{
2370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2371
2372 HRESULT rc = i_checkStateDependency(MutableStateDep);
2373 if (FAILED(rc)) return rc;
2374
2375 switch(aId)
2376 {
2377 case 0x0:
2378 case 0x1:
2379 case 0x2:
2380 case 0x3:
2381 case 0x4:
2382 case 0x5:
2383 case 0x6:
2384 case 0x7:
2385 case 0x8:
2386 case 0x9:
2387 case 0xA:
2388 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2389 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2390 i_setModified(IsModified_MachineData);
2391 mHWData.backup();
2392 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2393 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2394 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2395 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2396 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2397 break;
2398
2399 case 0x80000000:
2400 case 0x80000001:
2401 case 0x80000002:
2402 case 0x80000003:
2403 case 0x80000004:
2404 case 0x80000005:
2405 case 0x80000006:
2406 case 0x80000007:
2407 case 0x80000008:
2408 case 0x80000009:
2409 case 0x8000000A:
2410 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2411 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2412 i_setModified(IsModified_MachineData);
2413 mHWData.backup();
2414 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2415 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2416 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2417 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2418 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2419 break;
2420
2421 default:
2422 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2423 }
2424 return S_OK;
2425}
2426
2427HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2428{
2429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2430
2431 HRESULT rc = i_checkStateDependency(MutableStateDep);
2432 if (FAILED(rc)) return rc;
2433
2434 switch(aId)
2435 {
2436 case 0x0:
2437 case 0x1:
2438 case 0x2:
2439 case 0x3:
2440 case 0x4:
2441 case 0x5:
2442 case 0x6:
2443 case 0x7:
2444 case 0x8:
2445 case 0x9:
2446 case 0xA:
2447 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2448 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2449 i_setModified(IsModified_MachineData);
2450 mHWData.backup();
2451 /* Invalidate leaf. */
2452 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2453 break;
2454
2455 case 0x80000000:
2456 case 0x80000001:
2457 case 0x80000002:
2458 case 0x80000003:
2459 case 0x80000004:
2460 case 0x80000005:
2461 case 0x80000006:
2462 case 0x80000007:
2463 case 0x80000008:
2464 case 0x80000009:
2465 case 0x8000000A:
2466 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2467 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2468 i_setModified(IsModified_MachineData);
2469 mHWData.backup();
2470 /* Invalidate leaf. */
2471 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2472 break;
2473
2474 default:
2475 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2476 }
2477 return S_OK;
2478}
2479
2480HRESULT Machine::removeAllCPUIDLeaves()
2481{
2482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 HRESULT rc = i_checkStateDependency(MutableStateDep);
2485 if (FAILED(rc)) return rc;
2486
2487 i_setModified(IsModified_MachineData);
2488 mHWData.backup();
2489
2490 /* Invalidate all standard leafs. */
2491 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2492 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2493
2494 /* Invalidate all extended leafs. */
2495 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2496 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2497
2498 return S_OK;
2499}
2500HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2501{
2502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2503
2504 switch(aProperty)
2505 {
2506 case HWVirtExPropertyType_Enabled:
2507 *aValue = mHWData->mHWVirtExEnabled;
2508 break;
2509
2510 case HWVirtExPropertyType_VPID:
2511 *aValue = mHWData->mHWVirtExVPIDEnabled;
2512 break;
2513
2514 case HWVirtExPropertyType_NestedPaging:
2515 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2516 break;
2517
2518 case HWVirtExPropertyType_UnrestrictedExecution:
2519 *aValue = mHWData->mHWVirtExUXEnabled;
2520 break;
2521
2522 case HWVirtExPropertyType_LargePages:
2523 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2524#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2525 *aValue = FALSE;
2526#endif
2527 break;
2528
2529 case HWVirtExPropertyType_Force:
2530 *aValue = mHWData->mHWVirtExForceEnabled;
2531 break;
2532
2533 default:
2534 return E_INVALIDARG;
2535 }
2536 return S_OK;
2537}
2538
2539HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2540{
2541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 HRESULT rc = i_checkStateDependency(MutableStateDep);
2544 if (FAILED(rc)) return rc;
2545
2546 switch(aProperty)
2547 {
2548 case HWVirtExPropertyType_Enabled:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_VPID:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_NestedPaging:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_UnrestrictedExecution:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExUXEnabled = !!aValue;
2570 break;
2571
2572 case HWVirtExPropertyType_LargePages:
2573 i_setModified(IsModified_MachineData);
2574 mHWData.backup();
2575 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2576 break;
2577
2578 case HWVirtExPropertyType_Force:
2579 i_setModified(IsModified_MachineData);
2580 mHWData.backup();
2581 mHWData->mHWVirtExForceEnabled = !!aValue;
2582 break;
2583
2584 default:
2585 return E_INVALIDARG;
2586 }
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2601{
2602 /** @todo (r=dmik):
2603 * 1. Allow to change the name of the snapshot folder containing snapshots
2604 * 2. Rename the folder on disk instead of just changing the property
2605 * value (to be smart and not to leave garbage). Note that it cannot be
2606 * done here because the change may be rolled back. Thus, the right
2607 * place is #saveSettings().
2608 */
2609
2610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2611
2612 HRESULT rc = i_checkStateDependency(MutableStateDep);
2613 if (FAILED(rc)) return rc;
2614
2615 if (!mData->mCurrentSnapshot.isNull())
2616 return setError(E_FAIL,
2617 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2618
2619 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2620
2621 if (strSnapshotFolder.isEmpty())
2622 strSnapshotFolder = "Snapshots";
2623 int vrc = i_calculateFullPath(strSnapshotFolder,
2624 strSnapshotFolder);
2625 if (RT_FAILURE(vrc))
2626 return setError(E_FAIL,
2627 tr("Invalid snapshot folder '%s' (%Rrc)"),
2628 strSnapshotFolder.c_str(), vrc);
2629
2630 i_setModified(IsModified_MachineData);
2631 mUserData.backup();
2632
2633 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 aMediumAttachments.resize(mMediumAttachments->size());
2643 size_t i = 0;
2644 for (MediumAttachmentList::const_iterator
2645 it = mMediumAttachments->begin();
2646 it != mMediumAttachments->end();
2647 ++it, ++i)
2648 aMediumAttachments[i] = *it;
2649
2650 return S_OK;
2651}
2652
2653HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2654{
2655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2656
2657 Assert(!!mVRDEServer);
2658
2659 aVRDEServer = mVRDEServer;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 aAudioAdapter = mAudioAdapter;
2669
2670 return S_OK;
2671}
2672
2673HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2674{
2675#ifdef VBOX_WITH_VUSB
2676 clearError();
2677 MultiResult rc(S_OK);
2678
2679# ifdef VBOX_WITH_USB
2680 rc = mParent->i_host()->i_checkUSBProxyService();
2681 if (FAILED(rc)) return rc;
2682# endif
2683
2684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2685
2686 aUSBControllers.resize(mUSBControllers->size());
2687 size_t i = 0;
2688 for (USBControllerList::const_iterator
2689 it = mUSBControllers->begin();
2690 it != mUSBControllers->end();
2691 ++it, ++i)
2692 aUSBControllers[i] = *it;
2693
2694 return S_OK;
2695#else
2696 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2697 * extended error info to indicate that USB is simply not available
2698 * (w/o treating it as a failure), for example, as in OSE */
2699 NOREF(aUSBControllers);
2700 ReturnComNotImplemented();
2701#endif /* VBOX_WITH_VUSB */
2702}
2703
2704HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2705{
2706#ifdef VBOX_WITH_VUSB
2707 clearError();
2708 MultiResult rc(S_OK);
2709
2710# ifdef VBOX_WITH_USB
2711 rc = mParent->i_host()->i_checkUSBProxyService();
2712 if (FAILED(rc)) return rc;
2713# endif
2714
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 aUSBDeviceFilters = mUSBDeviceFilters;
2718 return rc;
2719#else
2720 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2721 * extended error info to indicate that USB is simply not available
2722 * (w/o treating it as a failure), for example, as in OSE */
2723 NOREF(aUSBDeviceFilters);
2724 ReturnComNotImplemented();
2725#endif /* VBOX_WITH_VUSB */
2726}
2727
2728HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2729{
2730 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2731
2732 aSettingsFilePath = mData->m_strConfigFileFull;
2733
2734 return S_OK;
2735}
2736
2737HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2738{
2739 RT_NOREF(aSettingsFilePath);
2740 ReturnComNotImplemented();
2741}
2742
2743HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2744{
2745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2746
2747 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2748 if (FAILED(rc)) return rc;
2749
2750 if (!mData->pMachineConfigFile->fileExists())
2751 // this is a new machine, and no config file exists yet:
2752 *aSettingsModified = TRUE;
2753 else
2754 *aSettingsModified = (mData->flModifications != 0);
2755
2756 return S_OK;
2757}
2758
2759HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2760{
2761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2762
2763 *aSessionState = mData->mSession.mState;
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aSessionName = mData->mSession.mName;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 *aSessionPID = mData->mSession.mPID;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::getState(MachineState_T *aState)
2787{
2788 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 *aState = mData->mMachineState;
2791 Assert(mData->mMachineState != MachineState_Null);
2792
2793 return S_OK;
2794}
2795
2796HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2797{
2798 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2799
2800 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2801
2802 return S_OK;
2803}
2804
2805HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2806{
2807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2808
2809 aStateFilePath = mSSData->strStateFilePath;
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 i_getLogFolder(aLogFolder);
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2824{
2825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2826
2827 aCurrentSnapshot = mData->mCurrentSnapshot;
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835
2836 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2837 ? 0
2838 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2844{
2845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 /* Note: for machines with no snapshots, we always return FALSE
2848 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2849 * reasons :) */
2850
2851 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2852 ? FALSE
2853 : mData->mCurrentStateModified;
2854
2855 return S_OK;
2856}
2857
2858HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2859{
2860 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2861
2862 aSharedFolders.resize(mHWData->mSharedFolders.size());
2863 size_t i = 0;
2864 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2865 it = mHWData->mSharedFolders.begin();
2866 it != mHWData->mSharedFolders.end();
2867 ++it, ++i)
2868 aSharedFolders[i] = *it;
2869
2870 return S_OK;
2871}
2872
2873HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2874{
2875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2876
2877 *aClipboardMode = mHWData->mClipboardMode;
2878
2879 return S_OK;
2880}
2881
2882HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2883{
2884 HRESULT rc = S_OK;
2885
2886 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2887
2888 alock.release();
2889 rc = i_onClipboardModeChange(aClipboardMode);
2890 alock.acquire();
2891 if (FAILED(rc)) return rc;
2892
2893 i_setModified(IsModified_MachineData);
2894 mHWData.backup();
2895 mHWData->mClipboardMode = aClipboardMode;
2896
2897 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2898 if (Global::IsOnline(mData->mMachineState))
2899 i_saveSettings(NULL);
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 *aDnDMode = mHWData->mDnDMode;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2914{
2915 HRESULT rc = S_OK;
2916
2917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2918
2919 alock.release();
2920 rc = i_onDnDModeChange(aDnDMode);
2921
2922 alock.acquire();
2923 if (FAILED(rc)) return rc;
2924
2925 i_setModified(IsModified_MachineData);
2926 mHWData.backup();
2927 mHWData->mDnDMode = aDnDMode;
2928
2929 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2930 if (Global::IsOnline(mData->mMachineState))
2931 i_saveSettings(NULL);
2932
2933 return S_OK;
2934}
2935
2936HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2937{
2938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 aStorageControllers.resize(mStorageControllers->size());
2941 size_t i = 0;
2942 for (StorageControllerList::const_iterator
2943 it = mStorageControllers->begin();
2944 it != mStorageControllers->end();
2945 ++it, ++i)
2946 aStorageControllers[i] = *it;
2947
2948 return S_OK;
2949}
2950
2951HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2952{
2953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2954
2955 *aEnabled = mUserData->s.fTeleporterEnabled;
2956
2957 return S_OK;
2958}
2959
2960HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2961{
2962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2963
2964 /* Only allow it to be set to true when PoweredOff or Aborted.
2965 (Clearing it is always permitted.) */
2966 if ( aTeleporterEnabled
2967 && mData->mRegistered
2968 && ( !i_isSessionMachine()
2969 || ( mData->mMachineState != MachineState_PoweredOff
2970 && mData->mMachineState != MachineState_Teleported
2971 && mData->mMachineState != MachineState_Aborted
2972 )
2973 )
2974 )
2975 return setError(VBOX_E_INVALID_VM_STATE,
2976 tr("The machine is not powered off (state is %s)"),
2977 Global::stringifyMachineState(mData->mMachineState));
2978
2979 i_setModified(IsModified_MachineData);
2980 mUserData.backup();
2981 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2982
2983 return S_OK;
2984}
2985
2986HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2987{
2988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2991
2992 return S_OK;
2993}
2994
2995HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2996{
2997 if (aTeleporterPort >= _64K)
2998 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2999
3000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3001
3002 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3003 if (FAILED(rc)) return rc;
3004
3005 i_setModified(IsModified_MachineData);
3006 mUserData.backup();
3007 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3008
3009 return S_OK;
3010}
3011
3012HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3013{
3014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3015
3016 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3017
3018 return S_OK;
3019}
3020
3021HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3022{
3023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3024
3025 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3026 if (FAILED(rc)) return rc;
3027
3028 i_setModified(IsModified_MachineData);
3029 mUserData.backup();
3030 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3031
3032 return S_OK;
3033}
3034
3035HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3036{
3037 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3038 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3039
3040 return S_OK;
3041}
3042
3043HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3044{
3045 /*
3046 * Hash the password first.
3047 */
3048 com::Utf8Str aT = aTeleporterPassword;
3049
3050 if (!aT.isEmpty())
3051 {
3052 if (VBoxIsPasswordHashed(&aT))
3053 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3054 VBoxHashPassword(&aT);
3055 }
3056
3057 /*
3058 * Do the update.
3059 */
3060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3061 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3062 if (SUCCEEDED(hrc))
3063 {
3064 i_setModified(IsModified_MachineData);
3065 mUserData.backup();
3066 mUserData->s.strTeleporterPassword = aT;
3067 }
3068
3069 return hrc;
3070}
3071
3072HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3073{
3074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3075
3076 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3077 return S_OK;
3078}
3079
3080HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3081{
3082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3083
3084 /** @todo deal with running state change. */
3085 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3086 if (FAILED(rc)) return rc;
3087
3088 i_setModified(IsModified_MachineData);
3089 mUserData.backup();
3090 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3091 return S_OK;
3092}
3093
3094HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3095{
3096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3097
3098 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3099 return S_OK;
3100}
3101
3102HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3103{
3104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3105
3106 /** @todo deal with running state change. */
3107 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3108 if (FAILED(rc)) return rc;
3109
3110 i_setModified(IsModified_MachineData);
3111 mUserData.backup();
3112 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3113 return S_OK;
3114}
3115
3116HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3117{
3118 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3119
3120 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3121 return S_OK;
3122}
3123
3124HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3125{
3126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3127
3128 /** @todo deal with running state change. */
3129 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3130 if (FAILED(rc)) return rc;
3131
3132 i_setModified(IsModified_MachineData);
3133 mUserData.backup();
3134 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3135 return S_OK;
3136}
3137
3138HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3139{
3140 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3141
3142 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3143
3144 return S_OK;
3145}
3146
3147HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3148{
3149 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3150
3151 /** @todo deal with running state change. */
3152 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3153 if (FAILED(rc)) return rc;
3154
3155 i_setModified(IsModified_MachineData);
3156 mUserData.backup();
3157 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3158
3159 return S_OK;
3160}
3161
3162HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3163{
3164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3165
3166 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3167 return S_OK;
3168}
3169
3170HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3171{
3172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3173
3174 /** @todo deal with running state change. */
3175 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3176 if (FAILED(rc)) return rc;
3177
3178 i_setModified(IsModified_MachineData);
3179 mUserData.backup();
3180 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3181 return S_OK;
3182}
3183
3184HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3185{
3186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3187
3188 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3189
3190 return S_OK;
3191}
3192
3193HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3194{
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 /* Only allow it to be set to true when PoweredOff or Aborted.
3198 (Clearing it is always permitted.) */
3199 if ( aRTCUseUTC
3200 && mData->mRegistered
3201 && ( !i_isSessionMachine()
3202 || ( mData->mMachineState != MachineState_PoweredOff
3203 && mData->mMachineState != MachineState_Teleported
3204 && mData->mMachineState != MachineState_Aborted
3205 )
3206 )
3207 )
3208 return setError(VBOX_E_INVALID_VM_STATE,
3209 tr("The machine is not powered off (state is %s)"),
3210 Global::stringifyMachineState(mData->mMachineState));
3211
3212 i_setModified(IsModified_MachineData);
3213 mUserData.backup();
3214 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3215
3216 return S_OK;
3217}
3218
3219HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3220{
3221 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3222
3223 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3224
3225 return S_OK;
3226}
3227
3228HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3229{
3230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3231
3232 HRESULT rc = i_checkStateDependency(MutableStateDep);
3233 if (FAILED(rc)) return rc;
3234
3235 i_setModified(IsModified_MachineData);
3236 mHWData.backup();
3237 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3238
3239 return S_OK;
3240}
3241
3242HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3243{
3244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3245
3246 *aIOCacheSize = mHWData->mIOCacheSize;
3247
3248 return S_OK;
3249}
3250
3251HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3252{
3253 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3254
3255 HRESULT rc = i_checkStateDependency(MutableStateDep);
3256 if (FAILED(rc)) return rc;
3257
3258 i_setModified(IsModified_MachineData);
3259 mHWData.backup();
3260 mHWData->mIOCacheSize = aIOCacheSize;
3261
3262 return S_OK;
3263}
3264
3265
3266/**
3267 * @note Locks objects!
3268 */
3269HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3270 LockType_T aLockType)
3271{
3272 /* check the session state */
3273 SessionState_T state;
3274 HRESULT rc = aSession->COMGETTER(State)(&state);
3275 if (FAILED(rc)) return rc;
3276
3277 if (state != SessionState_Unlocked)
3278 return setError(VBOX_E_INVALID_OBJECT_STATE,
3279 tr("The given session is busy"));
3280
3281 // get the client's IInternalSessionControl interface
3282 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3283 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3284 E_INVALIDARG);
3285
3286 // session name (only used in some code paths)
3287 Utf8Str strSessionName;
3288
3289 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3290
3291 if (!mData->mRegistered)
3292 return setError(E_UNEXPECTED,
3293 tr("The machine '%s' is not registered"),
3294 mUserData->s.strName.c_str());
3295
3296 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3297
3298 SessionState_T oldState = mData->mSession.mState;
3299 /* Hack: in case the session is closing and there is a progress object
3300 * which allows waiting for the session to be closed, take the opportunity
3301 * and do a limited wait (max. 1 second). This helps a lot when the system
3302 * is busy and thus session closing can take a little while. */
3303 if ( mData->mSession.mState == SessionState_Unlocking
3304 && mData->mSession.mProgress)
3305 {
3306 alock.release();
3307 mData->mSession.mProgress->WaitForCompletion(1000);
3308 alock.acquire();
3309 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3310 }
3311
3312 // try again now
3313 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3314 // (i.e. session machine exists)
3315 && (aLockType == LockType_Shared) // caller wants a shared link to the
3316 // existing session that holds the write lock:
3317 )
3318 {
3319 // OK, share the session... we are now dealing with three processes:
3320 // 1) VBoxSVC (where this code runs);
3321 // 2) process C: the caller's client process (who wants a shared session);
3322 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3323
3324 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3325 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3326 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3327 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3328 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3329
3330 /*
3331 * Release the lock before calling the client process. It's safe here
3332 * since the only thing to do after we get the lock again is to add
3333 * the remote control to the list (which doesn't directly influence
3334 * anything).
3335 */
3336 alock.release();
3337
3338 // get the console of the session holding the write lock (this is a remote call)
3339 ComPtr<IConsole> pConsoleW;
3340 if (mData->mSession.mLockType == LockType_VM)
3341 {
3342 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3343 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3344 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3345 if (FAILED(rc))
3346 // the failure may occur w/o any error info (from RPC), so provide one
3347 return setError(VBOX_E_VM_ERROR,
3348 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3349 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3350 }
3351
3352 // share the session machine and W's console with the caller's session
3353 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3354 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3355 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3356
3357 if (FAILED(rc))
3358 // the failure may occur w/o any error info (from RPC), so provide one
3359 return setError(VBOX_E_VM_ERROR,
3360 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3361 alock.acquire();
3362
3363 // need to revalidate the state after acquiring the lock again
3364 if (mData->mSession.mState != SessionState_Locked)
3365 {
3366 pSessionControl->Uninitialize();
3367 return setError(VBOX_E_INVALID_SESSION_STATE,
3368 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3369 mUserData->s.strName.c_str());
3370 }
3371
3372 // add the caller's session to the list
3373 mData->mSession.mRemoteControls.push_back(pSessionControl);
3374 }
3375 else if ( mData->mSession.mState == SessionState_Locked
3376 || mData->mSession.mState == SessionState_Unlocking
3377 )
3378 {
3379 // sharing not permitted, or machine still unlocking:
3380 return setError(VBOX_E_INVALID_OBJECT_STATE,
3381 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3382 mUserData->s.strName.c_str());
3383 }
3384 else
3385 {
3386 // machine is not locked: then write-lock the machine (create the session machine)
3387
3388 // must not be busy
3389 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3390
3391 // get the caller's session PID
3392 RTPROCESS pid = NIL_RTPROCESS;
3393 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3394 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3395 Assert(pid != NIL_RTPROCESS);
3396
3397 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3398
3399 if (fLaunchingVMProcess)
3400 {
3401 if (mData->mSession.mPID == NIL_RTPROCESS)
3402 {
3403 // two or more clients racing for a lock, the one which set the
3404 // session state to Spawning will win, the others will get an
3405 // error as we can't decide here if waiting a little would help
3406 // (only for shared locks this would avoid an error)
3407 return setError(VBOX_E_INVALID_OBJECT_STATE,
3408 tr("The machine '%s' already has a lock request pending"),
3409 mUserData->s.strName.c_str());
3410 }
3411
3412 // this machine is awaiting for a spawning session to be opened:
3413 // then the calling process must be the one that got started by
3414 // LaunchVMProcess()
3415
3416 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3417 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3418
3419#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3420 /* Hardened windows builds spawns three processes when a VM is
3421 launched, the 3rd one is the one that will end up here. */
3422 RTPROCESS ppid;
3423 int rc = RTProcQueryParent(pid, &ppid);
3424 if (RT_SUCCESS(rc))
3425 rc = RTProcQueryParent(ppid, &ppid);
3426 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3427 || rc == VERR_ACCESS_DENIED)
3428 {
3429 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3430 mData->mSession.mPID = pid;
3431 }
3432#endif
3433
3434 if (mData->mSession.mPID != pid)
3435 return setError(E_ACCESSDENIED,
3436 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3437 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3438 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3439 }
3440
3441 // create the mutable SessionMachine from the current machine
3442 ComObjPtr<SessionMachine> sessionMachine;
3443 sessionMachine.createObject();
3444 rc = sessionMachine->init(this);
3445 AssertComRC(rc);
3446
3447 /* NOTE: doing return from this function after this point but
3448 * before the end is forbidden since it may call SessionMachine::uninit()
3449 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3450 * lock while still holding the Machine lock in alock so that a deadlock
3451 * is possible due to the wrong lock order. */
3452
3453 if (SUCCEEDED(rc))
3454 {
3455 /*
3456 * Set the session state to Spawning to protect against subsequent
3457 * attempts to open a session and to unregister the machine after
3458 * we release the lock.
3459 */
3460 SessionState_T origState = mData->mSession.mState;
3461 mData->mSession.mState = SessionState_Spawning;
3462
3463#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3464 /* Get the client token ID to be passed to the client process */
3465 Utf8Str strTokenId;
3466 sessionMachine->i_getTokenId(strTokenId);
3467 Assert(!strTokenId.isEmpty());
3468#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3469 /* Get the client token to be passed to the client process */
3470 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3471 /* The token is now "owned" by pToken, fix refcount */
3472 if (!pToken.isNull())
3473 pToken->Release();
3474#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3475
3476 /*
3477 * Release the lock before calling the client process -- it will call
3478 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3479 * because the state is Spawning, so that LaunchVMProcess() and
3480 * LockMachine() calls will fail. This method, called before we
3481 * acquire the lock again, will fail because of the wrong PID.
3482 *
3483 * Note that mData->mSession.mRemoteControls accessed outside
3484 * the lock may not be modified when state is Spawning, so it's safe.
3485 */
3486 alock.release();
3487
3488 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3489#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3490 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3491#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3492 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3493 /* Now the token is owned by the client process. */
3494 pToken.setNull();
3495#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3496 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3497
3498 /* The failure may occur w/o any error info (from RPC), so provide one */
3499 if (FAILED(rc))
3500 setError(VBOX_E_VM_ERROR,
3501 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3502
3503 // get session name, either to remember or to compare against
3504 // the already known session name.
3505 {
3506 Bstr bstrSessionName;
3507 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3508 if (SUCCEEDED(rc2))
3509 strSessionName = bstrSessionName;
3510 }
3511
3512 if ( SUCCEEDED(rc)
3513 && fLaunchingVMProcess
3514 )
3515 {
3516 /* complete the remote session initialization */
3517
3518 /* get the console from the direct session */
3519 ComPtr<IConsole> console;
3520 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3521 ComAssertComRC(rc);
3522
3523 if (SUCCEEDED(rc) && !console)
3524 {
3525 ComAssert(!!console);
3526 rc = E_FAIL;
3527 }
3528
3529 /* assign machine & console to the remote session */
3530 if (SUCCEEDED(rc))
3531 {
3532 /*
3533 * after LaunchVMProcess(), the first and the only
3534 * entry in remoteControls is that remote session
3535 */
3536 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3537 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3538 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3539
3540 /* The failure may occur w/o any error info (from RPC), so provide one */
3541 if (FAILED(rc))
3542 setError(VBOX_E_VM_ERROR,
3543 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3544 }
3545
3546 if (FAILED(rc))
3547 pSessionControl->Uninitialize();
3548 }
3549
3550 /* acquire the lock again */
3551 alock.acquire();
3552
3553 /* Restore the session state */
3554 mData->mSession.mState = origState;
3555 }
3556
3557 // finalize spawning anyway (this is why we don't return on errors above)
3558 if (fLaunchingVMProcess)
3559 {
3560 Assert(mData->mSession.mName == strSessionName);
3561 /* Note that the progress object is finalized later */
3562 /** @todo Consider checking mData->mSession.mProgress for cancellation
3563 * around here. */
3564
3565 /* We don't reset mSession.mPID here because it is necessary for
3566 * SessionMachine::uninit() to reap the child process later. */
3567
3568 if (FAILED(rc))
3569 {
3570 /* Close the remote session, remove the remote control from the list
3571 * and reset session state to Closed (@note keep the code in sync
3572 * with the relevant part in checkForSpawnFailure()). */
3573
3574 Assert(mData->mSession.mRemoteControls.size() == 1);
3575 if (mData->mSession.mRemoteControls.size() == 1)
3576 {
3577 ErrorInfoKeeper eik;
3578 mData->mSession.mRemoteControls.front()->Uninitialize();
3579 }
3580
3581 mData->mSession.mRemoteControls.clear();
3582 mData->mSession.mState = SessionState_Unlocked;
3583 }
3584 }
3585 else
3586 {
3587 /* memorize PID of the directly opened session */
3588 if (SUCCEEDED(rc))
3589 mData->mSession.mPID = pid;
3590 }
3591
3592 if (SUCCEEDED(rc))
3593 {
3594 mData->mSession.mLockType = aLockType;
3595 /* memorize the direct session control and cache IUnknown for it */
3596 mData->mSession.mDirectControl = pSessionControl;
3597 mData->mSession.mState = SessionState_Locked;
3598 if (!fLaunchingVMProcess)
3599 mData->mSession.mName = strSessionName;
3600 /* associate the SessionMachine with this Machine */
3601 mData->mSession.mMachine = sessionMachine;
3602
3603 /* request an IUnknown pointer early from the remote party for later
3604 * identity checks (it will be internally cached within mDirectControl
3605 * at least on XPCOM) */
3606 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3607 NOREF(unk);
3608 }
3609
3610 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3611 * would break the lock order */
3612 alock.release();
3613
3614 /* uninitialize the created session machine on failure */
3615 if (FAILED(rc))
3616 sessionMachine->uninit();
3617 }
3618
3619 if (SUCCEEDED(rc))
3620 {
3621 /*
3622 * tell the client watcher thread to update the set of
3623 * machines that have open sessions
3624 */
3625 mParent->i_updateClientWatcher();
3626
3627 if (oldState != SessionState_Locked)
3628 /* fire an event */
3629 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3630 }
3631
3632 return rc;
3633}
3634
3635/**
3636 * @note Locks objects!
3637 */
3638HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3639 const com::Utf8Str &aName,
3640 const com::Utf8Str &aEnvironment,
3641 ComPtr<IProgress> &aProgress)
3642{
3643 Utf8Str strFrontend(aName);
3644 /* "emergencystop" doesn't need the session, so skip the checks/interface
3645 * retrieval. This code doesn't quite fit in here, but introducing a
3646 * special API method would be even more effort, and would require explicit
3647 * support by every API client. It's better to hide the feature a bit. */
3648 if (strFrontend != "emergencystop")
3649 CheckComArgNotNull(aSession);
3650
3651 HRESULT rc = S_OK;
3652 if (strFrontend.isEmpty())
3653 {
3654 Bstr bstrFrontend;
3655 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3656 if (FAILED(rc))
3657 return rc;
3658 strFrontend = bstrFrontend;
3659 if (strFrontend.isEmpty())
3660 {
3661 ComPtr<ISystemProperties> systemProperties;
3662 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3663 if (FAILED(rc))
3664 return rc;
3665 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3666 if (FAILED(rc))
3667 return rc;
3668 strFrontend = bstrFrontend;
3669 }
3670 /* paranoia - emergencystop is not a valid default */
3671 if (strFrontend == "emergencystop")
3672 strFrontend = Utf8Str::Empty;
3673 }
3674 /* default frontend: Qt GUI */
3675 if (strFrontend.isEmpty())
3676 strFrontend = "GUI/Qt";
3677
3678 if (strFrontend != "emergencystop")
3679 {
3680 /* check the session state */
3681 SessionState_T state;
3682 rc = aSession->COMGETTER(State)(&state);
3683 if (FAILED(rc))
3684 return rc;
3685
3686 if (state != SessionState_Unlocked)
3687 return setError(VBOX_E_INVALID_OBJECT_STATE,
3688 tr("The given session is busy"));
3689
3690 /* get the IInternalSessionControl interface */
3691 ComPtr<IInternalSessionControl> control(aSession);
3692 ComAssertMsgRet(!control.isNull(),
3693 ("No IInternalSessionControl interface"),
3694 E_INVALIDARG);
3695
3696 /* get the teleporter enable state for the progress object init. */
3697 BOOL fTeleporterEnabled;
3698 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3699 if (FAILED(rc))
3700 return rc;
3701
3702 /* create a progress object */
3703 ComObjPtr<ProgressProxy> progress;
3704 progress.createObject();
3705 rc = progress->init(mParent,
3706 static_cast<IMachine*>(this),
3707 Bstr(tr("Starting VM")).raw(),
3708 TRUE /* aCancelable */,
3709 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3710 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3711 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3712 2 /* uFirstOperationWeight */,
3713 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3714
3715 if (SUCCEEDED(rc))
3716 {
3717 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3718 if (SUCCEEDED(rc))
3719 {
3720 aProgress = progress;
3721
3722 /* signal the client watcher thread */
3723 mParent->i_updateClientWatcher();
3724
3725 /* fire an event */
3726 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3727 }
3728 }
3729 }
3730 else
3731 {
3732 /* no progress object - either instant success or failure */
3733 aProgress = NULL;
3734
3735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3736
3737 if (mData->mSession.mState != SessionState_Locked)
3738 return setError(VBOX_E_INVALID_OBJECT_STATE,
3739 tr("The machine '%s' is not locked by a session"),
3740 mUserData->s.strName.c_str());
3741
3742 /* must have a VM process associated - do not kill normal API clients
3743 * with an open session */
3744 if (!Global::IsOnline(mData->mMachineState))
3745 return setError(VBOX_E_INVALID_OBJECT_STATE,
3746 tr("The machine '%s' does not have a VM process"),
3747 mUserData->s.strName.c_str());
3748
3749 /* forcibly terminate the VM process */
3750 if (mData->mSession.mPID != NIL_RTPROCESS)
3751 RTProcTerminate(mData->mSession.mPID);
3752
3753 /* signal the client watcher thread, as most likely the client has
3754 * been terminated */
3755 mParent->i_updateClientWatcher();
3756 }
3757
3758 return rc;
3759}
3760
3761HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3762{
3763 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3764 return setError(E_INVALIDARG,
3765 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3766 aPosition, SchemaDefs::MaxBootPosition);
3767
3768 if (aDevice == DeviceType_USB)
3769 return setError(E_NOTIMPL,
3770 tr("Booting from USB device is currently not supported"));
3771
3772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3773
3774 HRESULT rc = i_checkStateDependency(MutableStateDep);
3775 if (FAILED(rc)) return rc;
3776
3777 i_setModified(IsModified_MachineData);
3778 mHWData.backup();
3779 mHWData->mBootOrder[aPosition - 1] = aDevice;
3780
3781 return S_OK;
3782}
3783
3784HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3785{
3786 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3787 return setError(E_INVALIDARG,
3788 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3789 aPosition, SchemaDefs::MaxBootPosition);
3790
3791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3792
3793 *aDevice = mHWData->mBootOrder[aPosition - 1];
3794
3795 return S_OK;
3796}
3797
3798HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3799 LONG aControllerPort,
3800 LONG aDevice,
3801 DeviceType_T aType,
3802 const ComPtr<IMedium> &aMedium)
3803{
3804 IMedium *aM = aMedium;
3805 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3806 aName.c_str(), aControllerPort, aDevice, aType, aM));
3807
3808 // request the host lock first, since might be calling Host methods for getting host drives;
3809 // next, protect the media tree all the while we're in here, as well as our member variables
3810 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3811 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3812
3813 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3814 if (FAILED(rc)) return rc;
3815
3816 /// @todo NEWMEDIA implicit machine registration
3817 if (!mData->mRegistered)
3818 return setError(VBOX_E_INVALID_OBJECT_STATE,
3819 tr("Cannot attach storage devices to an unregistered machine"));
3820
3821 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3822
3823 /* Check for an existing controller. */
3824 ComObjPtr<StorageController> ctl;
3825 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3826 if (FAILED(rc)) return rc;
3827
3828 StorageControllerType_T ctrlType;
3829 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3830 if (FAILED(rc))
3831 return setError(E_FAIL,
3832 tr("Could not get type of controller '%s'"),
3833 aName.c_str());
3834
3835 bool fSilent = false;
3836 Utf8Str strReconfig;
3837
3838 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3839 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3840 if ( mData->mMachineState == MachineState_Paused
3841 && strReconfig == "1")
3842 fSilent = true;
3843
3844 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3845 bool fHotplug = false;
3846 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3847 fHotplug = true;
3848
3849 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3850 return setError(VBOX_E_INVALID_VM_STATE,
3851 tr("Controller '%s' does not support hotplugging"),
3852 aName.c_str());
3853
3854 // check that the port and device are not out of range
3855 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3856 if (FAILED(rc)) return rc;
3857
3858 /* check if the device slot is already busy */
3859 MediumAttachment *pAttachTemp;
3860 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3861 aName,
3862 aControllerPort,
3863 aDevice)))
3864 {
3865 Medium *pMedium = pAttachTemp->i_getMedium();
3866 if (pMedium)
3867 {
3868 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3869 return setError(VBOX_E_OBJECT_IN_USE,
3870 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3871 pMedium->i_getLocationFull().c_str(),
3872 aControllerPort,
3873 aDevice,
3874 aName.c_str());
3875 }
3876 else
3877 return setError(VBOX_E_OBJECT_IN_USE,
3878 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3879 aControllerPort, aDevice, aName.c_str());
3880 }
3881
3882 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3883 if (aMedium && medium.isNull())
3884 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3885
3886 AutoCaller mediumCaller(medium);
3887 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3888
3889 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3890
3891 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3892 && !medium.isNull()
3893 )
3894 return setError(VBOX_E_OBJECT_IN_USE,
3895 tr("Medium '%s' is already attached to this virtual machine"),
3896 medium->i_getLocationFull().c_str());
3897
3898 if (!medium.isNull())
3899 {
3900 MediumType_T mtype = medium->i_getType();
3901 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3902 // For DVDs it's not written to the config file, so needs no global config
3903 // version bump. For floppies it's a new attribute "type", which is ignored
3904 // by older VirtualBox version, so needs no global config version bump either.
3905 // For hard disks this type is not accepted.
3906 if (mtype == MediumType_MultiAttach)
3907 {
3908 // This type is new with VirtualBox 4.0 and therefore requires settings
3909 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3910 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3911 // two reasons: The medium type is a property of the media registry tree, which
3912 // can reside in the global config file (for pre-4.0 media); we would therefore
3913 // possibly need to bump the global config version. We don't want to do that though
3914 // because that might make downgrading to pre-4.0 impossible.
3915 // As a result, we can only use these two new types if the medium is NOT in the
3916 // global registry:
3917 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3918 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3919 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3920 )
3921 return setError(VBOX_E_INVALID_OBJECT_STATE,
3922 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3923 "to machines that were created with VirtualBox 4.0 or later"),
3924 medium->i_getLocationFull().c_str());
3925 }
3926 }
3927
3928 bool fIndirect = false;
3929 if (!medium.isNull())
3930 fIndirect = medium->i_isReadOnly();
3931 bool associate = true;
3932
3933 do
3934 {
3935 if ( aType == DeviceType_HardDisk
3936 && mMediumAttachments.isBackedUp())
3937 {
3938 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3939
3940 /* check if the medium was attached to the VM before we started
3941 * changing attachments in which case the attachment just needs to
3942 * be restored */
3943 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3944 {
3945 AssertReturn(!fIndirect, E_FAIL);
3946
3947 /* see if it's the same bus/channel/device */
3948 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3949 {
3950 /* the simplest case: restore the whole attachment
3951 * and return, nothing else to do */
3952 mMediumAttachments->push_back(pAttachTemp);
3953
3954 /* Reattach the medium to the VM. */
3955 if (fHotplug || fSilent)
3956 {
3957 mediumLock.release();
3958 treeLock.release();
3959 alock.release();
3960
3961 MediumLockList *pMediumLockList(new MediumLockList());
3962
3963 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3964 medium /* pToLockWrite */,
3965 false /* fMediumLockWriteAll */,
3966 NULL,
3967 *pMediumLockList);
3968 alock.acquire();
3969 if (FAILED(rc))
3970 delete pMediumLockList;
3971 else
3972 {
3973 mData->mSession.mLockedMedia.Unlock();
3974 alock.release();
3975 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3976 mData->mSession.mLockedMedia.Lock();
3977 alock.acquire();
3978 }
3979 alock.release();
3980
3981 if (SUCCEEDED(rc))
3982 {
3983 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3984 /* Remove lock list in case of error. */
3985 if (FAILED(rc))
3986 {
3987 mData->mSession.mLockedMedia.Unlock();
3988 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3989 mData->mSession.mLockedMedia.Lock();
3990 }
3991 }
3992 }
3993
3994 return S_OK;
3995 }
3996
3997 /* bus/channel/device differ; we need a new attachment object,
3998 * but don't try to associate it again */
3999 associate = false;
4000 break;
4001 }
4002 }
4003
4004 /* go further only if the attachment is to be indirect */
4005 if (!fIndirect)
4006 break;
4007
4008 /* perform the so called smart attachment logic for indirect
4009 * attachments. Note that smart attachment is only applicable to base
4010 * hard disks. */
4011
4012 if (medium->i_getParent().isNull())
4013 {
4014 /* first, investigate the backup copy of the current hard disk
4015 * attachments to make it possible to re-attach existing diffs to
4016 * another device slot w/o losing their contents */
4017 if (mMediumAttachments.isBackedUp())
4018 {
4019 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4020
4021 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4022 uint32_t foundLevel = 0;
4023
4024 for (MediumAttachmentList::const_iterator
4025 it = oldAtts.begin();
4026 it != oldAtts.end();
4027 ++it)
4028 {
4029 uint32_t level = 0;
4030 MediumAttachment *pAttach = *it;
4031 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4032 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4033 if (pMedium.isNull())
4034 continue;
4035
4036 if (pMedium->i_getBase(&level) == medium)
4037 {
4038 /* skip the hard disk if its currently attached (we
4039 * cannot attach the same hard disk twice) */
4040 if (i_findAttachment(*mMediumAttachments.data(),
4041 pMedium))
4042 continue;
4043
4044 /* matched device, channel and bus (i.e. attached to the
4045 * same place) will win and immediately stop the search;
4046 * otherwise the attachment that has the youngest
4047 * descendant of medium will be used
4048 */
4049 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4050 {
4051 /* the simplest case: restore the whole attachment
4052 * and return, nothing else to do */
4053 mMediumAttachments->push_back(*it);
4054
4055 /* Reattach the medium to the VM. */
4056 if (fHotplug || fSilent)
4057 {
4058 mediumLock.release();
4059 treeLock.release();
4060 alock.release();
4061
4062 MediumLockList *pMediumLockList(new MediumLockList());
4063
4064 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4065 medium /* pToLockWrite */,
4066 false /* fMediumLockWriteAll */,
4067 NULL,
4068 *pMediumLockList);
4069 alock.acquire();
4070 if (FAILED(rc))
4071 delete pMediumLockList;
4072 else
4073 {
4074 mData->mSession.mLockedMedia.Unlock();
4075 alock.release();
4076 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4077 mData->mSession.mLockedMedia.Lock();
4078 alock.acquire();
4079 }
4080 alock.release();
4081
4082 if (SUCCEEDED(rc))
4083 {
4084 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4085 /* Remove lock list in case of error. */
4086 if (FAILED(rc))
4087 {
4088 mData->mSession.mLockedMedia.Unlock();
4089 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4090 mData->mSession.mLockedMedia.Lock();
4091 }
4092 }
4093 }
4094
4095 return S_OK;
4096 }
4097 else if ( foundIt == oldAtts.end()
4098 || level > foundLevel /* prefer younger */
4099 )
4100 {
4101 foundIt = it;
4102 foundLevel = level;
4103 }
4104 }
4105 }
4106
4107 if (foundIt != oldAtts.end())
4108 {
4109 /* use the previously attached hard disk */
4110 medium = (*foundIt)->i_getMedium();
4111 mediumCaller.attach(medium);
4112 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4113 mediumLock.attach(medium);
4114 /* not implicit, doesn't require association with this VM */
4115 fIndirect = false;
4116 associate = false;
4117 /* go right to the MediumAttachment creation */
4118 break;
4119 }
4120 }
4121
4122 /* must give up the medium lock and medium tree lock as below we
4123 * go over snapshots, which needs a lock with higher lock order. */
4124 mediumLock.release();
4125 treeLock.release();
4126
4127 /* then, search through snapshots for the best diff in the given
4128 * hard disk's chain to base the new diff on */
4129
4130 ComObjPtr<Medium> base;
4131 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4132 while (snap)
4133 {
4134 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4135
4136 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4137
4138 MediumAttachment *pAttachFound = NULL;
4139 uint32_t foundLevel = 0;
4140
4141 for (MediumAttachmentList::const_iterator
4142 it = snapAtts.begin();
4143 it != snapAtts.end();
4144 ++it)
4145 {
4146 MediumAttachment *pAttach = *it;
4147 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4148 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4149 if (pMedium.isNull())
4150 continue;
4151
4152 uint32_t level = 0;
4153 if (pMedium->i_getBase(&level) == medium)
4154 {
4155 /* matched device, channel and bus (i.e. attached to the
4156 * same place) will win and immediately stop the search;
4157 * otherwise the attachment that has the youngest
4158 * descendant of medium will be used
4159 */
4160 if ( pAttach->i_getDevice() == aDevice
4161 && pAttach->i_getPort() == aControllerPort
4162 && pAttach->i_getControllerName() == aName
4163 )
4164 {
4165 pAttachFound = pAttach;
4166 break;
4167 }
4168 else if ( !pAttachFound
4169 || level > foundLevel /* prefer younger */
4170 )
4171 {
4172 pAttachFound = pAttach;
4173 foundLevel = level;
4174 }
4175 }
4176 }
4177
4178 if (pAttachFound)
4179 {
4180 base = pAttachFound->i_getMedium();
4181 break;
4182 }
4183
4184 snap = snap->i_getParent();
4185 }
4186
4187 /* re-lock medium tree and the medium, as we need it below */
4188 treeLock.acquire();
4189 mediumLock.acquire();
4190
4191 /* found a suitable diff, use it as a base */
4192 if (!base.isNull())
4193 {
4194 medium = base;
4195 mediumCaller.attach(medium);
4196 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4197 mediumLock.attach(medium);
4198 }
4199 }
4200
4201 Utf8Str strFullSnapshotFolder;
4202 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4203
4204 ComObjPtr<Medium> diff;
4205 diff.createObject();
4206 // store this diff in the same registry as the parent
4207 Guid uuidRegistryParent;
4208 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4209 {
4210 // parent image has no registry: this can happen if we're attaching a new immutable
4211 // image that has not yet been attached (medium then points to the base and we're
4212 // creating the diff image for the immutable, and the parent is not yet registered);
4213 // put the parent in the machine registry then
4214 mediumLock.release();
4215 treeLock.release();
4216 alock.release();
4217 i_addMediumToRegistry(medium);
4218 alock.acquire();
4219 treeLock.acquire();
4220 mediumLock.acquire();
4221 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4222 }
4223 rc = diff->init(mParent,
4224 medium->i_getPreferredDiffFormat(),
4225 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4226 uuidRegistryParent,
4227 DeviceType_HardDisk);
4228 if (FAILED(rc)) return rc;
4229
4230 /* Apply the normal locking logic to the entire chain. */
4231 MediumLockList *pMediumLockList(new MediumLockList());
4232 mediumLock.release();
4233 treeLock.release();
4234 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4235 diff /* pToLockWrite */,
4236 false /* fMediumLockWriteAll */,
4237 medium,
4238 *pMediumLockList);
4239 treeLock.acquire();
4240 mediumLock.acquire();
4241 if (SUCCEEDED(rc))
4242 {
4243 mediumLock.release();
4244 treeLock.release();
4245 rc = pMediumLockList->Lock();
4246 treeLock.acquire();
4247 mediumLock.acquire();
4248 if (FAILED(rc))
4249 setError(rc,
4250 tr("Could not lock medium when creating diff '%s'"),
4251 diff->i_getLocationFull().c_str());
4252 else
4253 {
4254 /* will release the lock before the potentially lengthy
4255 * operation, so protect with the special state */
4256 MachineState_T oldState = mData->mMachineState;
4257 i_setMachineState(MachineState_SettingUp);
4258
4259 mediumLock.release();
4260 treeLock.release();
4261 alock.release();
4262
4263 rc = medium->i_createDiffStorage(diff,
4264 medium->i_getPreferredDiffVariant(),
4265 pMediumLockList,
4266 NULL /* aProgress */,
4267 true /* aWait */);
4268
4269 alock.acquire();
4270 treeLock.acquire();
4271 mediumLock.acquire();
4272
4273 i_setMachineState(oldState);
4274 }
4275 }
4276
4277 /* Unlock the media and free the associated memory. */
4278 delete pMediumLockList;
4279
4280 if (FAILED(rc)) return rc;
4281
4282 /* use the created diff for the actual attachment */
4283 medium = diff;
4284 mediumCaller.attach(medium);
4285 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4286 mediumLock.attach(medium);
4287 }
4288 while (0);
4289
4290 ComObjPtr<MediumAttachment> attachment;
4291 attachment.createObject();
4292 rc = attachment->init(this,
4293 medium,
4294 aName,
4295 aControllerPort,
4296 aDevice,
4297 aType,
4298 fIndirect,
4299 false /* fPassthrough */,
4300 false /* fTempEject */,
4301 false /* fNonRotational */,
4302 false /* fDiscard */,
4303 fHotplug /* fHotPluggable */,
4304 Utf8Str::Empty);
4305 if (FAILED(rc)) return rc;
4306
4307 if (associate && !medium.isNull())
4308 {
4309 // as the last step, associate the medium to the VM
4310 rc = medium->i_addBackReference(mData->mUuid);
4311 // here we can fail because of Deleting, or being in process of creating a Diff
4312 if (FAILED(rc)) return rc;
4313
4314 mediumLock.release();
4315 treeLock.release();
4316 alock.release();
4317 i_addMediumToRegistry(medium);
4318 alock.acquire();
4319 treeLock.acquire();
4320 mediumLock.acquire();
4321 }
4322
4323 /* success: finally remember the attachment */
4324 i_setModified(IsModified_Storage);
4325 mMediumAttachments.backup();
4326 mMediumAttachments->push_back(attachment);
4327
4328 mediumLock.release();
4329 treeLock.release();
4330 alock.release();
4331
4332 if (fHotplug || fSilent)
4333 {
4334 if (!medium.isNull())
4335 {
4336 MediumLockList *pMediumLockList(new MediumLockList());
4337
4338 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4339 medium /* pToLockWrite */,
4340 false /* fMediumLockWriteAll */,
4341 NULL,
4342 *pMediumLockList);
4343 alock.acquire();
4344 if (FAILED(rc))
4345 delete pMediumLockList;
4346 else
4347 {
4348 mData->mSession.mLockedMedia.Unlock();
4349 alock.release();
4350 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4351 mData->mSession.mLockedMedia.Lock();
4352 alock.acquire();
4353 }
4354 alock.release();
4355 }
4356
4357 if (SUCCEEDED(rc))
4358 {
4359 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4360 /* Remove lock list in case of error. */
4361 if (FAILED(rc))
4362 {
4363 mData->mSession.mLockedMedia.Unlock();
4364 mData->mSession.mLockedMedia.Remove(attachment);
4365 mData->mSession.mLockedMedia.Lock();
4366 }
4367 }
4368 }
4369
4370 /* Save modified registries, but skip this machine as it's the caller's
4371 * job to save its settings like all other settings changes. */
4372 mParent->i_unmarkRegistryModified(i_getId());
4373 mParent->i_saveModifiedRegistries();
4374
4375 return rc;
4376}
4377
4378HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4379 LONG aDevice)
4380{
4381 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4382 aName.c_str(), aControllerPort, aDevice));
4383
4384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4385
4386 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4387 if (FAILED(rc)) return rc;
4388
4389 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4390
4391 /* Check for an existing controller. */
4392 ComObjPtr<StorageController> ctl;
4393 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4394 if (FAILED(rc)) return rc;
4395
4396 StorageControllerType_T ctrlType;
4397 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4398 if (FAILED(rc))
4399 return setError(E_FAIL,
4400 tr("Could not get type of controller '%s'"),
4401 aName.c_str());
4402
4403 bool fSilent = false;
4404 Utf8Str strReconfig;
4405
4406 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4407 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4408 if ( mData->mMachineState == MachineState_Paused
4409 && strReconfig == "1")
4410 fSilent = true;
4411
4412 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4413 bool fHotplug = false;
4414 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4415 fHotplug = true;
4416
4417 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4418 return setError(VBOX_E_INVALID_VM_STATE,
4419 tr("Controller '%s' does not support hotplugging"),
4420 aName.c_str());
4421
4422 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4423 aName,
4424 aControllerPort,
4425 aDevice);
4426 if (!pAttach)
4427 return setError(VBOX_E_OBJECT_NOT_FOUND,
4428 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4429 aDevice, aControllerPort, aName.c_str());
4430
4431 if (fHotplug && !pAttach->i_getHotPluggable())
4432 return setError(VBOX_E_NOT_SUPPORTED,
4433 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4434 aDevice, aControllerPort, aName.c_str());
4435
4436 /*
4437 * The VM has to detach the device before we delete any implicit diffs.
4438 * If this fails we can roll back without loosing data.
4439 */
4440 if (fHotplug || fSilent)
4441 {
4442 alock.release();
4443 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4444 alock.acquire();
4445 }
4446 if (FAILED(rc)) return rc;
4447
4448 /* If we are here everything went well and we can delete the implicit now. */
4449 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4450
4451 alock.release();
4452
4453 /* Save modified registries, but skip this machine as it's the caller's
4454 * job to save its settings like all other settings changes. */
4455 mParent->i_unmarkRegistryModified(i_getId());
4456 mParent->i_saveModifiedRegistries();
4457
4458 return rc;
4459}
4460
4461HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4462 LONG aDevice, BOOL aPassthrough)
4463{
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4465 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4466
4467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4468
4469 HRESULT rc = i_checkStateDependency(MutableStateDep);
4470 if (FAILED(rc)) return rc;
4471
4472 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4473
4474 if (Global::IsOnlineOrTransient(mData->mMachineState))
4475 return setError(VBOX_E_INVALID_VM_STATE,
4476 tr("Invalid machine state: %s"),
4477 Global::stringifyMachineState(mData->mMachineState));
4478
4479 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4480 aName,
4481 aControllerPort,
4482 aDevice);
4483 if (!pAttach)
4484 return setError(VBOX_E_OBJECT_NOT_FOUND,
4485 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4486 aDevice, aControllerPort, aName.c_str());
4487
4488
4489 i_setModified(IsModified_Storage);
4490 mMediumAttachments.backup();
4491
4492 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4493
4494 if (pAttach->i_getType() != DeviceType_DVD)
4495 return setError(E_INVALIDARG,
4496 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4497 aDevice, aControllerPort, aName.c_str());
4498 pAttach->i_updatePassthrough(!!aPassthrough);
4499
4500 return S_OK;
4501}
4502
4503HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4504 LONG aDevice, BOOL aTemporaryEject)
4505{
4506
4507 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4508 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4509
4510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4511
4512 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4513 if (FAILED(rc)) return rc;
4514
4515 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4516 aName,
4517 aControllerPort,
4518 aDevice);
4519 if (!pAttach)
4520 return setError(VBOX_E_OBJECT_NOT_FOUND,
4521 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4522 aDevice, aControllerPort, aName.c_str());
4523
4524
4525 i_setModified(IsModified_Storage);
4526 mMediumAttachments.backup();
4527
4528 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4529
4530 if (pAttach->i_getType() != DeviceType_DVD)
4531 return setError(E_INVALIDARG,
4532 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4533 aDevice, aControllerPort, aName.c_str());
4534 pAttach->i_updateTempEject(!!aTemporaryEject);
4535
4536 return S_OK;
4537}
4538
4539HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4540 LONG aDevice, BOOL aNonRotational)
4541{
4542
4543 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4544 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4545
4546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4547
4548 HRESULT rc = i_checkStateDependency(MutableStateDep);
4549 if (FAILED(rc)) return rc;
4550
4551 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4552
4553 if (Global::IsOnlineOrTransient(mData->mMachineState))
4554 return setError(VBOX_E_INVALID_VM_STATE,
4555 tr("Invalid machine state: %s"),
4556 Global::stringifyMachineState(mData->mMachineState));
4557
4558 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4559 aName,
4560 aControllerPort,
4561 aDevice);
4562 if (!pAttach)
4563 return setError(VBOX_E_OBJECT_NOT_FOUND,
4564 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4565 aDevice, aControllerPort, aName.c_str());
4566
4567
4568 i_setModified(IsModified_Storage);
4569 mMediumAttachments.backup();
4570
4571 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4572
4573 if (pAttach->i_getType() != DeviceType_HardDisk)
4574 return setError(E_INVALIDARG,
4575 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"),
4576 aDevice, aControllerPort, aName.c_str());
4577 pAttach->i_updateNonRotational(!!aNonRotational);
4578
4579 return S_OK;
4580}
4581
4582HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4583 LONG aDevice, BOOL aDiscard)
4584{
4585
4586 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4587 aName.c_str(), aControllerPort, aDevice, aDiscard));
4588
4589 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4590
4591 HRESULT rc = i_checkStateDependency(MutableStateDep);
4592 if (FAILED(rc)) return rc;
4593
4594 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4595
4596 if (Global::IsOnlineOrTransient(mData->mMachineState))
4597 return setError(VBOX_E_INVALID_VM_STATE,
4598 tr("Invalid machine state: %s"),
4599 Global::stringifyMachineState(mData->mMachineState));
4600
4601 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4602 aName,
4603 aControllerPort,
4604 aDevice);
4605 if (!pAttach)
4606 return setError(VBOX_E_OBJECT_NOT_FOUND,
4607 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4608 aDevice, aControllerPort, aName.c_str());
4609
4610
4611 i_setModified(IsModified_Storage);
4612 mMediumAttachments.backup();
4613
4614 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4615
4616 if (pAttach->i_getType() != DeviceType_HardDisk)
4617 return setError(E_INVALIDARG,
4618 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"),
4619 aDevice, aControllerPort, aName.c_str());
4620 pAttach->i_updateDiscard(!!aDiscard);
4621
4622 return S_OK;
4623}
4624
4625HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4626 LONG aDevice, BOOL aHotPluggable)
4627{
4628 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4629 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4630
4631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4632
4633 HRESULT rc = i_checkStateDependency(MutableStateDep);
4634 if (FAILED(rc)) return rc;
4635
4636 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4637
4638 if (Global::IsOnlineOrTransient(mData->mMachineState))
4639 return setError(VBOX_E_INVALID_VM_STATE,
4640 tr("Invalid machine state: %s"),
4641 Global::stringifyMachineState(mData->mMachineState));
4642
4643 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4644 aName,
4645 aControllerPort,
4646 aDevice);
4647 if (!pAttach)
4648 return setError(VBOX_E_OBJECT_NOT_FOUND,
4649 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4650 aDevice, aControllerPort, aName.c_str());
4651
4652 /* Check for an existing controller. */
4653 ComObjPtr<StorageController> ctl;
4654 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4655 if (FAILED(rc)) return rc;
4656
4657 StorageControllerType_T ctrlType;
4658 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4659 if (FAILED(rc))
4660 return setError(E_FAIL,
4661 tr("Could not get type of controller '%s'"),
4662 aName.c_str());
4663
4664 if (!i_isControllerHotplugCapable(ctrlType))
4665 return setError(VBOX_E_NOT_SUPPORTED,
4666 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4667 aName.c_str());
4668
4669 i_setModified(IsModified_Storage);
4670 mMediumAttachments.backup();
4671
4672 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4673
4674 if (pAttach->i_getType() == DeviceType_Floppy)
4675 return setError(E_INVALIDARG,
4676 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"),
4677 aDevice, aControllerPort, aName.c_str());
4678 pAttach->i_updateHotPluggable(!!aHotPluggable);
4679
4680 return S_OK;
4681}
4682
4683HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4684 LONG aDevice)
4685{
4686 int rc = S_OK;
4687 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4688 aName.c_str(), aControllerPort, aDevice));
4689
4690 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4691
4692 return rc;
4693}
4694
4695HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4696 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4697{
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4699 aName.c_str(), aControllerPort, aDevice));
4700
4701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4704 if (FAILED(rc)) return rc;
4705
4706 if (Global::IsOnlineOrTransient(mData->mMachineState))
4707 return setError(VBOX_E_INVALID_VM_STATE,
4708 tr("Invalid machine state: %s"),
4709 Global::stringifyMachineState(mData->mMachineState));
4710
4711 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4712 aName,
4713 aControllerPort,
4714 aDevice);
4715 if (!pAttach)
4716 return setError(VBOX_E_OBJECT_NOT_FOUND,
4717 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4718 aDevice, aControllerPort, aName.c_str());
4719
4720
4721 i_setModified(IsModified_Storage);
4722 mMediumAttachments.backup();
4723
4724 IBandwidthGroup *iB = aBandwidthGroup;
4725 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4726 if (aBandwidthGroup && group.isNull())
4727 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4728
4729 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4730
4731 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4732 if (strBandwidthGroupOld.isNotEmpty())
4733 {
4734 /* Get the bandwidth group object and release it - this must not fail. */
4735 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4736 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4737 Assert(SUCCEEDED(rc));
4738
4739 pBandwidthGroupOld->i_release();
4740 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4741 }
4742
4743 if (!group.isNull())
4744 {
4745 group->i_reference();
4746 pAttach->i_updateBandwidthGroup(group->i_getName());
4747 }
4748
4749 return S_OK;
4750}
4751
4752HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4753 LONG aControllerPort,
4754 LONG aDevice,
4755 DeviceType_T aType)
4756{
4757 HRESULT rc = S_OK;
4758
4759 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4760 aName.c_str(), aControllerPort, aDevice, aType));
4761
4762 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4763
4764 return rc;
4765}
4766
4767
4768HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4769 LONG aControllerPort,
4770 LONG aDevice,
4771 BOOL aForce)
4772{
4773 int rc = S_OK;
4774 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4775 aName.c_str(), aControllerPort, aForce));
4776
4777 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4778
4779 return rc;
4780}
4781
4782HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4783 LONG aControllerPort,
4784 LONG aDevice,
4785 const ComPtr<IMedium> &aMedium,
4786 BOOL aForce)
4787{
4788 int rc = S_OK;
4789 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4790 aName.c_str(), aControllerPort, aDevice, aForce));
4791
4792 // request the host lock first, since might be calling Host methods for getting host drives;
4793 // next, protect the media tree all the while we're in here, as well as our member variables
4794 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4795 this->lockHandle(),
4796 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4797
4798 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4799 aName,
4800 aControllerPort,
4801 aDevice);
4802 if (pAttach.isNull())
4803 return setError(VBOX_E_OBJECT_NOT_FOUND,
4804 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4805 aDevice, aControllerPort, aName.c_str());
4806
4807 /* Remember previously mounted medium. The medium before taking the
4808 * backup is not necessarily the same thing. */
4809 ComObjPtr<Medium> oldmedium;
4810 oldmedium = pAttach->i_getMedium();
4811
4812 IMedium *iM = aMedium;
4813 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4814 if (aMedium && pMedium.isNull())
4815 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4816
4817 AutoCaller mediumCaller(pMedium);
4818 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4819
4820 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4821 if (pMedium)
4822 {
4823 DeviceType_T mediumType = pAttach->i_getType();
4824 switch (mediumType)
4825 {
4826 case DeviceType_DVD:
4827 case DeviceType_Floppy:
4828 break;
4829
4830 default:
4831 return setError(VBOX_E_INVALID_OBJECT_STATE,
4832 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4833 aControllerPort,
4834 aDevice,
4835 aName.c_str());
4836 }
4837 }
4838
4839 i_setModified(IsModified_Storage);
4840 mMediumAttachments.backup();
4841
4842 {
4843 // The backup operation makes the pAttach reference point to the
4844 // old settings. Re-get the correct reference.
4845 pAttach = i_findAttachment(*mMediumAttachments.data(),
4846 aName,
4847 aControllerPort,
4848 aDevice);
4849 if (!oldmedium.isNull())
4850 oldmedium->i_removeBackReference(mData->mUuid);
4851 if (!pMedium.isNull())
4852 {
4853 pMedium->i_addBackReference(mData->mUuid);
4854
4855 mediumLock.release();
4856 multiLock.release();
4857 i_addMediumToRegistry(pMedium);
4858 multiLock.acquire();
4859 mediumLock.acquire();
4860 }
4861
4862 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4863 pAttach->i_updateMedium(pMedium);
4864 }
4865
4866 i_setModified(IsModified_Storage);
4867
4868 mediumLock.release();
4869 multiLock.release();
4870 rc = i_onMediumChange(pAttach, aForce);
4871 multiLock.acquire();
4872 mediumLock.acquire();
4873
4874 /* On error roll back this change only. */
4875 if (FAILED(rc))
4876 {
4877 if (!pMedium.isNull())
4878 pMedium->i_removeBackReference(mData->mUuid);
4879 pAttach = i_findAttachment(*mMediumAttachments.data(),
4880 aName,
4881 aControllerPort,
4882 aDevice);
4883 /* If the attachment is gone in the meantime, bail out. */
4884 if (pAttach.isNull())
4885 return rc;
4886 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4887 if (!oldmedium.isNull())
4888 oldmedium->i_addBackReference(mData->mUuid);
4889 pAttach->i_updateMedium(oldmedium);
4890 }
4891
4892 mediumLock.release();
4893 multiLock.release();
4894
4895 /* Save modified registries, but skip this machine as it's the caller's
4896 * job to save its settings like all other settings changes. */
4897 mParent->i_unmarkRegistryModified(i_getId());
4898 mParent->i_saveModifiedRegistries();
4899
4900 return rc;
4901}
4902HRESULT Machine::getMedium(const com::Utf8Str &aName,
4903 LONG aControllerPort,
4904 LONG aDevice,
4905 ComPtr<IMedium> &aMedium)
4906{
4907 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4908 aName.c_str(), aControllerPort, aDevice));
4909
4910 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4911
4912 aMedium = NULL;
4913
4914 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4915 aName,
4916 aControllerPort,
4917 aDevice);
4918 if (pAttach.isNull())
4919 return setError(VBOX_E_OBJECT_NOT_FOUND,
4920 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4921 aDevice, aControllerPort, aName.c_str());
4922
4923 aMedium = pAttach->i_getMedium();
4924
4925 return S_OK;
4926}
4927
4928HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4929{
4930
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4934
4935 return S_OK;
4936}
4937
4938HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4939{
4940 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4941
4942 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4943
4944 return S_OK;
4945}
4946
4947HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4948{
4949 /* Do not assert if slot is out of range, just return the advertised
4950 status. testdriver/vbox.py triggers this in logVmInfo. */
4951 if (aSlot >= mNetworkAdapters.size())
4952 return setError(E_INVALIDARG,
4953 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4954 aSlot, mNetworkAdapters.size());
4955
4956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4957
4958 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4959
4960 return S_OK;
4961}
4962
4963HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4964{
4965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4966
4967 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4968 size_t i = 0;
4969 for (settings::StringsMap::const_iterator
4970 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4971 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4972 ++it, ++i)
4973 aKeys[i] = it->first;
4974
4975 return S_OK;
4976}
4977
4978 /**
4979 * @note Locks this object for reading.
4980 */
4981HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4982 com::Utf8Str &aValue)
4983{
4984 /* start with nothing found */
4985 aValue = "";
4986
4987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4988
4989 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4990 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4991 // found:
4992 aValue = it->second; // source is a Utf8Str
4993
4994 /* return the result to caller (may be empty) */
4995 return S_OK;
4996}
4997
4998 /**
4999 * @note Locks mParent for writing + this object for writing.
5000 */
5001HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5002{
5003 Utf8Str strOldValue; // empty
5004
5005 // locking note: we only hold the read lock briefly to look up the old value,
5006 // then release it and call the onExtraCanChange callbacks. There is a small
5007 // chance of a race insofar as the callback might be called twice if two callers
5008 // change the same key at the same time, but that's a much better solution
5009 // than the deadlock we had here before. The actual changing of the extradata
5010 // is then performed under the write lock and race-free.
5011
5012 // look up the old value first; if nothing has changed then we need not do anything
5013 {
5014 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5015
5016 // For snapshots don't even think about allowing changes, extradata
5017 // is global for a machine, so there is nothing snapshot specific.
5018 if (i_isSnapshotMachine())
5019 return setError(VBOX_E_INVALID_VM_STATE,
5020 tr("Cannot set extradata for a snapshot"));
5021
5022 // check if the right IMachine instance is used
5023 if (mData->mRegistered && !i_isSessionMachine())
5024 return setError(VBOX_E_INVALID_VM_STATE,
5025 tr("Cannot set extradata for an immutable machine"));
5026
5027 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5028 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5029 strOldValue = it->second;
5030 }
5031
5032 bool fChanged;
5033 if ((fChanged = (strOldValue != aValue)))
5034 {
5035 // ask for permission from all listeners outside the locks;
5036 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5037 // lock to copy the list of callbacks to invoke
5038 Bstr error;
5039 Bstr bstrValue(aValue);
5040
5041 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5042 {
5043 const char *sep = error.isEmpty() ? "" : ": ";
5044 CBSTR err = error.raw();
5045 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5046 return setError(E_ACCESSDENIED,
5047 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5048 aKey.c_str(),
5049 aValue.c_str(),
5050 sep,
5051 err);
5052 }
5053
5054 // data is changing and change not vetoed: then write it out under the lock
5055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5056
5057 if (aValue.isEmpty())
5058 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5059 else
5060 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5061 // creates a new key if needed
5062
5063 bool fNeedsGlobalSaveSettings = false;
5064 // This saving of settings is tricky: there is no "old state" for the
5065 // extradata items at all (unlike all other settings), so the old/new
5066 // settings comparison would give a wrong result!
5067 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5068
5069 if (fNeedsGlobalSaveSettings)
5070 {
5071 // save the global settings; for that we should hold only the VirtualBox lock
5072 alock.release();
5073 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5074 mParent->i_saveSettings();
5075 }
5076 }
5077
5078 // fire notification outside the lock
5079 if (fChanged)
5080 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5081
5082 return S_OK;
5083}
5084
5085HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5086{
5087 aProgress = NULL;
5088 NOREF(aSettingsFilePath);
5089 ReturnComNotImplemented();
5090}
5091
5092HRESULT Machine::saveSettings()
5093{
5094 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5095
5096 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5097 if (FAILED(rc)) return rc;
5098
5099 /* the settings file path may never be null */
5100 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5101
5102 /* save all VM data excluding snapshots */
5103 bool fNeedsGlobalSaveSettings = false;
5104 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5105 mlock.release();
5106
5107 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5108 {
5109 // save the global settings; for that we should hold only the VirtualBox lock
5110 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5111 rc = mParent->i_saveSettings();
5112 }
5113
5114 return rc;
5115}
5116
5117
5118HRESULT Machine::discardSettings()
5119{
5120 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5121
5122 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5123 if (FAILED(rc)) return rc;
5124
5125 /*
5126 * during this rollback, the session will be notified if data has
5127 * been actually changed
5128 */
5129 i_rollback(true /* aNotify */);
5130
5131 return S_OK;
5132}
5133
5134/** @note Locks objects! */
5135HRESULT Machine::unregister(AutoCaller &autoCaller,
5136 CleanupMode_T aCleanupMode,
5137 std::vector<ComPtr<IMedium> > &aMedia)
5138{
5139 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5140
5141 Guid id(i_getId());
5142
5143 if (mData->mSession.mState != SessionState_Unlocked)
5144 return setError(VBOX_E_INVALID_OBJECT_STATE,
5145 tr("Cannot unregister the machine '%s' while it is locked"),
5146 mUserData->s.strName.c_str());
5147
5148 // wait for state dependents to drop to zero
5149 i_ensureNoStateDependencies();
5150
5151 if (!mData->mAccessible)
5152 {
5153 // inaccessible maschines can only be unregistered; uninitialize ourselves
5154 // here because currently there may be no unregistered that are inaccessible
5155 // (this state combination is not supported). Note releasing the caller and
5156 // leaving the lock before calling uninit()
5157 alock.release();
5158 autoCaller.release();
5159
5160 uninit();
5161
5162 mParent->i_unregisterMachine(this, id);
5163 // calls VirtualBox::i_saveSettings()
5164
5165 return S_OK;
5166 }
5167
5168 HRESULT rc = S_OK;
5169
5170 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5171 // discard saved state
5172 if (mData->mMachineState == MachineState_Saved)
5173 {
5174 // add the saved state file to the list of files the caller should delete
5175 Assert(!mSSData->strStateFilePath.isEmpty());
5176 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5177
5178 mSSData->strStateFilePath.setNull();
5179
5180 // unconditionally set the machine state to powered off, we now
5181 // know no session has locked the machine
5182 mData->mMachineState = MachineState_PoweredOff;
5183 }
5184
5185 size_t cSnapshots = 0;
5186 if (mData->mFirstSnapshot)
5187 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5188 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5189 // fail now before we start detaching media
5190 return setError(VBOX_E_INVALID_OBJECT_STATE,
5191 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5192 mUserData->s.strName.c_str(), cSnapshots);
5193
5194 // This list collects the medium objects from all medium attachments
5195 // which we will detach from the machine and its snapshots, in a specific
5196 // order which allows for closing all media without getting "media in use"
5197 // errors, simply by going through the list from the front to the back:
5198 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5199 // and must be closed before the parent media from the snapshots, or closing the parents
5200 // will fail because they still have children);
5201 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5202 // the root ("first") snapshot of the machine.
5203 MediaList llMedia;
5204
5205 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5206 && mMediumAttachments->size()
5207 )
5208 {
5209 // we have media attachments: detach them all and add the Medium objects to our list
5210 if (aCleanupMode != CleanupMode_UnregisterOnly)
5211 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5212 else
5213 return setError(VBOX_E_INVALID_OBJECT_STATE,
5214 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5215 mUserData->s.strName.c_str(), mMediumAttachments->size());
5216 }
5217
5218 if (cSnapshots)
5219 {
5220 // add the media from the medium attachments of the snapshots to llMedia
5221 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5222 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5223 // into the children first
5224
5225 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5226 MachineState_T oldState = mData->mMachineState;
5227 mData->mMachineState = MachineState_DeletingSnapshot;
5228
5229 // make a copy of the first snapshot so the refcount does not drop to 0
5230 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5231 // because of the AutoCaller voodoo)
5232 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5233
5234 // GO!
5235 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5236
5237 mData->mMachineState = oldState;
5238 }
5239
5240 if (FAILED(rc))
5241 {
5242 i_rollbackMedia();
5243 return rc;
5244 }
5245
5246 // commit all the media changes made above
5247 i_commitMedia();
5248
5249 mData->mRegistered = false;
5250
5251 // machine lock no longer needed
5252 alock.release();
5253
5254 // return media to caller
5255 aMedia.resize(llMedia.size());
5256 size_t i = 0;
5257 for (MediaList::const_iterator
5258 it = llMedia.begin();
5259 it != llMedia.end();
5260 ++it, ++i)
5261 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5262
5263 mParent->i_unregisterMachine(this, id);
5264 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5265
5266 return S_OK;
5267}
5268
5269/**
5270 * Task record for deleting a machine config.
5271 */
5272class Machine::DeleteConfigTask
5273 : public Machine::Task
5274{
5275public:
5276 DeleteConfigTask(Machine *m,
5277 Progress *p,
5278 const Utf8Str &t,
5279 const RTCList<ComPtr<IMedium> > &llMediums,
5280 const StringsList &llFilesToDelete)
5281 : Task(m, p, t),
5282 m_llMediums(llMediums),
5283 m_llFilesToDelete(llFilesToDelete)
5284 {}
5285
5286private:
5287 void handler()
5288 {
5289 try
5290 {
5291 m_pMachine->i_deleteConfigHandler(*this);
5292 }
5293 catch (...)
5294 {
5295 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5296 }
5297 }
5298
5299 RTCList<ComPtr<IMedium> > m_llMediums;
5300 StringsList m_llFilesToDelete;
5301
5302 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5303};
5304
5305/**
5306 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5307 * SessionMachine::taskHandler().
5308 *
5309 * @note Locks this object for writing.
5310 *
5311 * @param task
5312 * @return
5313 */
5314void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5315{
5316 LogFlowThisFuncEnter();
5317
5318 AutoCaller autoCaller(this);
5319 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5320 if (FAILED(autoCaller.rc()))
5321 {
5322 /* we might have been uninitialized because the session was accidentally
5323 * closed by the client, so don't assert */
5324 HRESULT rc = setError(E_FAIL,
5325 tr("The session has been accidentally closed"));
5326 task.m_pProgress->i_notifyComplete(rc);
5327 LogFlowThisFuncLeave();
5328 return;
5329 }
5330
5331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5332
5333 HRESULT rc = S_OK;
5334
5335 try
5336 {
5337 ULONG uLogHistoryCount = 3;
5338 ComPtr<ISystemProperties> systemProperties;
5339 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5340 if (FAILED(rc)) throw rc;
5341
5342 if (!systemProperties.isNull())
5343 {
5344 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5345 if (FAILED(rc)) throw rc;
5346 }
5347
5348 MachineState_T oldState = mData->mMachineState;
5349 i_setMachineState(MachineState_SettingUp);
5350 alock.release();
5351 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5352 {
5353 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5354 {
5355 AutoCaller mac(pMedium);
5356 if (FAILED(mac.rc())) throw mac.rc();
5357 Utf8Str strLocation = pMedium->i_getLocationFull();
5358 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5359 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5360 if (FAILED(rc)) throw rc;
5361 }
5362 if (pMedium->i_isMediumFormatFile())
5363 {
5364 ComPtr<IProgress> pProgress2;
5365 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5366 if (FAILED(rc)) throw rc;
5367 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5368 if (FAILED(rc)) throw rc;
5369 /* Check the result of the asynchronous process. */
5370 LONG iRc;
5371 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5372 if (FAILED(rc)) throw rc;
5373 /* If the thread of the progress object has an error, then
5374 * retrieve the error info from there, or it'll be lost. */
5375 if (FAILED(iRc))
5376 throw setError(ProgressErrorInfo(pProgress2));
5377 }
5378
5379 /* Close the medium, deliberately without checking the return
5380 * code, and without leaving any trace in the error info, as
5381 * a failure here is a very minor issue, which shouldn't happen
5382 * as above we even managed to delete the medium. */
5383 {
5384 ErrorInfoKeeper eik;
5385 pMedium->Close();
5386 }
5387 }
5388 i_setMachineState(oldState);
5389 alock.acquire();
5390
5391 // delete the files pushed on the task list by Machine::Delete()
5392 // (this includes saved states of the machine and snapshots and
5393 // medium storage files from the IMedium list passed in, and the
5394 // machine XML file)
5395 for (StringsList::const_iterator
5396 it = task.m_llFilesToDelete.begin();
5397 it != task.m_llFilesToDelete.end();
5398 ++it)
5399 {
5400 const Utf8Str &strFile = *it;
5401 LogFunc(("Deleting file %s\n", strFile.c_str()));
5402 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5403 if (FAILED(rc)) throw rc;
5404
5405 int vrc = RTFileDelete(strFile.c_str());
5406 if (RT_FAILURE(vrc))
5407 throw setError(VBOX_E_IPRT_ERROR,
5408 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5409 }
5410
5411 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5412 if (FAILED(rc)) throw rc;
5413
5414 /* delete the settings only when the file actually exists */
5415 if (mData->pMachineConfigFile->fileExists())
5416 {
5417 /* Delete any backup or uncommitted XML files. Ignore failures.
5418 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5419 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5420 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5421 RTFileDelete(otherXml.c_str());
5422 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5423 RTFileDelete(otherXml.c_str());
5424
5425 /* delete the Logs folder, nothing important should be left
5426 * there (we don't check for errors because the user might have
5427 * some private files there that we don't want to delete) */
5428 Utf8Str logFolder;
5429 getLogFolder(logFolder);
5430 Assert(logFolder.length());
5431 if (RTDirExists(logFolder.c_str()))
5432 {
5433 /* Delete all VBox.log[.N] files from the Logs folder
5434 * (this must be in sync with the rotation logic in
5435 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5436 * files that may have been created by the GUI. */
5437 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5438 logFolder.c_str(), RTPATH_DELIMITER);
5439 RTFileDelete(log.c_str());
5440 log = Utf8StrFmt("%s%cVBox.png",
5441 logFolder.c_str(), RTPATH_DELIMITER);
5442 RTFileDelete(log.c_str());
5443 for (int i = uLogHistoryCount; i > 0; i--)
5444 {
5445 log = Utf8StrFmt("%s%cVBox.log.%d",
5446 logFolder.c_str(), RTPATH_DELIMITER, i);
5447 RTFileDelete(log.c_str());
5448 log = Utf8StrFmt("%s%cVBox.png.%d",
5449 logFolder.c_str(), RTPATH_DELIMITER, i);
5450 RTFileDelete(log.c_str());
5451 }
5452#if defined(RT_OS_WINDOWS)
5453 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5454 RTFileDelete(log.c_str());
5455 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5456 RTFileDelete(log.c_str());
5457#endif
5458
5459 RTDirRemove(logFolder.c_str());
5460 }
5461
5462 /* delete the Snapshots folder, nothing important should be left
5463 * there (we don't check for errors because the user might have
5464 * some private files there that we don't want to delete) */
5465 Utf8Str strFullSnapshotFolder;
5466 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5467 Assert(!strFullSnapshotFolder.isEmpty());
5468 if (RTDirExists(strFullSnapshotFolder.c_str()))
5469 RTDirRemove(strFullSnapshotFolder.c_str());
5470
5471 // delete the directory that contains the settings file, but only
5472 // if it matches the VM name
5473 Utf8Str settingsDir;
5474 if (i_isInOwnDir(&settingsDir))
5475 RTDirRemove(settingsDir.c_str());
5476 }
5477
5478 alock.release();
5479
5480 mParent->i_saveModifiedRegistries();
5481 }
5482 catch (HRESULT aRC) { rc = aRC; }
5483
5484 task.m_pProgress->i_notifyComplete(rc);
5485
5486 LogFlowThisFuncLeave();
5487}
5488
5489HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5490{
5491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5492
5493 HRESULT rc = i_checkStateDependency(MutableStateDep);
5494 if (FAILED(rc)) return rc;
5495
5496 if (mData->mRegistered)
5497 return setError(VBOX_E_INVALID_VM_STATE,
5498 tr("Cannot delete settings of a registered machine"));
5499
5500 // collect files to delete
5501 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5502 if (mData->pMachineConfigFile->fileExists())
5503 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5504
5505 RTCList<ComPtr<IMedium> > llMediums;
5506 for (size_t i = 0; i < aMedia.size(); ++i)
5507 {
5508 IMedium *pIMedium(aMedia[i]);
5509 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5510 if (pMedium.isNull())
5511 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5512 SafeArray<BSTR> ids;
5513 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5514 if (FAILED(rc)) return rc;
5515 /* At this point the medium should not have any back references
5516 * anymore. If it has it is attached to another VM and *must* not
5517 * deleted. */
5518 if (ids.size() < 1)
5519 llMediums.append(pMedium);
5520 }
5521
5522 ComObjPtr<Progress> pProgress;
5523 pProgress.createObject();
5524 rc = pProgress->init(i_getVirtualBox(),
5525 static_cast<IMachine*>(this) /* aInitiator */,
5526 tr("Deleting files"),
5527 true /* fCancellable */,
5528 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5529 tr("Collecting file inventory"));
5530 if (FAILED(rc))
5531 return rc;
5532
5533 /* create and start the task on a separate thread (note that it will not
5534 * start working until we release alock) */
5535 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5536 rc = pTask->createThread();
5537 if (FAILED(rc))
5538 return rc;
5539
5540 pProgress.queryInterfaceTo(aProgress.asOutParam());
5541
5542 LogFlowFuncLeave();
5543
5544 return S_OK;
5545}
5546
5547HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5548{
5549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5550
5551 ComObjPtr<Snapshot> pSnapshot;
5552 HRESULT rc;
5553
5554 if (aNameOrId.isEmpty())
5555 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5556 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5557 else
5558 {
5559 Guid uuid(aNameOrId);
5560 if (uuid.isValid())
5561 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5562 else
5563 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5564 }
5565 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5566
5567 return rc;
5568}
5569
5570HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5571{
5572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5573
5574 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5575 if (FAILED(rc)) return rc;
5576
5577 ComObjPtr<SharedFolder> sharedFolder;
5578 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5579 if (SUCCEEDED(rc))
5580 return setError(VBOX_E_OBJECT_IN_USE,
5581 tr("Shared folder named '%s' already exists"),
5582 aName.c_str());
5583
5584 sharedFolder.createObject();
5585 rc = sharedFolder->init(i_getMachine(),
5586 aName,
5587 aHostPath,
5588 !!aWritable,
5589 !!aAutomount,
5590 true /* fFailOnError */);
5591 if (FAILED(rc)) return rc;
5592
5593 i_setModified(IsModified_SharedFolders);
5594 mHWData.backup();
5595 mHWData->mSharedFolders.push_back(sharedFolder);
5596
5597 /* inform the direct session if any */
5598 alock.release();
5599 i_onSharedFolderChange();
5600
5601 return S_OK;
5602}
5603
5604HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5605{
5606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5607
5608 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5609 if (FAILED(rc)) return rc;
5610
5611 ComObjPtr<SharedFolder> sharedFolder;
5612 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5613 if (FAILED(rc)) return rc;
5614
5615 i_setModified(IsModified_SharedFolders);
5616 mHWData.backup();
5617 mHWData->mSharedFolders.remove(sharedFolder);
5618
5619 /* inform the direct session if any */
5620 alock.release();
5621 i_onSharedFolderChange();
5622
5623 return S_OK;
5624}
5625
5626HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5627{
5628 /* start with No */
5629 *aCanShow = FALSE;
5630
5631 ComPtr<IInternalSessionControl> directControl;
5632 {
5633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5634
5635 if (mData->mSession.mState != SessionState_Locked)
5636 return setError(VBOX_E_INVALID_VM_STATE,
5637 tr("Machine is not locked for session (session state: %s)"),
5638 Global::stringifySessionState(mData->mSession.mState));
5639
5640 if (mData->mSession.mLockType == LockType_VM)
5641 directControl = mData->mSession.mDirectControl;
5642 }
5643
5644 /* ignore calls made after #OnSessionEnd() is called */
5645 if (!directControl)
5646 return S_OK;
5647
5648 LONG64 dummy;
5649 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5650}
5651
5652HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5653{
5654 ComPtr<IInternalSessionControl> directControl;
5655 {
5656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5657
5658 if (mData->mSession.mState != SessionState_Locked)
5659 return setError(E_FAIL,
5660 tr("Machine is not locked for session (session state: %s)"),
5661 Global::stringifySessionState(mData->mSession.mState));
5662
5663 if (mData->mSession.mLockType == LockType_VM)
5664 directControl = mData->mSession.mDirectControl;
5665 }
5666
5667 /* ignore calls made after #OnSessionEnd() is called */
5668 if (!directControl)
5669 return S_OK;
5670
5671 BOOL dummy;
5672 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5673}
5674
5675#ifdef VBOX_WITH_GUEST_PROPS
5676/**
5677 * Look up a guest property in VBoxSVC's internal structures.
5678 */
5679HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5680 com::Utf8Str &aValue,
5681 LONG64 *aTimestamp,
5682 com::Utf8Str &aFlags) const
5683{
5684 using namespace guestProp;
5685
5686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5687 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5688
5689 if (it != mHWData->mGuestProperties.end())
5690 {
5691 char szFlags[MAX_FLAGS_LEN + 1];
5692 aValue = it->second.strValue;
5693 *aTimestamp = it->second.mTimestamp;
5694 writeFlags(it->second.mFlags, szFlags);
5695 aFlags = Utf8Str(szFlags);
5696 }
5697
5698 return S_OK;
5699}
5700
5701/**
5702 * Query the VM that a guest property belongs to for the property.
5703 * @returns E_ACCESSDENIED if the VM process is not available or not
5704 * currently handling queries and the lookup should then be done in
5705 * VBoxSVC.
5706 */
5707HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5708 com::Utf8Str &aValue,
5709 LONG64 *aTimestamp,
5710 com::Utf8Str &aFlags) const
5711{
5712 HRESULT rc = S_OK;
5713 BSTR bValue = NULL;
5714 BSTR bFlags = NULL;
5715
5716 ComPtr<IInternalSessionControl> directControl;
5717 {
5718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5719 if (mData->mSession.mLockType == LockType_VM)
5720 directControl = mData->mSession.mDirectControl;
5721 }
5722
5723 /* ignore calls made after #OnSessionEnd() is called */
5724 if (!directControl)
5725 rc = E_ACCESSDENIED;
5726 else
5727 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5728 0 /* accessMode */,
5729 &bValue, aTimestamp, &bFlags);
5730
5731 aValue = bValue;
5732 aFlags = bFlags;
5733
5734 return rc;
5735}
5736#endif // VBOX_WITH_GUEST_PROPS
5737
5738HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5739 com::Utf8Str &aValue,
5740 LONG64 *aTimestamp,
5741 com::Utf8Str &aFlags)
5742{
5743#ifndef VBOX_WITH_GUEST_PROPS
5744 ReturnComNotImplemented();
5745#else // VBOX_WITH_GUEST_PROPS
5746
5747 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5748
5749 if (rc == E_ACCESSDENIED)
5750 /* The VM is not running or the service is not (yet) accessible */
5751 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5752 return rc;
5753#endif // VBOX_WITH_GUEST_PROPS
5754}
5755
5756HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5757{
5758 LONG64 dummyTimestamp;
5759 com::Utf8Str dummyFlags;
5760 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5761 return rc;
5762
5763}
5764HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5765{
5766 com::Utf8Str dummyFlags;
5767 com::Utf8Str dummyValue;
5768 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5769 return rc;
5770}
5771
5772#ifdef VBOX_WITH_GUEST_PROPS
5773/**
5774 * Set a guest property in VBoxSVC's internal structures.
5775 */
5776HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5777 const com::Utf8Str &aFlags, bool fDelete)
5778{
5779 using namespace guestProp;
5780
5781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5782 HRESULT rc = S_OK;
5783
5784 rc = i_checkStateDependency(MutableOrSavedStateDep);
5785 if (FAILED(rc)) return rc;
5786
5787 try
5788 {
5789 uint32_t fFlags = NILFLAG;
5790 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5791 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5792
5793 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5794 if (it == mHWData->mGuestProperties.end())
5795 {
5796 if (!fDelete)
5797 {
5798 i_setModified(IsModified_MachineData);
5799 mHWData.backupEx();
5800
5801 RTTIMESPEC time;
5802 HWData::GuestProperty prop;
5803 prop.strValue = Bstr(aValue).raw();
5804 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5805 prop.mFlags = fFlags;
5806 mHWData->mGuestProperties[aName] = prop;
5807 }
5808 }
5809 else
5810 {
5811 if (it->second.mFlags & (RDONLYHOST))
5812 {
5813 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5814 }
5815 else
5816 {
5817 i_setModified(IsModified_MachineData);
5818 mHWData.backupEx();
5819
5820 /* The backupEx() operation invalidates our iterator,
5821 * so get a new one. */
5822 it = mHWData->mGuestProperties.find(aName);
5823 Assert(it != mHWData->mGuestProperties.end());
5824
5825 if (!fDelete)
5826 {
5827 RTTIMESPEC time;
5828 it->second.strValue = aValue;
5829 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5830 it->second.mFlags = fFlags;
5831 }
5832 else
5833 mHWData->mGuestProperties.erase(it);
5834 }
5835 }
5836
5837 if (SUCCEEDED(rc))
5838 {
5839 alock.release();
5840
5841 mParent->i_onGuestPropertyChange(mData->mUuid,
5842 Bstr(aName).raw(),
5843 Bstr(aValue).raw(),
5844 Bstr(aFlags).raw());
5845 }
5846 }
5847 catch (std::bad_alloc &)
5848 {
5849 rc = E_OUTOFMEMORY;
5850 }
5851
5852 return rc;
5853}
5854
5855/**
5856 * Set a property on the VM that that property belongs to.
5857 * @returns E_ACCESSDENIED if the VM process is not available or not
5858 * currently handling queries and the setting should then be done in
5859 * VBoxSVC.
5860 */
5861HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5862 const com::Utf8Str &aFlags, bool fDelete)
5863{
5864 HRESULT rc;
5865
5866 try
5867 {
5868 ComPtr<IInternalSessionControl> directControl;
5869 {
5870 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5871 if (mData->mSession.mLockType == LockType_VM)
5872 directControl = mData->mSession.mDirectControl;
5873 }
5874
5875 BSTR dummy = NULL; /* will not be changed (setter) */
5876 LONG64 dummy64;
5877 if (!directControl)
5878 rc = E_ACCESSDENIED;
5879 else
5880 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5881 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5882 fDelete? 2: 1 /* accessMode */,
5883 &dummy, &dummy64, &dummy);
5884 }
5885 catch (std::bad_alloc &)
5886 {
5887 rc = E_OUTOFMEMORY;
5888 }
5889
5890 return rc;
5891}
5892#endif // VBOX_WITH_GUEST_PROPS
5893
5894HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5895 const com::Utf8Str &aFlags)
5896{
5897#ifndef VBOX_WITH_GUEST_PROPS
5898 ReturnComNotImplemented();
5899#else // VBOX_WITH_GUEST_PROPS
5900 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5901 if (rc == E_ACCESSDENIED)
5902 /* The VM is not running or the service is not (yet) accessible */
5903 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5904 return rc;
5905#endif // VBOX_WITH_GUEST_PROPS
5906}
5907
5908HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5909{
5910 return setGuestProperty(aProperty, aValue, "");
5911}
5912
5913HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5914{
5915#ifndef VBOX_WITH_GUEST_PROPS
5916 ReturnComNotImplemented();
5917#else // VBOX_WITH_GUEST_PROPS
5918 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5919 if (rc == E_ACCESSDENIED)
5920 /* The VM is not running or the service is not (yet) accessible */
5921 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5922 return rc;
5923#endif // VBOX_WITH_GUEST_PROPS
5924}
5925
5926#ifdef VBOX_WITH_GUEST_PROPS
5927/**
5928 * Enumerate the guest properties in VBoxSVC's internal structures.
5929 */
5930HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5931 std::vector<com::Utf8Str> &aNames,
5932 std::vector<com::Utf8Str> &aValues,
5933 std::vector<LONG64> &aTimestamps,
5934 std::vector<com::Utf8Str> &aFlags)
5935{
5936 using namespace guestProp;
5937
5938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5939 Utf8Str strPatterns(aPatterns);
5940
5941 HWData::GuestPropertyMap propMap;
5942
5943 /*
5944 * Look for matching patterns and build up a list.
5945 */
5946 for (HWData::GuestPropertyMap::const_iterator
5947 it = mHWData->mGuestProperties.begin();
5948 it != mHWData->mGuestProperties.end();
5949 ++it)
5950 {
5951 if ( strPatterns.isEmpty()
5952 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5953 RTSTR_MAX,
5954 it->first.c_str(),
5955 RTSTR_MAX,
5956 NULL)
5957 )
5958 propMap.insert(*it);
5959 }
5960
5961 alock.release();
5962
5963 /*
5964 * And build up the arrays for returning the property information.
5965 */
5966 size_t cEntries = propMap.size();
5967
5968 aNames.resize(cEntries);
5969 aValues.resize(cEntries);
5970 aTimestamps.resize(cEntries);
5971 aFlags.resize(cEntries);
5972
5973 char szFlags[MAX_FLAGS_LEN + 1];
5974 size_t i = 0;
5975 for (HWData::GuestPropertyMap::const_iterator
5976 it = propMap.begin();
5977 it != propMap.end();
5978 ++it, ++i)
5979 {
5980 aNames[i] = it->first;
5981 aValues[i] = it->second.strValue;
5982 aTimestamps[i] = it->second.mTimestamp;
5983 writeFlags(it->second.mFlags, szFlags);
5984 aFlags[i] = Utf8Str(szFlags);
5985 }
5986
5987 return S_OK;
5988}
5989
5990/**
5991 * Enumerate the properties managed by a VM.
5992 * @returns E_ACCESSDENIED if the VM process is not available or not
5993 * currently handling queries and the setting should then be done in
5994 * VBoxSVC.
5995 */
5996HRESULT Machine::i_enumerateGuestPropertiesOnVM(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 HRESULT rc;
6003 ComPtr<IInternalSessionControl> directControl;
6004 {
6005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6006 if (mData->mSession.mLockType == LockType_VM)
6007 directControl = mData->mSession.mDirectControl;
6008 }
6009
6010 com::SafeArray<BSTR> bNames;
6011 com::SafeArray<BSTR> bValues;
6012 com::SafeArray<LONG64> bTimestamps;
6013 com::SafeArray<BSTR> bFlags;
6014
6015 if (!directControl)
6016 rc = E_ACCESSDENIED;
6017 else
6018 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6019 ComSafeArrayAsOutParam(bNames),
6020 ComSafeArrayAsOutParam(bValues),
6021 ComSafeArrayAsOutParam(bTimestamps),
6022 ComSafeArrayAsOutParam(bFlags));
6023 size_t i;
6024 aNames.resize(bNames.size());
6025 for (i = 0; i < bNames.size(); ++i)
6026 aNames[i] = Utf8Str(bNames[i]);
6027 aValues.resize(bValues.size());
6028 for (i = 0; i < bValues.size(); ++i)
6029 aValues[i] = Utf8Str(bValues[i]);
6030 aTimestamps.resize(bTimestamps.size());
6031 for (i = 0; i < bTimestamps.size(); ++i)
6032 aTimestamps[i] = bTimestamps[i];
6033 aFlags.resize(bFlags.size());
6034 for (i = 0; i < bFlags.size(); ++i)
6035 aFlags[i] = Utf8Str(bFlags[i]);
6036
6037 return rc;
6038}
6039#endif // VBOX_WITH_GUEST_PROPS
6040HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6041 std::vector<com::Utf8Str> &aNames,
6042 std::vector<com::Utf8Str> &aValues,
6043 std::vector<LONG64> &aTimestamps,
6044 std::vector<com::Utf8Str> &aFlags)
6045{
6046#ifndef VBOX_WITH_GUEST_PROPS
6047 ReturnComNotImplemented();
6048#else // VBOX_WITH_GUEST_PROPS
6049
6050 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6051
6052 if (rc == E_ACCESSDENIED)
6053 /* The VM is not running or the service is not (yet) accessible */
6054 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6055 return rc;
6056#endif // VBOX_WITH_GUEST_PROPS
6057}
6058
6059HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6060 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6061{
6062 MediumAttachmentList atts;
6063
6064 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6065 if (FAILED(rc)) return rc;
6066
6067 aMediumAttachments.resize(atts.size());
6068 size_t i = 0;
6069 for (MediumAttachmentList::const_iterator
6070 it = atts.begin();
6071 it != atts.end();
6072 ++it, ++i)
6073 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6074
6075 return S_OK;
6076}
6077
6078HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6079 LONG aControllerPort,
6080 LONG aDevice,
6081 ComPtr<IMediumAttachment> &aAttachment)
6082{
6083 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6084 aName.c_str(), aControllerPort, aDevice));
6085
6086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6087
6088 aAttachment = NULL;
6089
6090 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6091 aName,
6092 aControllerPort,
6093 aDevice);
6094 if (pAttach.isNull())
6095 return setError(VBOX_E_OBJECT_NOT_FOUND,
6096 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6097 aDevice, aControllerPort, aName.c_str());
6098
6099 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6100
6101 return S_OK;
6102}
6103
6104
6105HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6106 StorageBus_T aConnectionType,
6107 ComPtr<IStorageController> &aController)
6108{
6109 if ( (aConnectionType <= StorageBus_Null)
6110 || (aConnectionType > StorageBus_PCIe))
6111 return setError(E_INVALIDARG,
6112 tr("Invalid connection type: %d"),
6113 aConnectionType);
6114
6115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6116
6117 HRESULT rc = i_checkStateDependency(MutableStateDep);
6118 if (FAILED(rc)) return rc;
6119
6120 /* try to find one with the name first. */
6121 ComObjPtr<StorageController> ctrl;
6122
6123 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6124 if (SUCCEEDED(rc))
6125 return setError(VBOX_E_OBJECT_IN_USE,
6126 tr("Storage controller named '%s' already exists"),
6127 aName.c_str());
6128
6129 ctrl.createObject();
6130
6131 /* get a new instance number for the storage controller */
6132 ULONG ulInstance = 0;
6133 bool fBootable = true;
6134 for (StorageControllerList::const_iterator
6135 it = mStorageControllers->begin();
6136 it != mStorageControllers->end();
6137 ++it)
6138 {
6139 if ((*it)->i_getStorageBus() == aConnectionType)
6140 {
6141 ULONG ulCurInst = (*it)->i_getInstance();
6142
6143 if (ulCurInst >= ulInstance)
6144 ulInstance = ulCurInst + 1;
6145
6146 /* Only one controller of each type can be marked as bootable. */
6147 if ((*it)->i_getBootable())
6148 fBootable = false;
6149 }
6150 }
6151
6152 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6153 if (FAILED(rc)) return rc;
6154
6155 i_setModified(IsModified_Storage);
6156 mStorageControllers.backup();
6157 mStorageControllers->push_back(ctrl);
6158
6159 ctrl.queryInterfaceTo(aController.asOutParam());
6160
6161 /* inform the direct session if any */
6162 alock.release();
6163 i_onStorageControllerChange();
6164
6165 return S_OK;
6166}
6167
6168HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6169 ComPtr<IStorageController> &aStorageController)
6170{
6171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6172
6173 ComObjPtr<StorageController> ctrl;
6174
6175 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6176 if (SUCCEEDED(rc))
6177 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6178
6179 return rc;
6180}
6181
6182HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6183 ULONG aInstance,
6184 ComPtr<IStorageController> &aStorageController)
6185{
6186 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6187
6188 for (StorageControllerList::const_iterator
6189 it = mStorageControllers->begin();
6190 it != mStorageControllers->end();
6191 ++it)
6192 {
6193 if ( (*it)->i_getStorageBus() == aConnectionType
6194 && (*it)->i_getInstance() == aInstance)
6195 {
6196 (*it).queryInterfaceTo(aStorageController.asOutParam());
6197 return S_OK;
6198 }
6199 }
6200
6201 return setError(VBOX_E_OBJECT_NOT_FOUND,
6202 tr("Could not find a storage controller with instance number '%lu'"),
6203 aInstance);
6204}
6205
6206HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6207{
6208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6209
6210 HRESULT rc = i_checkStateDependency(MutableStateDep);
6211 if (FAILED(rc)) return rc;
6212
6213 ComObjPtr<StorageController> ctrl;
6214
6215 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6216 if (SUCCEEDED(rc))
6217 {
6218 /* Ensure that only one controller of each type is marked as bootable. */
6219 if (aBootable == TRUE)
6220 {
6221 for (StorageControllerList::const_iterator
6222 it = mStorageControllers->begin();
6223 it != mStorageControllers->end();
6224 ++it)
6225 {
6226 ComObjPtr<StorageController> aCtrl = (*it);
6227
6228 if ( (aCtrl->i_getName() != aName)
6229 && aCtrl->i_getBootable() == TRUE
6230 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6231 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6232 {
6233 aCtrl->i_setBootable(FALSE);
6234 break;
6235 }
6236 }
6237 }
6238
6239 if (SUCCEEDED(rc))
6240 {
6241 ctrl->i_setBootable(aBootable);
6242 i_setModified(IsModified_Storage);
6243 }
6244 }
6245
6246 if (SUCCEEDED(rc))
6247 {
6248 /* inform the direct session if any */
6249 alock.release();
6250 i_onStorageControllerChange();
6251 }
6252
6253 return rc;
6254}
6255
6256HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6257{
6258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 HRESULT rc = i_checkStateDependency(MutableStateDep);
6261 if (FAILED(rc)) return rc;
6262
6263 ComObjPtr<StorageController> ctrl;
6264 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6265 if (FAILED(rc)) return rc;
6266
6267 {
6268 /* find all attached devices to the appropriate storage controller and detach them all */
6269 // make a temporary list because detachDevice invalidates iterators into
6270 // mMediumAttachments
6271 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6272
6273 for (MediumAttachmentList::const_iterator
6274 it = llAttachments2.begin();
6275 it != llAttachments2.end();
6276 ++it)
6277 {
6278 MediumAttachment *pAttachTemp = *it;
6279
6280 AutoCaller localAutoCaller(pAttachTemp);
6281 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6282
6283 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6284
6285 if (pAttachTemp->i_getControllerName() == aName)
6286 {
6287 rc = i_detachDevice(pAttachTemp, alock, NULL);
6288 if (FAILED(rc)) return rc;
6289 }
6290 }
6291 }
6292
6293 /* We can remove it now. */
6294 i_setModified(IsModified_Storage);
6295 mStorageControllers.backup();
6296
6297 ctrl->i_unshare();
6298
6299 mStorageControllers->remove(ctrl);
6300
6301 /* inform the direct session if any */
6302 alock.release();
6303 i_onStorageControllerChange();
6304
6305 return S_OK;
6306}
6307
6308HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6309 ComPtr<IUSBController> &aController)
6310{
6311 if ( (aType <= USBControllerType_Null)
6312 || (aType >= USBControllerType_Last))
6313 return setError(E_INVALIDARG,
6314 tr("Invalid USB controller type: %d"),
6315 aType);
6316
6317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 HRESULT rc = i_checkStateDependency(MutableStateDep);
6320 if (FAILED(rc)) return rc;
6321
6322 /* try to find one with the same type first. */
6323 ComObjPtr<USBController> ctrl;
6324
6325 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6326 if (SUCCEEDED(rc))
6327 return setError(VBOX_E_OBJECT_IN_USE,
6328 tr("USB controller named '%s' already exists"),
6329 aName.c_str());
6330
6331 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6332 ULONG maxInstances;
6333 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6334 if (FAILED(rc))
6335 return rc;
6336
6337 ULONG cInstances = i_getUSBControllerCountByType(aType);
6338 if (cInstances >= maxInstances)
6339 return setError(E_INVALIDARG,
6340 tr("Too many USB controllers of this type"));
6341
6342 ctrl.createObject();
6343
6344 rc = ctrl->init(this, aName, aType);
6345 if (FAILED(rc)) return rc;
6346
6347 i_setModified(IsModified_USB);
6348 mUSBControllers.backup();
6349 mUSBControllers->push_back(ctrl);
6350
6351 ctrl.queryInterfaceTo(aController.asOutParam());
6352
6353 /* inform the direct session if any */
6354 alock.release();
6355 i_onUSBControllerChange();
6356
6357 return S_OK;
6358}
6359
6360HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6361{
6362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6363
6364 ComObjPtr<USBController> ctrl;
6365
6366 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6367 if (SUCCEEDED(rc))
6368 ctrl.queryInterfaceTo(aController.asOutParam());
6369
6370 return rc;
6371}
6372
6373HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6374 ULONG *aControllers)
6375{
6376 if ( (aType <= USBControllerType_Null)
6377 || (aType >= USBControllerType_Last))
6378 return setError(E_INVALIDARG,
6379 tr("Invalid USB controller type: %d"),
6380 aType);
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 ComObjPtr<USBController> ctrl;
6385
6386 *aControllers = i_getUSBControllerCountByType(aType);
6387
6388 return S_OK;
6389}
6390
6391HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6392{
6393
6394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6395
6396 HRESULT rc = i_checkStateDependency(MutableStateDep);
6397 if (FAILED(rc)) return rc;
6398
6399 ComObjPtr<USBController> ctrl;
6400 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6401 if (FAILED(rc)) return rc;
6402
6403 i_setModified(IsModified_USB);
6404 mUSBControllers.backup();
6405
6406 ctrl->i_unshare();
6407
6408 mUSBControllers->remove(ctrl);
6409
6410 /* inform the direct session if any */
6411 alock.release();
6412 i_onUSBControllerChange();
6413
6414 return S_OK;
6415}
6416
6417HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6418 ULONG *aOriginX,
6419 ULONG *aOriginY,
6420 ULONG *aWidth,
6421 ULONG *aHeight,
6422 BOOL *aEnabled)
6423{
6424 uint32_t u32OriginX= 0;
6425 uint32_t u32OriginY= 0;
6426 uint32_t u32Width = 0;
6427 uint32_t u32Height = 0;
6428 uint16_t u16Flags = 0;
6429
6430 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6431 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6432 if (RT_FAILURE(vrc))
6433 {
6434#ifdef RT_OS_WINDOWS
6435 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6436 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6437 * So just assign fEnable to TRUE again.
6438 * The right fix would be to change GUI API wrappers to make sure that parameters
6439 * are changed only if API succeeds.
6440 */
6441 *aEnabled = TRUE;
6442#endif
6443 return setError(VBOX_E_IPRT_ERROR,
6444 tr("Saved guest size is not available (%Rrc)"),
6445 vrc);
6446 }
6447
6448 *aOriginX = u32OriginX;
6449 *aOriginY = u32OriginY;
6450 *aWidth = u32Width;
6451 *aHeight = u32Height;
6452 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6453
6454 return S_OK;
6455}
6456
6457HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6458 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6459{
6460 if (aScreenId != 0)
6461 return E_NOTIMPL;
6462
6463 if ( aBitmapFormat != BitmapFormat_BGR0
6464 && aBitmapFormat != BitmapFormat_BGRA
6465 && aBitmapFormat != BitmapFormat_RGBA
6466 && aBitmapFormat != BitmapFormat_PNG)
6467 return setError(E_NOTIMPL,
6468 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6469
6470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6471
6472 uint8_t *pu8Data = NULL;
6473 uint32_t cbData = 0;
6474 uint32_t u32Width = 0;
6475 uint32_t u32Height = 0;
6476
6477 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6478
6479 if (RT_FAILURE(vrc))
6480 return setError(VBOX_E_IPRT_ERROR,
6481 tr("Saved thumbnail data is not available (%Rrc)"),
6482 vrc);
6483
6484 HRESULT hr = S_OK;
6485
6486 *aWidth = u32Width;
6487 *aHeight = u32Height;
6488
6489 if (cbData > 0)
6490 {
6491 /* Convert pixels to the format expected by the API caller. */
6492 if (aBitmapFormat == BitmapFormat_BGR0)
6493 {
6494 /* [0] B, [1] G, [2] R, [3] 0. */
6495 aData.resize(cbData);
6496 memcpy(&aData.front(), pu8Data, cbData);
6497 }
6498 else if (aBitmapFormat == BitmapFormat_BGRA)
6499 {
6500 /* [0] B, [1] G, [2] R, [3] A. */
6501 aData.resize(cbData);
6502 for (uint32_t i = 0; i < cbData; i += 4)
6503 {
6504 aData[i] = pu8Data[i];
6505 aData[i + 1] = pu8Data[i + 1];
6506 aData[i + 2] = pu8Data[i + 2];
6507 aData[i + 3] = 0xff;
6508 }
6509 }
6510 else if (aBitmapFormat == BitmapFormat_RGBA)
6511 {
6512 /* [0] R, [1] G, [2] B, [3] A. */
6513 aData.resize(cbData);
6514 for (uint32_t i = 0; i < cbData; i += 4)
6515 {
6516 aData[i] = pu8Data[i + 2];
6517 aData[i + 1] = pu8Data[i + 1];
6518 aData[i + 2] = pu8Data[i];
6519 aData[i + 3] = 0xff;
6520 }
6521 }
6522 else if (aBitmapFormat == BitmapFormat_PNG)
6523 {
6524 uint8_t *pu8PNG = NULL;
6525 uint32_t cbPNG = 0;
6526 uint32_t cxPNG = 0;
6527 uint32_t cyPNG = 0;
6528
6529 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6530
6531 if (RT_SUCCESS(vrc))
6532 {
6533 aData.resize(cbPNG);
6534 if (cbPNG)
6535 memcpy(&aData.front(), pu8PNG, cbPNG);
6536 }
6537 else
6538 hr = setError(VBOX_E_IPRT_ERROR,
6539 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6540 vrc);
6541
6542 RTMemFree(pu8PNG);
6543 }
6544 }
6545
6546 freeSavedDisplayScreenshot(pu8Data);
6547
6548 return hr;
6549}
6550
6551HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6552 ULONG *aWidth,
6553 ULONG *aHeight,
6554 std::vector<BitmapFormat_T> &aBitmapFormats)
6555{
6556 if (aScreenId != 0)
6557 return E_NOTIMPL;
6558
6559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6560
6561 uint8_t *pu8Data = NULL;
6562 uint32_t cbData = 0;
6563 uint32_t u32Width = 0;
6564 uint32_t u32Height = 0;
6565
6566 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6567
6568 if (RT_FAILURE(vrc))
6569 return setError(VBOX_E_IPRT_ERROR,
6570 tr("Saved screenshot data is not available (%Rrc)"),
6571 vrc);
6572
6573 *aWidth = u32Width;
6574 *aHeight = u32Height;
6575 aBitmapFormats.resize(1);
6576 aBitmapFormats[0] = BitmapFormat_PNG;
6577
6578 freeSavedDisplayScreenshot(pu8Data);
6579
6580 return S_OK;
6581}
6582
6583HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6584 BitmapFormat_T aBitmapFormat,
6585 ULONG *aWidth,
6586 ULONG *aHeight,
6587 std::vector<BYTE> &aData)
6588{
6589 if (aScreenId != 0)
6590 return E_NOTIMPL;
6591
6592 if (aBitmapFormat != BitmapFormat_PNG)
6593 return E_NOTIMPL;
6594
6595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6596
6597 uint8_t *pu8Data = NULL;
6598 uint32_t cbData = 0;
6599 uint32_t u32Width = 0;
6600 uint32_t u32Height = 0;
6601
6602 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6603
6604 if (RT_FAILURE(vrc))
6605 return setError(VBOX_E_IPRT_ERROR,
6606 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6607 vrc);
6608
6609 *aWidth = u32Width;
6610 *aHeight = u32Height;
6611
6612 aData.resize(cbData);
6613 if (cbData)
6614 memcpy(&aData.front(), pu8Data, cbData);
6615
6616 freeSavedDisplayScreenshot(pu8Data);
6617
6618 return S_OK;
6619}
6620
6621HRESULT Machine::hotPlugCPU(ULONG aCpu)
6622{
6623 HRESULT rc = S_OK;
6624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6625
6626 if (!mHWData->mCPUHotPlugEnabled)
6627 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6628
6629 if (aCpu >= mHWData->mCPUCount)
6630 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6631
6632 if (mHWData->mCPUAttached[aCpu])
6633 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6634
6635 alock.release();
6636 rc = i_onCPUChange(aCpu, false);
6637 alock.acquire();
6638 if (FAILED(rc)) return rc;
6639
6640 i_setModified(IsModified_MachineData);
6641 mHWData.backup();
6642 mHWData->mCPUAttached[aCpu] = true;
6643
6644 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6645 if (Global::IsOnline(mData->mMachineState))
6646 i_saveSettings(NULL);
6647
6648 return S_OK;
6649}
6650
6651HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6652{
6653 HRESULT rc = S_OK;
6654
6655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 if (!mHWData->mCPUHotPlugEnabled)
6658 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6659
6660 if (aCpu >= SchemaDefs::MaxCPUCount)
6661 return setError(E_INVALIDARG,
6662 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6663 SchemaDefs::MaxCPUCount);
6664
6665 if (!mHWData->mCPUAttached[aCpu])
6666 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6667
6668 /* CPU 0 can't be detached */
6669 if (aCpu == 0)
6670 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6671
6672 alock.release();
6673 rc = i_onCPUChange(aCpu, true);
6674 alock.acquire();
6675 if (FAILED(rc)) return rc;
6676
6677 i_setModified(IsModified_MachineData);
6678 mHWData.backup();
6679 mHWData->mCPUAttached[aCpu] = false;
6680
6681 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6682 if (Global::IsOnline(mData->mMachineState))
6683 i_saveSettings(NULL);
6684
6685 return S_OK;
6686}
6687
6688HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6689{
6690 *aAttached = false;
6691
6692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6693
6694 /* If hotplug is enabled the CPU is always enabled. */
6695 if (!mHWData->mCPUHotPlugEnabled)
6696 {
6697 if (aCpu < mHWData->mCPUCount)
6698 *aAttached = true;
6699 }
6700 else
6701 {
6702 if (aCpu < SchemaDefs::MaxCPUCount)
6703 *aAttached = mHWData->mCPUAttached[aCpu];
6704 }
6705
6706 return S_OK;
6707}
6708
6709HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6710{
6711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6712
6713 Utf8Str log = i_getLogFilename(aIdx);
6714 if (!RTFileExists(log.c_str()))
6715 log.setNull();
6716 aFilename = log;
6717
6718 return S_OK;
6719}
6720
6721HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6722{
6723 if (aSize < 0)
6724 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6725
6726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6727
6728 HRESULT rc = S_OK;
6729 Utf8Str log = i_getLogFilename(aIdx);
6730
6731 /* do not unnecessarily hold the lock while doing something which does
6732 * not need the lock and potentially takes a long time. */
6733 alock.release();
6734
6735 /* Limit the chunk size to 32K for now, as that gives better performance
6736 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6737 * One byte expands to approx. 25 bytes of breathtaking XML. */
6738 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6739 aData.resize(cbData);
6740
6741 RTFILE LogFile;
6742 int vrc = RTFileOpen(&LogFile, log.c_str(),
6743 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6744 if (RT_SUCCESS(vrc))
6745 {
6746 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6747 if (RT_SUCCESS(vrc))
6748 aData.resize(cbData);
6749 else
6750 rc = setError(VBOX_E_IPRT_ERROR,
6751 tr("Could not read log file '%s' (%Rrc)"),
6752 log.c_str(), vrc);
6753 RTFileClose(LogFile);
6754 }
6755 else
6756 rc = setError(VBOX_E_IPRT_ERROR,
6757 tr("Could not open log file '%s' (%Rrc)"),
6758 log.c_str(), vrc);
6759
6760 if (FAILED(rc))
6761 aData.resize(0);
6762
6763 return rc;
6764}
6765
6766
6767/**
6768 * Currently this method doesn't attach device to the running VM,
6769 * just makes sure it's plugged on next VM start.
6770 */
6771HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6772{
6773 // lock scope
6774 {
6775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 HRESULT rc = i_checkStateDependency(MutableStateDep);
6778 if (FAILED(rc)) return rc;
6779
6780 ChipsetType_T aChipset = ChipsetType_PIIX3;
6781 COMGETTER(ChipsetType)(&aChipset);
6782
6783 if (aChipset != ChipsetType_ICH9)
6784 {
6785 return setError(E_INVALIDARG,
6786 tr("Host PCI attachment only supported with ICH9 chipset"));
6787 }
6788
6789 // check if device with this host PCI address already attached
6790 for (HWData::PCIDeviceAssignmentList::const_iterator
6791 it = mHWData->mPCIDeviceAssignments.begin();
6792 it != mHWData->mPCIDeviceAssignments.end();
6793 ++it)
6794 {
6795 LONG iHostAddress = -1;
6796 ComPtr<PCIDeviceAttachment> pAttach;
6797 pAttach = *it;
6798 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6799 if (iHostAddress == aHostAddress)
6800 return setError(E_INVALIDARG,
6801 tr("Device with host PCI address already attached to this VM"));
6802 }
6803
6804 ComObjPtr<PCIDeviceAttachment> pda;
6805 char name[32];
6806
6807 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6808 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6809 pda.createObject();
6810 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6811 i_setModified(IsModified_MachineData);
6812 mHWData.backup();
6813 mHWData->mPCIDeviceAssignments.push_back(pda);
6814 }
6815
6816 return S_OK;
6817}
6818
6819/**
6820 * Currently this method doesn't detach device from the running VM,
6821 * just makes sure it's not plugged on next VM start.
6822 */
6823HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6824{
6825 ComObjPtr<PCIDeviceAttachment> pAttach;
6826 bool fRemoved = false;
6827 HRESULT rc;
6828
6829 // lock scope
6830 {
6831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6832
6833 rc = i_checkStateDependency(MutableStateDep);
6834 if (FAILED(rc)) return rc;
6835
6836 for (HWData::PCIDeviceAssignmentList::const_iterator
6837 it = mHWData->mPCIDeviceAssignments.begin();
6838 it != mHWData->mPCIDeviceAssignments.end();
6839 ++it)
6840 {
6841 LONG iHostAddress = -1;
6842 pAttach = *it;
6843 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6844 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6845 {
6846 i_setModified(IsModified_MachineData);
6847 mHWData.backup();
6848 mHWData->mPCIDeviceAssignments.remove(pAttach);
6849 fRemoved = true;
6850 break;
6851 }
6852 }
6853 }
6854
6855
6856 /* Fire event outside of the lock */
6857 if (fRemoved)
6858 {
6859 Assert(!pAttach.isNull());
6860 ComPtr<IEventSource> es;
6861 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6862 Assert(SUCCEEDED(rc));
6863 Bstr mid;
6864 rc = this->COMGETTER(Id)(mid.asOutParam());
6865 Assert(SUCCEEDED(rc));
6866 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6867 }
6868
6869 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6870 tr("No host PCI device %08x attached"),
6871 aHostAddress
6872 );
6873}
6874
6875HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6876{
6877 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6878
6879 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6880 size_t i = 0;
6881 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6882 it = mHWData->mPCIDeviceAssignments.begin();
6883 it != mHWData->mPCIDeviceAssignments.end();
6884 ++it, ++i)
6885 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6886
6887 return S_OK;
6888}
6889
6890HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6891{
6892 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6893
6894 return S_OK;
6895}
6896
6897HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6898{
6899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6900
6901 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6902
6903 return S_OK;
6904}
6905
6906HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6907{
6908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6909 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6910 if (SUCCEEDED(hrc))
6911 {
6912 hrc = mHWData.backupEx();
6913 if (SUCCEEDED(hrc))
6914 {
6915 i_setModified(IsModified_MachineData);
6916 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6917 }
6918 }
6919 return hrc;
6920}
6921
6922HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6923{
6924 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6925 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6926 return S_OK;
6927}
6928
6929HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6930{
6931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6932 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6933 if (SUCCEEDED(hrc))
6934 {
6935 hrc = mHWData.backupEx();
6936 if (SUCCEEDED(hrc))
6937 {
6938 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6939 if (SUCCEEDED(hrc))
6940 i_setModified(IsModified_MachineData);
6941 }
6942 }
6943 return hrc;
6944}
6945
6946HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6947{
6948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6949
6950 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6951
6952 return S_OK;
6953}
6954
6955HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6956{
6957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6958 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6959 if (SUCCEEDED(hrc))
6960 {
6961 hrc = mHWData.backupEx();
6962 if (SUCCEEDED(hrc))
6963 {
6964 i_setModified(IsModified_MachineData);
6965 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6966 }
6967 }
6968 return hrc;
6969}
6970
6971HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6972{
6973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6974
6975 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6976
6977 return S_OK;
6978}
6979
6980HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6981{
6982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6983
6984 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6985 if ( SUCCEEDED(hrc)
6986 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6987 {
6988 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6989 int vrc;
6990
6991 if (aAutostartEnabled)
6992 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6993 else
6994 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6995
6996 if (RT_SUCCESS(vrc))
6997 {
6998 hrc = mHWData.backupEx();
6999 if (SUCCEEDED(hrc))
7000 {
7001 i_setModified(IsModified_MachineData);
7002 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7003 }
7004 }
7005 else if (vrc == VERR_NOT_SUPPORTED)
7006 hrc = setError(VBOX_E_NOT_SUPPORTED,
7007 tr("The VM autostart feature is not supported on this platform"));
7008 else if (vrc == VERR_PATH_NOT_FOUND)
7009 hrc = setError(E_FAIL,
7010 tr("The path to the autostart database is not set"));
7011 else
7012 hrc = setError(E_UNEXPECTED,
7013 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7014 aAutostartEnabled ? "Adding" : "Removing",
7015 mUserData->s.strName.c_str(), vrc);
7016 }
7017 return hrc;
7018}
7019
7020HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7021{
7022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7023
7024 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7025
7026 return S_OK;
7027}
7028
7029HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7030{
7031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7032 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7033 if (SUCCEEDED(hrc))
7034 {
7035 hrc = mHWData.backupEx();
7036 if (SUCCEEDED(hrc))
7037 {
7038 i_setModified(IsModified_MachineData);
7039 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7040 }
7041 }
7042 return hrc;
7043}
7044
7045HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7046{
7047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7048
7049 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7050
7051 return S_OK;
7052}
7053
7054HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7055{
7056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7057 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7058 if ( SUCCEEDED(hrc)
7059 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7060 {
7061 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7062 int vrc;
7063
7064 if (aAutostopType != AutostopType_Disabled)
7065 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7066 else
7067 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7068
7069 if (RT_SUCCESS(vrc))
7070 {
7071 hrc = mHWData.backupEx();
7072 if (SUCCEEDED(hrc))
7073 {
7074 i_setModified(IsModified_MachineData);
7075 mHWData->mAutostart.enmAutostopType = aAutostopType;
7076 }
7077 }
7078 else if (vrc == VERR_NOT_SUPPORTED)
7079 hrc = setError(VBOX_E_NOT_SUPPORTED,
7080 tr("The VM autostop feature is not supported on this platform"));
7081 else if (vrc == VERR_PATH_NOT_FOUND)
7082 hrc = setError(E_FAIL,
7083 tr("The path to the autostart database is not set"));
7084 else
7085 hrc = setError(E_UNEXPECTED,
7086 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7087 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7088 mUserData->s.strName.c_str(), vrc);
7089 }
7090 return hrc;
7091}
7092
7093HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7094{
7095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7096
7097 aDefaultFrontend = mHWData->mDefaultFrontend;
7098
7099 return S_OK;
7100}
7101
7102HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7103{
7104 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7105 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7106 if (SUCCEEDED(hrc))
7107 {
7108 hrc = mHWData.backupEx();
7109 if (SUCCEEDED(hrc))
7110 {
7111 i_setModified(IsModified_MachineData);
7112 mHWData->mDefaultFrontend = aDefaultFrontend;
7113 }
7114 }
7115 return hrc;
7116}
7117
7118HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7119{
7120 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7121 size_t cbIcon = mUserData->s.ovIcon.size();
7122 aIcon.resize(cbIcon);
7123 if (cbIcon)
7124 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7125 return S_OK;
7126}
7127
7128HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7129{
7130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7131 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7132 if (SUCCEEDED(hrc))
7133 {
7134 i_setModified(IsModified_MachineData);
7135 mUserData.backup();
7136 size_t cbIcon = aIcon.size();
7137 mUserData->s.ovIcon.resize(cbIcon);
7138 if (cbIcon)
7139 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7140 }
7141 return hrc;
7142}
7143
7144HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7145{
7146#ifdef VBOX_WITH_USB
7147 *aUSBProxyAvailable = true;
7148#else
7149 *aUSBProxyAvailable = false;
7150#endif
7151 return S_OK;
7152}
7153
7154HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7155{
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 aVMProcessPriority = mUserData->s.strVMPriority;
7159
7160 return S_OK;
7161}
7162
7163HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7164{
7165 RT_NOREF(aVMProcessPriority);
7166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7167 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7168 if (SUCCEEDED(hrc))
7169 {
7170 /** @todo r=klaus: currently this is marked as not implemented, as
7171 * the code for setting the priority of the process is not there
7172 * (neither when starting the VM nor at runtime). */
7173 ReturnComNotImplemented();
7174#if 0
7175 hrc = mUserData.backupEx();
7176 if (SUCCEEDED(hrc))
7177 {
7178 i_setModified(IsModified_MachineData);
7179 mUserData->s.strVMPriority = aVMProcessPriority;
7180 }
7181#endif
7182 }
7183 return hrc;
7184}
7185
7186HRESULT Machine::getUnattended(ComPtr<IUnattended> &aUnattended)
7187{
7188#ifdef VBOX_WITH_UNATTENDED
7189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7190 if (mUnattended.isNotNull())
7191 aUnattended = mUnattended;
7192 else
7193 {
7194 /* Do on-demand creation. */
7195 alock.release();
7196 AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS);
7197 if (mUnattended.isNull())
7198 {
7199 unconst(mUnattended).createObject();
7200 HRESULT hrc = mUnattended->init(this);
7201 if (FAILED(hrc))
7202 {
7203 mUnattended->uninit();
7204 unconst(mUnattended).setNull();
7205 return hrc;
7206 }
7207 }
7208 aUnattended = mUnattended;
7209 }
7210 return S_OK;
7211#else
7212 NOREF(aUnattended);
7213 return E_NOTIMPL;
7214#endif
7215}
7216
7217HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7218 ComPtr<IProgress> &aProgress)
7219{
7220 ComObjPtr<Progress> pP;
7221 Progress *ppP = pP;
7222 IProgress *iP = static_cast<IProgress *>(ppP);
7223 IProgress **pProgress = &iP;
7224
7225 IMachine *pTarget = aTarget;
7226
7227 /* Convert the options. */
7228 RTCList<CloneOptions_T> optList;
7229 if (aOptions.size())
7230 for (size_t i = 0; i < aOptions.size(); ++i)
7231 optList.append(aOptions[i]);
7232
7233 if (optList.contains(CloneOptions_Link))
7234 {
7235 if (!i_isSnapshotMachine())
7236 return setError(E_INVALIDARG,
7237 tr("Linked clone can only be created from a snapshot"));
7238 if (aMode != CloneMode_MachineState)
7239 return setError(E_INVALIDARG,
7240 tr("Linked clone can only be created for a single machine state"));
7241 }
7242 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7243
7244 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7245
7246 HRESULT rc = pWorker->start(pProgress);
7247
7248 pP = static_cast<Progress *>(*pProgress);
7249 pP.queryInterfaceTo(aProgress.asOutParam());
7250
7251 return rc;
7252
7253}
7254
7255HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7256{
7257 NOREF(aProgress);
7258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7259
7260 // This check should always fail.
7261 HRESULT rc = i_checkStateDependency(MutableStateDep);
7262 if (FAILED(rc)) return rc;
7263
7264 AssertFailedReturn(E_NOTIMPL);
7265}
7266
7267HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7268{
7269 NOREF(aSavedStateFile);
7270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7271
7272 // This check should always fail.
7273 HRESULT rc = i_checkStateDependency(MutableStateDep);
7274 if (FAILED(rc)) return rc;
7275
7276 AssertFailedReturn(E_NOTIMPL);
7277}
7278
7279HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7280{
7281 NOREF(aFRemoveFile);
7282 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7283
7284 // This check should always fail.
7285 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7286 if (FAILED(rc)) return rc;
7287
7288 AssertFailedReturn(E_NOTIMPL);
7289}
7290
7291// public methods for internal purposes
7292/////////////////////////////////////////////////////////////////////////////
7293
7294/**
7295 * Adds the given IsModified_* flag to the dirty flags of the machine.
7296 * This must be called either during i_loadSettings or under the machine write lock.
7297 * @param fl Flag
7298 * @param fAllowStateModification If state modifications are allowed.
7299 */
7300void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7301{
7302 mData->flModifications |= fl;
7303 if (fAllowStateModification && i_isStateModificationAllowed())
7304 mData->mCurrentStateModified = true;
7305}
7306
7307/**
7308 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7309 * care of the write locking.
7310 *
7311 * @param fModification The flag to add.
7312 * @param fAllowStateModification If state modifications are allowed.
7313 */
7314void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7315{
7316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7317 i_setModified(fModification, fAllowStateModification);
7318}
7319
7320/**
7321 * Saves the registry entry of this machine to the given configuration node.
7322 *
7323 * @param data Machine registry data.
7324 *
7325 * @note locks this object for reading.
7326 */
7327HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7328{
7329 AutoLimitedCaller autoCaller(this);
7330 AssertComRCReturnRC(autoCaller.rc());
7331
7332 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7333
7334 data.uuid = mData->mUuid;
7335 data.strSettingsFile = mData->m_strConfigFile;
7336
7337 return S_OK;
7338}
7339
7340/**
7341 * Calculates the absolute path of the given path taking the directory of the
7342 * machine settings file as the current directory.
7343 *
7344 * @param strPath Path to calculate the absolute path for.
7345 * @param aResult Where to put the result (used only on success, can be the
7346 * same Utf8Str instance as passed in @a aPath).
7347 * @return IPRT result.
7348 *
7349 * @note Locks this object for reading.
7350 */
7351int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7352{
7353 AutoCaller autoCaller(this);
7354 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7355
7356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7357
7358 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7359
7360 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7361
7362 strSettingsDir.stripFilename();
7363 char folder[RTPATH_MAX];
7364 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7365 if (RT_SUCCESS(vrc))
7366 aResult = folder;
7367
7368 return vrc;
7369}
7370
7371/**
7372 * Copies strSource to strTarget, making it relative to the machine folder
7373 * if it is a subdirectory thereof, or simply copying it otherwise.
7374 *
7375 * @param strSource Path to evaluate and copy.
7376 * @param strTarget Buffer to receive target path.
7377 *
7378 * @note Locks this object for reading.
7379 */
7380void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7381 Utf8Str &strTarget)
7382{
7383 AutoCaller autoCaller(this);
7384 AssertComRCReturn(autoCaller.rc(), (void)0);
7385
7386 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7387
7388 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7389 // use strTarget as a temporary buffer to hold the machine settings dir
7390 strTarget = mData->m_strConfigFileFull;
7391 strTarget.stripFilename();
7392 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7393 {
7394 // is relative: then append what's left
7395 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7396 // for empty paths (only possible for subdirs) use "." to avoid
7397 // triggering default settings for not present config attributes.
7398 if (strTarget.isEmpty())
7399 strTarget = ".";
7400 }
7401 else
7402 // is not relative: then overwrite
7403 strTarget = strSource;
7404}
7405
7406/**
7407 * Returns the full path to the machine's log folder in the
7408 * \a aLogFolder argument.
7409 */
7410void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7411{
7412 AutoCaller autoCaller(this);
7413 AssertComRCReturnVoid(autoCaller.rc());
7414
7415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7416
7417 char szTmp[RTPATH_MAX];
7418 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7419 if (RT_SUCCESS(vrc))
7420 {
7421 if (szTmp[0] && !mUserData.isNull())
7422 {
7423 char szTmp2[RTPATH_MAX];
7424 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7425 if (RT_SUCCESS(vrc))
7426 aLogFolder = Utf8StrFmt("%s%c%s",
7427 szTmp2,
7428 RTPATH_DELIMITER,
7429 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7430 }
7431 else
7432 vrc = VERR_PATH_IS_RELATIVE;
7433 }
7434
7435 if (RT_FAILURE(vrc))
7436 {
7437 // fallback if VBOX_USER_LOGHOME is not set or invalid
7438 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7439 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7440 aLogFolder.append(RTPATH_DELIMITER);
7441 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7442 }
7443}
7444
7445/**
7446 * Returns the full path to the machine's log file for an given index.
7447 */
7448Utf8Str Machine::i_getLogFilename(ULONG idx)
7449{
7450 Utf8Str logFolder;
7451 getLogFolder(logFolder);
7452 Assert(logFolder.length());
7453
7454 Utf8Str log;
7455 if (idx == 0)
7456 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7457#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7458 else if (idx == 1)
7459 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7460 else
7461 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7462#else
7463 else
7464 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7465#endif
7466 return log;
7467}
7468
7469/**
7470 * Returns the full path to the machine's hardened log file.
7471 */
7472Utf8Str Machine::i_getHardeningLogFilename(void)
7473{
7474 Utf8Str strFilename;
7475 getLogFolder(strFilename);
7476 Assert(strFilename.length());
7477 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7478 return strFilename;
7479}
7480
7481
7482/**
7483 * Composes a unique saved state filename based on the current system time. The filename is
7484 * granular to the second so this will work so long as no more than one snapshot is taken on
7485 * a machine per second.
7486 *
7487 * Before version 4.1, we used this formula for saved state files:
7488 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7489 * which no longer works because saved state files can now be shared between the saved state of the
7490 * "saved" machine and an online snapshot, and the following would cause problems:
7491 * 1) save machine
7492 * 2) create online snapshot from that machine state --> reusing saved state file
7493 * 3) save machine again --> filename would be reused, breaking the online snapshot
7494 *
7495 * So instead we now use a timestamp.
7496 *
7497 * @param strStateFilePath
7498 */
7499
7500void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7501{
7502 AutoCaller autoCaller(this);
7503 AssertComRCReturnVoid(autoCaller.rc());
7504
7505 {
7506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7507 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7508 }
7509
7510 RTTIMESPEC ts;
7511 RTTimeNow(&ts);
7512 RTTIME time;
7513 RTTimeExplode(&time, &ts);
7514
7515 strStateFilePath += RTPATH_DELIMITER;
7516 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7517 time.i32Year, time.u8Month, time.u8MonthDay,
7518 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7519}
7520
7521/**
7522 * Returns the full path to the default video capture file.
7523 */
7524void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7525{
7526 AutoCaller autoCaller(this);
7527 AssertComRCReturnVoid(autoCaller.rc());
7528
7529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7530
7531 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7532 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7533 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7534}
7535
7536/**
7537 * Returns whether at least one USB controller is present for the VM.
7538 */
7539bool Machine::i_isUSBControllerPresent()
7540{
7541 AutoCaller autoCaller(this);
7542 AssertComRCReturn(autoCaller.rc(), false);
7543
7544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7545
7546 return (mUSBControllers->size() > 0);
7547}
7548
7549/**
7550 * @note Locks this object for writing, calls the client process
7551 * (inside the lock).
7552 */
7553HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7554 const Utf8Str &strFrontend,
7555 const Utf8Str &strEnvironment,
7556 ProgressProxy *aProgress)
7557{
7558 LogFlowThisFuncEnter();
7559
7560 AssertReturn(aControl, E_FAIL);
7561 AssertReturn(aProgress, E_FAIL);
7562 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7563
7564 AutoCaller autoCaller(this);
7565 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7566
7567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7568
7569 if (!mData->mRegistered)
7570 return setError(E_UNEXPECTED,
7571 tr("The machine '%s' is not registered"),
7572 mUserData->s.strName.c_str());
7573
7574 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7575
7576 /* The process started when launching a VM with separate UI/VM processes is always
7577 * the UI process, i.e. needs special handling as it won't claim the session. */
7578 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7579
7580 if (fSeparate)
7581 {
7582 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7583 return setError(VBOX_E_INVALID_OBJECT_STATE,
7584 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7585 mUserData->s.strName.c_str());
7586 }
7587 else
7588 {
7589 if ( mData->mSession.mState == SessionState_Locked
7590 || mData->mSession.mState == SessionState_Spawning
7591 || mData->mSession.mState == SessionState_Unlocking)
7592 return setError(VBOX_E_INVALID_OBJECT_STATE,
7593 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7594 mUserData->s.strName.c_str());
7595
7596 /* may not be busy */
7597 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7598 }
7599
7600 /* get the path to the executable */
7601 char szPath[RTPATH_MAX];
7602 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7603 size_t cchBufLeft = strlen(szPath);
7604 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7605 szPath[cchBufLeft] = 0;
7606 char *pszNamePart = szPath + cchBufLeft;
7607 cchBufLeft = sizeof(szPath) - cchBufLeft;
7608
7609 int vrc = VINF_SUCCESS;
7610 RTPROCESS pid = NIL_RTPROCESS;
7611
7612 RTENV env = RTENV_DEFAULT;
7613
7614 if (!strEnvironment.isEmpty())
7615 {
7616 char *newEnvStr = NULL;
7617
7618 do
7619 {
7620 /* clone the current environment */
7621 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7622 AssertRCBreakStmt(vrc2, vrc = vrc2);
7623
7624 newEnvStr = RTStrDup(strEnvironment.c_str());
7625 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7626
7627 /* put new variables to the environment
7628 * (ignore empty variable names here since RTEnv API
7629 * intentionally doesn't do that) */
7630 char *var = newEnvStr;
7631 for (char *p = newEnvStr; *p; ++p)
7632 {
7633 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7634 {
7635 *p = '\0';
7636 if (*var)
7637 {
7638 char *val = strchr(var, '=');
7639 if (val)
7640 {
7641 *val++ = '\0';
7642 vrc2 = RTEnvSetEx(env, var, val);
7643 }
7644 else
7645 vrc2 = RTEnvUnsetEx(env, var);
7646 if (RT_FAILURE(vrc2))
7647 break;
7648 }
7649 var = p + 1;
7650 }
7651 }
7652 if (RT_SUCCESS(vrc2) && *var)
7653 vrc2 = RTEnvPutEx(env, var);
7654
7655 AssertRCBreakStmt(vrc2, vrc = vrc2);
7656 }
7657 while (0);
7658
7659 if (newEnvStr != NULL)
7660 RTStrFree(newEnvStr);
7661 }
7662
7663 /* Hardening logging */
7664#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7665 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7666 {
7667 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7668 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7669 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7670 {
7671 Utf8Str strStartupLogDir = strHardeningLogFile;
7672 strStartupLogDir.stripFilename();
7673 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7674 file without stripping the file. */
7675 }
7676 strSupHardeningLogArg.append(strHardeningLogFile);
7677
7678 /* Remove legacy log filename to avoid confusion. */
7679 Utf8Str strOldStartupLogFile;
7680 getLogFolder(strOldStartupLogFile);
7681 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7682 RTFileDelete(strOldStartupLogFile.c_str());
7683 }
7684 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7685#else
7686 const char *pszSupHardeningLogArg = NULL;
7687#endif
7688
7689 Utf8Str strCanonicalName;
7690
7691#ifdef VBOX_WITH_QTGUI
7692 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7693 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7694 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7695 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7696 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7697 {
7698 strCanonicalName = "GUI/Qt";
7699# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7700 /* Modify the base path so that we don't need to use ".." below. */
7701 RTPathStripTrailingSlash(szPath);
7702 RTPathStripFilename(szPath);
7703 cchBufLeft = strlen(szPath);
7704 pszNamePart = szPath + cchBufLeft;
7705 cchBufLeft = sizeof(szPath) - cchBufLeft;
7706
7707# define OSX_APP_NAME "VirtualBoxVM"
7708# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7709
7710 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7711 if ( strAppOverride.contains(".")
7712 || strAppOverride.contains("/")
7713 || strAppOverride.contains("\\")
7714 || strAppOverride.contains(":"))
7715 strAppOverride.setNull();
7716 Utf8Str strAppPath;
7717 if (!strAppOverride.isEmpty())
7718 {
7719 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7720 Utf8Str strFullPath(szPath);
7721 strFullPath.append(strAppPath);
7722 /* there is a race, but people using this deserve the failure */
7723 if (!RTFileExists(strFullPath.c_str()))
7724 strAppOverride.setNull();
7725 }
7726 if (strAppOverride.isEmpty())
7727 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7728 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7729 strcpy(pszNamePart, strAppPath.c_str());
7730# else
7731 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7732 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7733 strcpy(pszNamePart, s_szVirtualBox_exe);
7734# endif
7735
7736 Utf8Str idStr = mData->mUuid.toString();
7737 const char *apszArgs[] =
7738 {
7739 szPath,
7740 "--comment", mUserData->s.strName.c_str(),
7741 "--startvm", idStr.c_str(),
7742 "--no-startvm-errormsgbox",
7743 NULL, /* For "--separate". */
7744 NULL, /* For "--sup-startup-log". */
7745 NULL
7746 };
7747 unsigned iArg = 6;
7748 if (fSeparate)
7749 apszArgs[iArg++] = "--separate";
7750 apszArgs[iArg++] = pszSupHardeningLogArg;
7751
7752 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7753 }
7754#else /* !VBOX_WITH_QTGUI */
7755 if (0)
7756 ;
7757#endif /* VBOX_WITH_QTGUI */
7758
7759 else
7760
7761#ifdef VBOX_WITH_VBOXSDL
7762 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7763 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7764 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7765 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7766 {
7767 strCanonicalName = "GUI/SDL";
7768 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7769 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7770 strcpy(pszNamePart, s_szVBoxSDL_exe);
7771
7772 Utf8Str idStr = mData->mUuid.toString();
7773 const char *apszArgs[] =
7774 {
7775 szPath,
7776 "--comment", mUserData->s.strName.c_str(),
7777 "--startvm", idStr.c_str(),
7778 NULL, /* For "--separate". */
7779 NULL, /* For "--sup-startup-log". */
7780 NULL
7781 };
7782 unsigned iArg = 5;
7783 if (fSeparate)
7784 apszArgs[iArg++] = "--separate";
7785 apszArgs[iArg++] = pszSupHardeningLogArg;
7786
7787 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7788 }
7789#else /* !VBOX_WITH_VBOXSDL */
7790 if (0)
7791 ;
7792#endif /* !VBOX_WITH_VBOXSDL */
7793
7794 else
7795
7796#ifdef VBOX_WITH_HEADLESS
7797 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7798 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7799 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7800 )
7801 {
7802 strCanonicalName = "headless";
7803 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7804 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7805 * and a VM works even if the server has not been installed.
7806 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7807 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7808 * differently in 4.0 and 3.x.
7809 */
7810 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7811 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7812 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7813
7814 Utf8Str idStr = mData->mUuid.toString();
7815 const char *apszArgs[] =
7816 {
7817 szPath,
7818 "--comment", mUserData->s.strName.c_str(),
7819 "--startvm", idStr.c_str(),
7820 "--vrde", "config",
7821 NULL, /* For "--capture". */
7822 NULL, /* For "--sup-startup-log". */
7823 NULL
7824 };
7825 unsigned iArg = 7;
7826 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7827 apszArgs[iArg++] = "--capture";
7828 apszArgs[iArg++] = pszSupHardeningLogArg;
7829
7830# ifdef RT_OS_WINDOWS
7831 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7832# else
7833 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7834# endif
7835 }
7836#else /* !VBOX_WITH_HEADLESS */
7837 if (0)
7838 ;
7839#endif /* !VBOX_WITH_HEADLESS */
7840 else
7841 {
7842 RTEnvDestroy(env);
7843 return setError(E_INVALIDARG,
7844 tr("Invalid frontend name: '%s'"),
7845 strFrontend.c_str());
7846 }
7847
7848 RTEnvDestroy(env);
7849
7850 if (RT_FAILURE(vrc))
7851 return setError(VBOX_E_IPRT_ERROR,
7852 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7853 mUserData->s.strName.c_str(), vrc);
7854
7855 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7856
7857 if (!fSeparate)
7858 {
7859 /*
7860 * Note that we don't release the lock here before calling the client,
7861 * because it doesn't need to call us back if called with a NULL argument.
7862 * Releasing the lock here is dangerous because we didn't prepare the
7863 * launch data yet, but the client we've just started may happen to be
7864 * too fast and call LockMachine() that will fail (because of PID, etc.),
7865 * so that the Machine will never get out of the Spawning session state.
7866 */
7867
7868 /* inform the session that it will be a remote one */
7869 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7870#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7871 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7872#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7873 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7874#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7875 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7876
7877 if (FAILED(rc))
7878 {
7879 /* restore the session state */
7880 mData->mSession.mState = SessionState_Unlocked;
7881 alock.release();
7882 mParent->i_addProcessToReap(pid);
7883 /* The failure may occur w/o any error info (from RPC), so provide one */
7884 return setError(VBOX_E_VM_ERROR,
7885 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7886 }
7887
7888 /* attach launch data to the machine */
7889 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7890 mData->mSession.mRemoteControls.push_back(aControl);
7891 mData->mSession.mProgress = aProgress;
7892 mData->mSession.mPID = pid;
7893 mData->mSession.mState = SessionState_Spawning;
7894 Assert(strCanonicalName.isNotEmpty());
7895 mData->mSession.mName = strCanonicalName;
7896 }
7897 else
7898 {
7899 /* For separate UI process we declare the launch as completed instantly, as the
7900 * actual headless VM start may or may not come. No point in remembering anything
7901 * yet, as what matters for us is when the headless VM gets started. */
7902 aProgress->i_notifyComplete(S_OK);
7903 }
7904
7905 alock.release();
7906 mParent->i_addProcessToReap(pid);
7907
7908 LogFlowThisFuncLeave();
7909 return S_OK;
7910}
7911
7912/**
7913 * Returns @c true if the given session machine instance has an open direct
7914 * session (and optionally also for direct sessions which are closing) and
7915 * returns the session control machine instance if so.
7916 *
7917 * Note that when the method returns @c false, the arguments remain unchanged.
7918 *
7919 * @param aMachine Session machine object.
7920 * @param aControl Direct session control object (optional).
7921 * @param aRequireVM If true then only allow VM sessions.
7922 * @param aAllowClosing If true then additionally a session which is currently
7923 * being closed will also be allowed.
7924 *
7925 * @note locks this object for reading.
7926 */
7927bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7928 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7929 bool aRequireVM /*= false*/,
7930 bool aAllowClosing /*= false*/)
7931{
7932 AutoLimitedCaller autoCaller(this);
7933 AssertComRCReturn(autoCaller.rc(), false);
7934
7935 /* just return false for inaccessible machines */
7936 if (getObjectState().getState() != ObjectState::Ready)
7937 return false;
7938
7939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7940
7941 if ( ( mData->mSession.mState == SessionState_Locked
7942 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7943 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7944 )
7945 {
7946 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7947
7948 aMachine = mData->mSession.mMachine;
7949
7950 if (aControl != NULL)
7951 *aControl = mData->mSession.mDirectControl;
7952
7953 return true;
7954 }
7955
7956 return false;
7957}
7958
7959/**
7960 * Returns @c true if the given machine has an spawning direct session.
7961 *
7962 * @note locks this object for reading.
7963 */
7964bool Machine::i_isSessionSpawning()
7965{
7966 AutoLimitedCaller autoCaller(this);
7967 AssertComRCReturn(autoCaller.rc(), false);
7968
7969 /* just return false for inaccessible machines */
7970 if (getObjectState().getState() != ObjectState::Ready)
7971 return false;
7972
7973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7974
7975 if (mData->mSession.mState == SessionState_Spawning)
7976 return true;
7977
7978 return false;
7979}
7980
7981/**
7982 * Called from the client watcher thread to check for unexpected client process
7983 * death during Session_Spawning state (e.g. before it successfully opened a
7984 * direct session).
7985 *
7986 * On Win32 and on OS/2, this method is called only when we've got the
7987 * direct client's process termination notification, so it always returns @c
7988 * true.
7989 *
7990 * On other platforms, this method returns @c true if the client process is
7991 * terminated and @c false if it's still alive.
7992 *
7993 * @note Locks this object for writing.
7994 */
7995bool Machine::i_checkForSpawnFailure()
7996{
7997 AutoCaller autoCaller(this);
7998 if (!autoCaller.isOk())
7999 {
8000 /* nothing to do */
8001 LogFlowThisFunc(("Already uninitialized!\n"));
8002 return true;
8003 }
8004
8005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8006
8007 if (mData->mSession.mState != SessionState_Spawning)
8008 {
8009 /* nothing to do */
8010 LogFlowThisFunc(("Not spawning any more!\n"));
8011 return true;
8012 }
8013
8014 HRESULT rc = S_OK;
8015
8016 /* PID not yet initialized, skip check. */
8017 if (mData->mSession.mPID == NIL_RTPROCESS)
8018 return false;
8019
8020 RTPROCSTATUS status;
8021 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8022
8023 if (vrc != VERR_PROCESS_RUNNING)
8024 {
8025 Utf8Str strExtraInfo;
8026
8027#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8028 /* If the startup logfile exists and is of non-zero length, tell the
8029 user to look there for more details to encourage them to attach it
8030 when reporting startup issues. */
8031 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8032 uint64_t cbStartupLogFile = 0;
8033 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8034 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8035 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8036#endif
8037
8038 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8039 rc = setError(E_FAIL,
8040 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8041 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8042 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8043 rc = setError(E_FAIL,
8044 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8045 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8046 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8047 rc = setError(E_FAIL,
8048 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8049 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8050 else
8051 rc = setError(E_FAIL,
8052 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8053 i_getName().c_str(), vrc, strExtraInfo.c_str());
8054 }
8055
8056 if (FAILED(rc))
8057 {
8058 /* Close the remote session, remove the remote control from the list
8059 * and reset session state to Closed (@note keep the code in sync with
8060 * the relevant part in LockMachine()). */
8061
8062 Assert(mData->mSession.mRemoteControls.size() == 1);
8063 if (mData->mSession.mRemoteControls.size() == 1)
8064 {
8065 ErrorInfoKeeper eik;
8066 mData->mSession.mRemoteControls.front()->Uninitialize();
8067 }
8068
8069 mData->mSession.mRemoteControls.clear();
8070 mData->mSession.mState = SessionState_Unlocked;
8071
8072 /* finalize the progress after setting the state */
8073 if (!mData->mSession.mProgress.isNull())
8074 {
8075 mData->mSession.mProgress->notifyComplete(rc);
8076 mData->mSession.mProgress.setNull();
8077 }
8078
8079 mData->mSession.mPID = NIL_RTPROCESS;
8080
8081 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8082 return true;
8083 }
8084
8085 return false;
8086}
8087
8088/**
8089 * Checks whether the machine can be registered. If so, commits and saves
8090 * all settings.
8091 *
8092 * @note Must be called from mParent's write lock. Locks this object and
8093 * children for writing.
8094 */
8095HRESULT Machine::i_prepareRegister()
8096{
8097 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8098
8099 AutoLimitedCaller autoCaller(this);
8100 AssertComRCReturnRC(autoCaller.rc());
8101
8102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8103
8104 /* wait for state dependents to drop to zero */
8105 i_ensureNoStateDependencies();
8106
8107 if (!mData->mAccessible)
8108 return setError(VBOX_E_INVALID_OBJECT_STATE,
8109 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8110 mUserData->s.strName.c_str(),
8111 mData->mUuid.toString().c_str());
8112
8113 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8114
8115 if (mData->mRegistered)
8116 return setError(VBOX_E_INVALID_OBJECT_STATE,
8117 tr("The machine '%s' with UUID {%s} is already registered"),
8118 mUserData->s.strName.c_str(),
8119 mData->mUuid.toString().c_str());
8120
8121 HRESULT rc = S_OK;
8122
8123 // Ensure the settings are saved. If we are going to be registered and
8124 // no config file exists yet, create it by calling i_saveSettings() too.
8125 if ( (mData->flModifications)
8126 || (!mData->pMachineConfigFile->fileExists())
8127 )
8128 {
8129 rc = i_saveSettings(NULL);
8130 // no need to check whether VirtualBox.xml needs saving too since
8131 // we can't have a machine XML file rename pending
8132 if (FAILED(rc)) return rc;
8133 }
8134
8135 /* more config checking goes here */
8136
8137 if (SUCCEEDED(rc))
8138 {
8139 /* we may have had implicit modifications we want to fix on success */
8140 i_commit();
8141
8142 mData->mRegistered = true;
8143 }
8144 else
8145 {
8146 /* we may have had implicit modifications we want to cancel on failure*/
8147 i_rollback(false /* aNotify */);
8148 }
8149
8150 return rc;
8151}
8152
8153/**
8154 * Increases the number of objects dependent on the machine state or on the
8155 * registered state. Guarantees that these two states will not change at least
8156 * until #i_releaseStateDependency() is called.
8157 *
8158 * Depending on the @a aDepType value, additional state checks may be made.
8159 * These checks will set extended error info on failure. See
8160 * #i_checkStateDependency() for more info.
8161 *
8162 * If this method returns a failure, the dependency is not added and the caller
8163 * is not allowed to rely on any particular machine state or registration state
8164 * value and may return the failed result code to the upper level.
8165 *
8166 * @param aDepType Dependency type to add.
8167 * @param aState Current machine state (NULL if not interested).
8168 * @param aRegistered Current registered state (NULL if not interested).
8169 *
8170 * @note Locks this object for writing.
8171 */
8172HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8173 MachineState_T *aState /* = NULL */,
8174 BOOL *aRegistered /* = NULL */)
8175{
8176 AutoCaller autoCaller(this);
8177 AssertComRCReturnRC(autoCaller.rc());
8178
8179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8180
8181 HRESULT rc = i_checkStateDependency(aDepType);
8182 if (FAILED(rc)) return rc;
8183
8184 {
8185 if (mData->mMachineStateChangePending != 0)
8186 {
8187 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8188 * drop to zero so don't add more. It may make sense to wait a bit
8189 * and retry before reporting an error (since the pending state
8190 * transition should be really quick) but let's just assert for
8191 * now to see if it ever happens on practice. */
8192
8193 AssertFailed();
8194
8195 return setError(E_ACCESSDENIED,
8196 tr("Machine state change is in progress. Please retry the operation later."));
8197 }
8198
8199 ++mData->mMachineStateDeps;
8200 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8201 }
8202
8203 if (aState)
8204 *aState = mData->mMachineState;
8205 if (aRegistered)
8206 *aRegistered = mData->mRegistered;
8207
8208 return S_OK;
8209}
8210
8211/**
8212 * Decreases the number of objects dependent on the machine state.
8213 * Must always complete the #i_addStateDependency() call after the state
8214 * dependency is no more necessary.
8215 */
8216void Machine::i_releaseStateDependency()
8217{
8218 AutoCaller autoCaller(this);
8219 AssertComRCReturnVoid(autoCaller.rc());
8220
8221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8222
8223 /* releaseStateDependency() w/o addStateDependency()? */
8224 AssertReturnVoid(mData->mMachineStateDeps != 0);
8225 -- mData->mMachineStateDeps;
8226
8227 if (mData->mMachineStateDeps == 0)
8228 {
8229 /* inform i_ensureNoStateDependencies() that there are no more deps */
8230 if (mData->mMachineStateChangePending != 0)
8231 {
8232 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8233 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8234 }
8235 }
8236}
8237
8238Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8239{
8240 /* start with nothing found */
8241 Utf8Str strResult("");
8242
8243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8244
8245 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8246 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8247 // found:
8248 strResult = it->second; // source is a Utf8Str
8249
8250 return strResult;
8251}
8252
8253// protected methods
8254/////////////////////////////////////////////////////////////////////////////
8255
8256/**
8257 * Performs machine state checks based on the @a aDepType value. If a check
8258 * fails, this method will set extended error info, otherwise it will return
8259 * S_OK. It is supposed, that on failure, the caller will immediately return
8260 * the return value of this method to the upper level.
8261 *
8262 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8263 *
8264 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8265 * current state of this machine object allows to change settings of the
8266 * machine (i.e. the machine is not registered, or registered but not running
8267 * and not saved). It is useful to call this method from Machine setters
8268 * before performing any change.
8269 *
8270 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8271 * as for MutableStateDep except that if the machine is saved, S_OK is also
8272 * returned. This is useful in setters which allow changing machine
8273 * properties when it is in the saved state.
8274 *
8275 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8276 * if the current state of this machine object allows to change runtime
8277 * changeable settings of the machine (i.e. the machine is not registered, or
8278 * registered but either running or not running and not saved). It is useful
8279 * to call this method from Machine setters before performing any changes to
8280 * runtime changeable settings.
8281 *
8282 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8283 * the same as for MutableOrRunningStateDep except that if the machine is
8284 * saved, S_OK is also returned. This is useful in setters which allow
8285 * changing runtime and saved state changeable machine properties.
8286 *
8287 * @param aDepType Dependency type to check.
8288 *
8289 * @note Non Machine based classes should use #i_addStateDependency() and
8290 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8291 * template.
8292 *
8293 * @note This method must be called from under this object's read or write
8294 * lock.
8295 */
8296HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8297{
8298 switch (aDepType)
8299 {
8300 case AnyStateDep:
8301 {
8302 break;
8303 }
8304 case MutableStateDep:
8305 {
8306 if ( mData->mRegistered
8307 && ( !i_isSessionMachine()
8308 || ( mData->mMachineState != MachineState_Aborted
8309 && mData->mMachineState != MachineState_Teleported
8310 && mData->mMachineState != MachineState_PoweredOff
8311 )
8312 )
8313 )
8314 return setError(VBOX_E_INVALID_VM_STATE,
8315 tr("The machine is not mutable (state is %s)"),
8316 Global::stringifyMachineState(mData->mMachineState));
8317 break;
8318 }
8319 case MutableOrSavedStateDep:
8320 {
8321 if ( mData->mRegistered
8322 && ( !i_isSessionMachine()
8323 || ( mData->mMachineState != MachineState_Aborted
8324 && mData->mMachineState != MachineState_Teleported
8325 && mData->mMachineState != MachineState_Saved
8326 && mData->mMachineState != MachineState_PoweredOff
8327 )
8328 )
8329 )
8330 return setError(VBOX_E_INVALID_VM_STATE,
8331 tr("The machine is not mutable or saved (state is %s)"),
8332 Global::stringifyMachineState(mData->mMachineState));
8333 break;
8334 }
8335 case MutableOrRunningStateDep:
8336 {
8337 if ( mData->mRegistered
8338 && ( !i_isSessionMachine()
8339 || ( mData->mMachineState != MachineState_Aborted
8340 && mData->mMachineState != MachineState_Teleported
8341 && mData->mMachineState != MachineState_PoweredOff
8342 && !Global::IsOnline(mData->mMachineState)
8343 )
8344 )
8345 )
8346 return setError(VBOX_E_INVALID_VM_STATE,
8347 tr("The machine is not mutable or running (state is %s)"),
8348 Global::stringifyMachineState(mData->mMachineState));
8349 break;
8350 }
8351 case MutableOrSavedOrRunningStateDep:
8352 {
8353 if ( mData->mRegistered
8354 && ( !i_isSessionMachine()
8355 || ( mData->mMachineState != MachineState_Aborted
8356 && mData->mMachineState != MachineState_Teleported
8357 && mData->mMachineState != MachineState_Saved
8358 && mData->mMachineState != MachineState_PoweredOff
8359 && !Global::IsOnline(mData->mMachineState)
8360 )
8361 )
8362 )
8363 return setError(VBOX_E_INVALID_VM_STATE,
8364 tr("The machine is not mutable, saved or running (state is %s)"),
8365 Global::stringifyMachineState(mData->mMachineState));
8366 break;
8367 }
8368 }
8369
8370 return S_OK;
8371}
8372
8373/**
8374 * Helper to initialize all associated child objects and allocate data
8375 * structures.
8376 *
8377 * This method must be called as a part of the object's initialization procedure
8378 * (usually done in the #init() method).
8379 *
8380 * @note Must be called only from #init() or from #i_registeredInit().
8381 */
8382HRESULT Machine::initDataAndChildObjects()
8383{
8384 AutoCaller autoCaller(this);
8385 AssertComRCReturnRC(autoCaller.rc());
8386 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8387 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8388
8389 AssertReturn(!mData->mAccessible, E_FAIL);
8390
8391 /* allocate data structures */
8392 mSSData.allocate();
8393 mUserData.allocate();
8394 mHWData.allocate();
8395 mMediumAttachments.allocate();
8396 mStorageControllers.allocate();
8397 mUSBControllers.allocate();
8398
8399 /* initialize mOSTypeId */
8400 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8401
8402/** @todo r=bird: init() methods never fails, right? Why don't we make them
8403 * return void then! */
8404
8405 /* create associated BIOS settings object */
8406 unconst(mBIOSSettings).createObject();
8407 mBIOSSettings->init(this);
8408
8409 /* create an associated VRDE object (default is disabled) */
8410 unconst(mVRDEServer).createObject();
8411 mVRDEServer->init(this);
8412
8413 /* create associated serial port objects */
8414 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8415 {
8416 unconst(mSerialPorts[slot]).createObject();
8417 mSerialPorts[slot]->init(this, slot);
8418 }
8419
8420 /* create associated parallel port objects */
8421 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8422 {
8423 unconst(mParallelPorts[slot]).createObject();
8424 mParallelPorts[slot]->init(this, slot);
8425 }
8426
8427 /* create the audio adapter object (always present, default is disabled) */
8428 unconst(mAudioAdapter).createObject();
8429 mAudioAdapter->init(this);
8430
8431 /* create the USB device filters object (always present) */
8432 unconst(mUSBDeviceFilters).createObject();
8433 mUSBDeviceFilters->init(this);
8434
8435 /* create associated network adapter objects */
8436 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8437 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8438 {
8439 unconst(mNetworkAdapters[slot]).createObject();
8440 mNetworkAdapters[slot]->init(this, slot);
8441 }
8442
8443 /* create the bandwidth control */
8444 unconst(mBandwidthControl).createObject();
8445 mBandwidthControl->init(this);
8446
8447#ifdef VBOX_WITH_UNATTENDED
8448 Assert(mUnattended.isNull()); /* Created on-demand. */
8449#endif
8450
8451 return S_OK;
8452}
8453
8454/**
8455 * Helper to uninitialize all associated child objects and to free all data
8456 * structures.
8457 *
8458 * This method must be called as a part of the object's uninitialization
8459 * procedure (usually done in the #uninit() method).
8460 *
8461 * @note Must be called only from #uninit() or from #i_registeredInit().
8462 */
8463void Machine::uninitDataAndChildObjects()
8464{
8465 AutoCaller autoCaller(this);
8466 AssertComRCReturnVoid(autoCaller.rc());
8467 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8468 || getObjectState().getState() == ObjectState::Limited);
8469
8470 /* tell all our other child objects we've been uninitialized */
8471 if (mBandwidthControl)
8472 {
8473 mBandwidthControl->uninit();
8474 unconst(mBandwidthControl).setNull();
8475 }
8476
8477 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8478 {
8479 if (mNetworkAdapters[slot])
8480 {
8481 mNetworkAdapters[slot]->uninit();
8482 unconst(mNetworkAdapters[slot]).setNull();
8483 }
8484 }
8485
8486 if (mUSBDeviceFilters)
8487 {
8488 mUSBDeviceFilters->uninit();
8489 unconst(mUSBDeviceFilters).setNull();
8490 }
8491
8492 if (mAudioAdapter)
8493 {
8494 mAudioAdapter->uninit();
8495 unconst(mAudioAdapter).setNull();
8496 }
8497
8498 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8499 {
8500 if (mParallelPorts[slot])
8501 {
8502 mParallelPorts[slot]->uninit();
8503 unconst(mParallelPorts[slot]).setNull();
8504 }
8505 }
8506
8507 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8508 {
8509 if (mSerialPorts[slot])
8510 {
8511 mSerialPorts[slot]->uninit();
8512 unconst(mSerialPorts[slot]).setNull();
8513 }
8514 }
8515
8516 if (mVRDEServer)
8517 {
8518 mVRDEServer->uninit();
8519 unconst(mVRDEServer).setNull();
8520 }
8521
8522 if (mBIOSSettings)
8523 {
8524 mBIOSSettings->uninit();
8525 unconst(mBIOSSettings).setNull();
8526 }
8527
8528#ifdef VBOX_WITH_UNATTENDED
8529 if (mUnattended)
8530 {
8531 mUnattended->uninit();
8532 unconst(mUnattended).setNull();
8533 }
8534#endif
8535
8536 /* Deassociate media (only when a real Machine or a SnapshotMachine
8537 * instance is uninitialized; SessionMachine instances refer to real
8538 * Machine media). This is necessary for a clean re-initialization of
8539 * the VM after successfully re-checking the accessibility state. Note
8540 * that in case of normal Machine or SnapshotMachine uninitialization (as
8541 * a result of unregistering or deleting the snapshot), outdated media
8542 * attachments will already be uninitialized and deleted, so this
8543 * code will not affect them. */
8544 if ( !mMediumAttachments.isNull()
8545 && !i_isSessionMachine()
8546 )
8547 {
8548 for (MediumAttachmentList::const_iterator
8549 it = mMediumAttachments->begin();
8550 it != mMediumAttachments->end();
8551 ++it)
8552 {
8553 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8554 if (pMedium.isNull())
8555 continue;
8556 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8557 AssertComRC(rc);
8558 }
8559 }
8560
8561 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8562 {
8563 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8564 if (mData->mFirstSnapshot)
8565 {
8566 // snapshots tree is protected by machine write lock; strictly
8567 // this isn't necessary here since we're deleting the entire
8568 // machine, but otherwise we assert in Snapshot::uninit()
8569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8570 mData->mFirstSnapshot->uninit();
8571 mData->mFirstSnapshot.setNull();
8572 }
8573
8574 mData->mCurrentSnapshot.setNull();
8575 }
8576
8577 /* free data structures (the essential mData structure is not freed here
8578 * since it may be still in use) */
8579 mMediumAttachments.free();
8580 mStorageControllers.free();
8581 mUSBControllers.free();
8582 mHWData.free();
8583 mUserData.free();
8584 mSSData.free();
8585}
8586
8587/**
8588 * Returns a pointer to the Machine object for this machine that acts like a
8589 * parent for complex machine data objects such as shared folders, etc.
8590 *
8591 * For primary Machine objects and for SnapshotMachine objects, returns this
8592 * object's pointer itself. For SessionMachine objects, returns the peer
8593 * (primary) machine pointer.
8594 */
8595Machine *Machine::i_getMachine()
8596{
8597 if (i_isSessionMachine())
8598 return (Machine*)mPeer;
8599 return this;
8600}
8601
8602/**
8603 * Makes sure that there are no machine state dependents. If necessary, waits
8604 * for the number of dependents to drop to zero.
8605 *
8606 * Make sure this method is called from under this object's write lock to
8607 * guarantee that no new dependents may be added when this method returns
8608 * control to the caller.
8609 *
8610 * @note Locks this object for writing. The lock will be released while waiting
8611 * (if necessary).
8612 *
8613 * @warning To be used only in methods that change the machine state!
8614 */
8615void Machine::i_ensureNoStateDependencies()
8616{
8617 AssertReturnVoid(isWriteLockOnCurrentThread());
8618
8619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8620
8621 /* Wait for all state dependents if necessary */
8622 if (mData->mMachineStateDeps != 0)
8623 {
8624 /* lazy semaphore creation */
8625 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8626 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8627
8628 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8629 mData->mMachineStateDeps));
8630
8631 ++mData->mMachineStateChangePending;
8632
8633 /* reset the semaphore before waiting, the last dependent will signal
8634 * it */
8635 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8636
8637 alock.release();
8638
8639 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8640
8641 alock.acquire();
8642
8643 -- mData->mMachineStateChangePending;
8644 }
8645}
8646
8647/**
8648 * Changes the machine state and informs callbacks.
8649 *
8650 * This method is not intended to fail so it either returns S_OK or asserts (and
8651 * returns a failure).
8652 *
8653 * @note Locks this object for writing.
8654 */
8655HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8656{
8657 LogFlowThisFuncEnter();
8658 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8659 Assert(aMachineState != MachineState_Null);
8660
8661 AutoCaller autoCaller(this);
8662 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8663
8664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8665
8666 /* wait for state dependents to drop to zero */
8667 i_ensureNoStateDependencies();
8668
8669 MachineState_T const enmOldState = mData->mMachineState;
8670 if (enmOldState != aMachineState)
8671 {
8672 mData->mMachineState = aMachineState;
8673 RTTimeNow(&mData->mLastStateChange);
8674
8675#ifdef VBOX_WITH_DTRACE_R3_MAIN
8676 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8677#endif
8678 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8679 }
8680
8681 LogFlowThisFuncLeave();
8682 return S_OK;
8683}
8684
8685/**
8686 * Searches for a shared folder with the given logical name
8687 * in the collection of shared folders.
8688 *
8689 * @param aName logical name of the shared folder
8690 * @param aSharedFolder where to return the found object
8691 * @param aSetError whether to set the error info if the folder is
8692 * not found
8693 * @return
8694 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8695 *
8696 * @note
8697 * must be called from under the object's lock!
8698 */
8699HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8700 ComObjPtr<SharedFolder> &aSharedFolder,
8701 bool aSetError /* = false */)
8702{
8703 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8704 for (HWData::SharedFolderList::const_iterator
8705 it = mHWData->mSharedFolders.begin();
8706 it != mHWData->mSharedFolders.end();
8707 ++it)
8708 {
8709 SharedFolder *pSF = *it;
8710 AutoCaller autoCaller(pSF);
8711 if (pSF->i_getName() == aName)
8712 {
8713 aSharedFolder = pSF;
8714 rc = S_OK;
8715 break;
8716 }
8717 }
8718
8719 if (aSetError && FAILED(rc))
8720 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8721
8722 return rc;
8723}
8724
8725/**
8726 * Initializes all machine instance data from the given settings structures
8727 * from XML. The exception is the machine UUID which needs special handling
8728 * depending on the caller's use case, so the caller needs to set that herself.
8729 *
8730 * This gets called in several contexts during machine initialization:
8731 *
8732 * -- When machine XML exists on disk already and needs to be loaded into memory,
8733 * for example, from #i_registeredInit() to load all registered machines on
8734 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8735 * attached to the machine should be part of some media registry already.
8736 *
8737 * -- During OVF import, when a machine config has been constructed from an
8738 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8739 * ensure that the media listed as attachments in the config (which have
8740 * been imported from the OVF) receive the correct registry ID.
8741 *
8742 * -- During VM cloning.
8743 *
8744 * @param config Machine settings from XML.
8745 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8746 * for each attached medium in the config.
8747 * @return
8748 */
8749HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8750 const Guid *puuidRegistry)
8751{
8752 // copy name, description, OS type, teleporter, UTC etc.
8753 mUserData->s = config.machineUserData;
8754
8755 // look up the object by Id to check it is valid
8756 ComObjPtr<GuestOSType> pGuestOSType;
8757 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8758 pGuestOSType);
8759 if (FAILED(rc)) return rc;
8760 mUserData->s.strOsType = pGuestOSType->i_id();
8761
8762 // stateFile (optional)
8763 if (config.strStateFile.isEmpty())
8764 mSSData->strStateFilePath.setNull();
8765 else
8766 {
8767 Utf8Str stateFilePathFull(config.strStateFile);
8768 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8769 if (RT_FAILURE(vrc))
8770 return setError(E_FAIL,
8771 tr("Invalid saved state file path '%s' (%Rrc)"),
8772 config.strStateFile.c_str(),
8773 vrc);
8774 mSSData->strStateFilePath = stateFilePathFull;
8775 }
8776
8777 // snapshot folder needs special processing so set it again
8778 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8779 if (FAILED(rc)) return rc;
8780
8781 /* Copy the extra data items (config may or may not be the same as
8782 * mData->pMachineConfigFile) if necessary. When loading the XML files
8783 * from disk they are the same, but not for OVF import. */
8784 if (mData->pMachineConfigFile != &config)
8785 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8786
8787 /* currentStateModified (optional, default is true) */
8788 mData->mCurrentStateModified = config.fCurrentStateModified;
8789
8790 mData->mLastStateChange = config.timeLastStateChange;
8791
8792 /*
8793 * note: all mUserData members must be assigned prior this point because
8794 * we need to commit changes in order to let mUserData be shared by all
8795 * snapshot machine instances.
8796 */
8797 mUserData.commitCopy();
8798
8799 // machine registry, if present (must be loaded before snapshots)
8800 if (config.canHaveOwnMediaRegistry())
8801 {
8802 // determine machine folder
8803 Utf8Str strMachineFolder = i_getSettingsFileFull();
8804 strMachineFolder.stripFilename();
8805 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8806 config.mediaRegistry,
8807 strMachineFolder);
8808 if (FAILED(rc)) return rc;
8809 }
8810
8811 /* Snapshot node (optional) */
8812 size_t cRootSnapshots;
8813 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8814 {
8815 // there must be only one root snapshot
8816 Assert(cRootSnapshots == 1);
8817
8818 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8819
8820 rc = i_loadSnapshot(snap,
8821 config.uuidCurrentSnapshot,
8822 NULL); // no parent == first snapshot
8823 if (FAILED(rc)) return rc;
8824 }
8825
8826 // hardware data
8827 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8828 if (FAILED(rc)) return rc;
8829
8830 /*
8831 * NOTE: the assignment below must be the last thing to do,
8832 * otherwise it will be not possible to change the settings
8833 * somewhere in the code above because all setters will be
8834 * blocked by i_checkStateDependency(MutableStateDep).
8835 */
8836
8837 /* set the machine state to Aborted or Saved when appropriate */
8838 if (config.fAborted)
8839 {
8840 mSSData->strStateFilePath.setNull();
8841
8842 /* no need to use i_setMachineState() during init() */
8843 mData->mMachineState = MachineState_Aborted;
8844 }
8845 else if (!mSSData->strStateFilePath.isEmpty())
8846 {
8847 /* no need to use i_setMachineState() during init() */
8848 mData->mMachineState = MachineState_Saved;
8849 }
8850
8851 // after loading settings, we are no longer different from the XML on disk
8852 mData->flModifications = 0;
8853
8854 return S_OK;
8855}
8856
8857/**
8858 * Recursively loads all snapshots starting from the given.
8859 *
8860 * @param data snapshot settings.
8861 * @param aCurSnapshotId Current snapshot ID from the settings file.
8862 * @param aParentSnapshot Parent snapshot.
8863 */
8864HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8865 const Guid &aCurSnapshotId,
8866 Snapshot *aParentSnapshot)
8867{
8868 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8869 AssertReturn(!i_isSessionMachine(), E_FAIL);
8870
8871 HRESULT rc = S_OK;
8872
8873 Utf8Str strStateFile;
8874 if (!data.strStateFile.isEmpty())
8875 {
8876 /* optional */
8877 strStateFile = data.strStateFile;
8878 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8879 if (RT_FAILURE(vrc))
8880 return setError(E_FAIL,
8881 tr("Invalid saved state file path '%s' (%Rrc)"),
8882 strStateFile.c_str(),
8883 vrc);
8884 }
8885
8886 /* create a snapshot machine object */
8887 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8888 pSnapshotMachine.createObject();
8889 rc = pSnapshotMachine->initFromSettings(this,
8890 data.hardware,
8891 &data.debugging,
8892 &data.autostart,
8893 data.uuid.ref(),
8894 strStateFile);
8895 if (FAILED(rc)) return rc;
8896
8897 /* create a snapshot object */
8898 ComObjPtr<Snapshot> pSnapshot;
8899 pSnapshot.createObject();
8900 /* initialize the snapshot */
8901 rc = pSnapshot->init(mParent, // VirtualBox object
8902 data.uuid,
8903 data.strName,
8904 data.strDescription,
8905 data.timestamp,
8906 pSnapshotMachine,
8907 aParentSnapshot);
8908 if (FAILED(rc)) return rc;
8909
8910 /* memorize the first snapshot if necessary */
8911 if (!mData->mFirstSnapshot)
8912 mData->mFirstSnapshot = pSnapshot;
8913
8914 /* memorize the current snapshot when appropriate */
8915 if ( !mData->mCurrentSnapshot
8916 && pSnapshot->i_getId() == aCurSnapshotId
8917 )
8918 mData->mCurrentSnapshot = pSnapshot;
8919
8920 // now create the children
8921 for (settings::SnapshotsList::const_iterator
8922 it = data.llChildSnapshots.begin();
8923 it != data.llChildSnapshots.end();
8924 ++it)
8925 {
8926 const settings::Snapshot &childData = *it;
8927 // recurse
8928 rc = i_loadSnapshot(childData,
8929 aCurSnapshotId,
8930 pSnapshot); // parent = the one we created above
8931 if (FAILED(rc)) return rc;
8932 }
8933
8934 return rc;
8935}
8936
8937/**
8938 * Loads settings into mHWData.
8939 *
8940 * @param puuidRegistry Registry ID.
8941 * @param puuidSnapshot Snapshot ID
8942 * @param data Reference to the hardware settings.
8943 * @param pDbg Pointer to the debugging settings.
8944 * @param pAutostart Pointer to the autostart settings.
8945 */
8946HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8947 const Guid *puuidSnapshot,
8948 const settings::Hardware &data,
8949 const settings::Debugging *pDbg,
8950 const settings::Autostart *pAutostart)
8951{
8952 AssertReturn(!i_isSessionMachine(), E_FAIL);
8953
8954 HRESULT rc = S_OK;
8955
8956 try
8957 {
8958 ComObjPtr<GuestOSType> pGuestOSType;
8959 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8960 pGuestOSType);
8961 if (FAILED(rc))
8962 return rc;
8963
8964 /* The hardware version attribute (optional). */
8965 mHWData->mHWVersion = data.strVersion;
8966 mHWData->mHardwareUUID = data.uuid;
8967
8968 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8969 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8970 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8971 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8972 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8973 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8974 mHWData->mPAEEnabled = data.fPAE;
8975 mHWData->mLongMode = data.enmLongMode;
8976 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8977 mHWData->mAPIC = data.fAPIC;
8978 mHWData->mX2APIC = data.fX2APIC;
8979 mHWData->mCPUCount = data.cCPUs;
8980 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8981 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8982 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8983 mHWData->mCpuProfile = data.strCpuProfile;
8984
8985 // cpu
8986 if (mHWData->mCPUHotPlugEnabled)
8987 {
8988 for (settings::CpuList::const_iterator
8989 it = data.llCpus.begin();
8990 it != data.llCpus.end();
8991 ++it)
8992 {
8993 const settings::Cpu &cpu = *it;
8994
8995 mHWData->mCPUAttached[cpu.ulId] = true;
8996 }
8997 }
8998
8999 // cpuid leafs
9000 for (settings::CpuIdLeafsList::const_iterator
9001 it = data.llCpuIdLeafs.begin();
9002 it != data.llCpuIdLeafs.end();
9003 ++it)
9004 {
9005 const settings::CpuIdLeaf &leaf = *it;
9006
9007 switch (leaf.ulId)
9008 {
9009 case 0x0:
9010 case 0x1:
9011 case 0x2:
9012 case 0x3:
9013 case 0x4:
9014 case 0x5:
9015 case 0x6:
9016 case 0x7:
9017 case 0x8:
9018 case 0x9:
9019 case 0xA:
9020 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
9021 break;
9022
9023 case 0x80000000:
9024 case 0x80000001:
9025 case 0x80000002:
9026 case 0x80000003:
9027 case 0x80000004:
9028 case 0x80000005:
9029 case 0x80000006:
9030 case 0x80000007:
9031 case 0x80000008:
9032 case 0x80000009:
9033 case 0x8000000A:
9034 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
9035 break;
9036
9037 default:
9038 /* just ignore */
9039 break;
9040 }
9041 }
9042
9043 mHWData->mMemorySize = data.ulMemorySizeMB;
9044 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9045
9046 // boot order
9047 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9048 {
9049 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9050 if (it == data.mapBootOrder.end())
9051 mHWData->mBootOrder[i] = DeviceType_Null;
9052 else
9053 mHWData->mBootOrder[i] = it->second;
9054 }
9055
9056 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9057 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9058 mHWData->mMonitorCount = data.cMonitors;
9059 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9060 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9061 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9062 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9063 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9064 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9065 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9066 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9067 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9068 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9069 if (!data.strVideoCaptureFile.isEmpty())
9070 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9071 else
9072 mHWData->mVideoCaptureFile.setNull();
9073 mHWData->mFirmwareType = data.firmwareType;
9074 mHWData->mPointingHIDType = data.pointingHIDType;
9075 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9076 mHWData->mChipsetType = data.chipsetType;
9077 mHWData->mParavirtProvider = data.paravirtProvider;
9078 mHWData->mParavirtDebug = data.strParavirtDebug;
9079 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9080 mHWData->mHPETEnabled = data.fHPETEnabled;
9081
9082 /* VRDEServer */
9083 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9084 if (FAILED(rc)) return rc;
9085
9086 /* BIOS */
9087 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9088 if (FAILED(rc)) return rc;
9089
9090 // Bandwidth control (must come before network adapters)
9091 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9092 if (FAILED(rc)) return rc;
9093
9094 /* Shared folders */
9095 for (settings::USBControllerList::const_iterator
9096 it = data.usbSettings.llUSBControllers.begin();
9097 it != data.usbSettings.llUSBControllers.end();
9098 ++it)
9099 {
9100 const settings::USBController &settingsCtrl = *it;
9101 ComObjPtr<USBController> newCtrl;
9102
9103 newCtrl.createObject();
9104 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9105 mUSBControllers->push_back(newCtrl);
9106 }
9107
9108 /* USB device filters */
9109 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9110 if (FAILED(rc)) return rc;
9111
9112 // network adapters (establish array size first and apply defaults, to
9113 // ensure reading the same settings as we saved, since the list skips
9114 // adapters having defaults)
9115 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9116 size_t oldCount = mNetworkAdapters.size();
9117 if (newCount > oldCount)
9118 {
9119 mNetworkAdapters.resize(newCount);
9120 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9121 {
9122 unconst(mNetworkAdapters[slot]).createObject();
9123 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9124 }
9125 }
9126 else if (newCount < oldCount)
9127 mNetworkAdapters.resize(newCount);
9128 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9129 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9130 for (settings::NetworkAdaptersList::const_iterator
9131 it = data.llNetworkAdapters.begin();
9132 it != data.llNetworkAdapters.end();
9133 ++it)
9134 {
9135 const settings::NetworkAdapter &nic = *it;
9136
9137 /* slot uniqueness is guaranteed by XML Schema */
9138 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9139 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9140 if (FAILED(rc)) return rc;
9141 }
9142
9143 // serial ports (establish defaults first, to ensure reading the same
9144 // settings as we saved, since the list skips ports having defaults)
9145 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9146 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9147 for (settings::SerialPortsList::const_iterator
9148 it = data.llSerialPorts.begin();
9149 it != data.llSerialPorts.end();
9150 ++it)
9151 {
9152 const settings::SerialPort &s = *it;
9153
9154 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9155 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9156 if (FAILED(rc)) return rc;
9157 }
9158
9159 // parallel ports (establish defaults first, to ensure reading the same
9160 // settings as we saved, since the list skips ports having defaults)
9161 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9162 mParallelPorts[i]->i_applyDefaults();
9163 for (settings::ParallelPortsList::const_iterator
9164 it = data.llParallelPorts.begin();
9165 it != data.llParallelPorts.end();
9166 ++it)
9167 {
9168 const settings::ParallelPort &p = *it;
9169
9170 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9171 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9172 if (FAILED(rc)) return rc;
9173 }
9174
9175 /* AudioAdapter */
9176 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9177 if (FAILED(rc)) return rc;
9178
9179 /* storage controllers */
9180 rc = i_loadStorageControllers(data.storage,
9181 puuidRegistry,
9182 puuidSnapshot);
9183 if (FAILED(rc)) return rc;
9184
9185 /* Shared folders */
9186 for (settings::SharedFoldersList::const_iterator
9187 it = data.llSharedFolders.begin();
9188 it != data.llSharedFolders.end();
9189 ++it)
9190 {
9191 const settings::SharedFolder &sf = *it;
9192
9193 ComObjPtr<SharedFolder> sharedFolder;
9194 /* Check for double entries. Not allowed! */
9195 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9196 if (SUCCEEDED(rc))
9197 return setError(VBOX_E_OBJECT_IN_USE,
9198 tr("Shared folder named '%s' already exists"),
9199 sf.strName.c_str());
9200
9201 /* Create the new shared folder. Don't break on error. This will be
9202 * reported when the machine starts. */
9203 sharedFolder.createObject();
9204 rc = sharedFolder->init(i_getMachine(),
9205 sf.strName,
9206 sf.strHostPath,
9207 RT_BOOL(sf.fWritable),
9208 RT_BOOL(sf.fAutoMount),
9209 false /* fFailOnError */);
9210 if (FAILED(rc)) return rc;
9211 mHWData->mSharedFolders.push_back(sharedFolder);
9212 }
9213
9214 // Clipboard
9215 mHWData->mClipboardMode = data.clipboardMode;
9216
9217 // drag'n'drop
9218 mHWData->mDnDMode = data.dndMode;
9219
9220 // guest settings
9221 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9222
9223 // IO settings
9224 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9225 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9226
9227 // Host PCI devices
9228 for (settings::HostPCIDeviceAttachmentList::const_iterator
9229 it = data.pciAttachments.begin();
9230 it != data.pciAttachments.end();
9231 ++it)
9232 {
9233 const settings::HostPCIDeviceAttachment &hpda = *it;
9234 ComObjPtr<PCIDeviceAttachment> pda;
9235
9236 pda.createObject();
9237 pda->i_loadSettings(this, hpda);
9238 mHWData->mPCIDeviceAssignments.push_back(pda);
9239 }
9240
9241 /*
9242 * (The following isn't really real hardware, but it lives in HWData
9243 * for reasons of convenience.)
9244 */
9245
9246#ifdef VBOX_WITH_GUEST_PROPS
9247 /* Guest properties (optional) */
9248
9249 /* Only load transient guest properties for configs which have saved
9250 * state, because there shouldn't be any for powered off VMs. The same
9251 * logic applies for snapshots, as offline snapshots shouldn't have
9252 * any such properties. They confuse the code in various places.
9253 * Note: can't rely on the machine state, as it isn't set yet. */
9254 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9255 /* apologies for the hacky unconst() usage, but this needs hacking
9256 * actually inconsistent settings into consistency, otherwise there
9257 * will be some corner cases where the inconsistency survives
9258 * surprisingly long without getting fixed, especially for snapshots
9259 * as there are no config changes. */
9260 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9261 for (settings::GuestPropertiesList::iterator
9262 it = llGuestProperties.begin();
9263 it != llGuestProperties.end();
9264 /*nothing*/)
9265 {
9266 const settings::GuestProperty &prop = *it;
9267 uint32_t fFlags = guestProp::NILFLAG;
9268 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9269 if ( fSkipTransientGuestProperties
9270 && ( fFlags & guestProp::TRANSIENT
9271 || fFlags & guestProp::TRANSRESET))
9272 {
9273 it = llGuestProperties.erase(it);
9274 continue;
9275 }
9276 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9277 mHWData->mGuestProperties[prop.strName] = property;
9278 ++it;
9279 }
9280#endif /* VBOX_WITH_GUEST_PROPS defined */
9281
9282 rc = i_loadDebugging(pDbg);
9283 if (FAILED(rc))
9284 return rc;
9285
9286 mHWData->mAutostart = *pAutostart;
9287
9288 /* default frontend */
9289 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9290 }
9291 catch (std::bad_alloc &)
9292 {
9293 return E_OUTOFMEMORY;
9294 }
9295
9296 AssertComRC(rc);
9297 return rc;
9298}
9299
9300/**
9301 * Called from i_loadHardware() to load the debugging settings of the
9302 * machine.
9303 *
9304 * @param pDbg Pointer to the settings.
9305 */
9306HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9307{
9308 mHWData->mDebugging = *pDbg;
9309 /* no more processing currently required, this will probably change. */
9310 return S_OK;
9311}
9312
9313/**
9314 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9315 *
9316 * @param data storage settings.
9317 * @param puuidRegistry media registry ID to set media to or NULL;
9318 * see Machine::i_loadMachineDataFromSettings()
9319 * @param puuidSnapshot snapshot ID
9320 * @return
9321 */
9322HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9323 const Guid *puuidRegistry,
9324 const Guid *puuidSnapshot)
9325{
9326 AssertReturn(!i_isSessionMachine(), E_FAIL);
9327
9328 HRESULT rc = S_OK;
9329
9330 for (settings::StorageControllersList::const_iterator
9331 it = data.llStorageControllers.begin();
9332 it != data.llStorageControllers.end();
9333 ++it)
9334 {
9335 const settings::StorageController &ctlData = *it;
9336
9337 ComObjPtr<StorageController> pCtl;
9338 /* Try to find one with the name first. */
9339 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9340 if (SUCCEEDED(rc))
9341 return setError(VBOX_E_OBJECT_IN_USE,
9342 tr("Storage controller named '%s' already exists"),
9343 ctlData.strName.c_str());
9344
9345 pCtl.createObject();
9346 rc = pCtl->init(this,
9347 ctlData.strName,
9348 ctlData.storageBus,
9349 ctlData.ulInstance,
9350 ctlData.fBootable);
9351 if (FAILED(rc)) return rc;
9352
9353 mStorageControllers->push_back(pCtl);
9354
9355 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9356 if (FAILED(rc)) return rc;
9357
9358 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9359 if (FAILED(rc)) return rc;
9360
9361 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9362 if (FAILED(rc)) return rc;
9363
9364 /* Load the attached devices now. */
9365 rc = i_loadStorageDevices(pCtl,
9366 ctlData,
9367 puuidRegistry,
9368 puuidSnapshot);
9369 if (FAILED(rc)) return rc;
9370 }
9371
9372 return S_OK;
9373}
9374
9375/**
9376 * Called from i_loadStorageControllers for a controller's devices.
9377 *
9378 * @param aStorageController
9379 * @param data
9380 * @param puuidRegistry media registry ID to set media to or NULL; see
9381 * Machine::i_loadMachineDataFromSettings()
9382 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9383 * @return
9384 */
9385HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9386 const settings::StorageController &data,
9387 const Guid *puuidRegistry,
9388 const Guid *puuidSnapshot)
9389{
9390 HRESULT rc = S_OK;
9391
9392 /* paranoia: detect duplicate attachments */
9393 for (settings::AttachedDevicesList::const_iterator
9394 it = data.llAttachedDevices.begin();
9395 it != data.llAttachedDevices.end();
9396 ++it)
9397 {
9398 const settings::AttachedDevice &ad = *it;
9399
9400 for (settings::AttachedDevicesList::const_iterator it2 = it;
9401 it2 != data.llAttachedDevices.end();
9402 ++it2)
9403 {
9404 if (it == it2)
9405 continue;
9406
9407 const settings::AttachedDevice &ad2 = *it2;
9408
9409 if ( ad.lPort == ad2.lPort
9410 && ad.lDevice == ad2.lDevice)
9411 {
9412 return setError(E_FAIL,
9413 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9414 aStorageController->i_getName().c_str(),
9415 ad.lPort,
9416 ad.lDevice,
9417 mUserData->s.strName.c_str());
9418 }
9419 }
9420 }
9421
9422 for (settings::AttachedDevicesList::const_iterator
9423 it = data.llAttachedDevices.begin();
9424 it != data.llAttachedDevices.end();
9425 ++it)
9426 {
9427 const settings::AttachedDevice &dev = *it;
9428 ComObjPtr<Medium> medium;
9429
9430 switch (dev.deviceType)
9431 {
9432 case DeviceType_Floppy:
9433 case DeviceType_DVD:
9434 if (dev.strHostDriveSrc.isNotEmpty())
9435 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9436 false /* fRefresh */, medium);
9437 else
9438 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9439 dev.uuid,
9440 false /* fRefresh */,
9441 false /* aSetError */,
9442 medium);
9443 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9444 // This is not an error. The host drive or UUID might have vanished, so just go
9445 // ahead without this removeable medium attachment
9446 rc = S_OK;
9447 break;
9448
9449 case DeviceType_HardDisk:
9450 {
9451 /* find a hard disk by UUID */
9452 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9453 if (FAILED(rc))
9454 {
9455 if (i_isSnapshotMachine())
9456 {
9457 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9458 // so the user knows that the bad disk is in a snapshot somewhere
9459 com::ErrorInfo info;
9460 return setError(E_FAIL,
9461 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9462 puuidSnapshot->raw(),
9463 info.getText().raw());
9464 }
9465 else
9466 return rc;
9467 }
9468
9469 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9470
9471 if (medium->i_getType() == MediumType_Immutable)
9472 {
9473 if (i_isSnapshotMachine())
9474 return setError(E_FAIL,
9475 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9476 "of the virtual machine '%s' ('%s')"),
9477 medium->i_getLocationFull().c_str(),
9478 dev.uuid.raw(),
9479 puuidSnapshot->raw(),
9480 mUserData->s.strName.c_str(),
9481 mData->m_strConfigFileFull.c_str());
9482
9483 return setError(E_FAIL,
9484 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9485 medium->i_getLocationFull().c_str(),
9486 dev.uuid.raw(),
9487 mUserData->s.strName.c_str(),
9488 mData->m_strConfigFileFull.c_str());
9489 }
9490
9491 if (medium->i_getType() == MediumType_MultiAttach)
9492 {
9493 if (i_isSnapshotMachine())
9494 return setError(E_FAIL,
9495 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9496 "of the virtual machine '%s' ('%s')"),
9497 medium->i_getLocationFull().c_str(),
9498 dev.uuid.raw(),
9499 puuidSnapshot->raw(),
9500 mUserData->s.strName.c_str(),
9501 mData->m_strConfigFileFull.c_str());
9502
9503 return setError(E_FAIL,
9504 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9505 medium->i_getLocationFull().c_str(),
9506 dev.uuid.raw(),
9507 mUserData->s.strName.c_str(),
9508 mData->m_strConfigFileFull.c_str());
9509 }
9510
9511 if ( !i_isSnapshotMachine()
9512 && medium->i_getChildren().size() != 0
9513 )
9514 return setError(E_FAIL,
9515 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9516 "because it has %d differencing child hard disks"),
9517 medium->i_getLocationFull().c_str(),
9518 dev.uuid.raw(),
9519 mUserData->s.strName.c_str(),
9520 mData->m_strConfigFileFull.c_str(),
9521 medium->i_getChildren().size());
9522
9523 if (i_findAttachment(*mMediumAttachments.data(),
9524 medium))
9525 return setError(E_FAIL,
9526 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9527 medium->i_getLocationFull().c_str(),
9528 dev.uuid.raw(),
9529 mUserData->s.strName.c_str(),
9530 mData->m_strConfigFileFull.c_str());
9531
9532 break;
9533 }
9534
9535 default:
9536 return setError(E_FAIL,
9537 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9538 medium->i_getLocationFull().c_str(),
9539 mUserData->s.strName.c_str(),
9540 mData->m_strConfigFileFull.c_str());
9541 }
9542
9543 if (FAILED(rc))
9544 break;
9545
9546 /* Bandwidth groups are loaded at this point. */
9547 ComObjPtr<BandwidthGroup> pBwGroup;
9548
9549 if (!dev.strBwGroup.isEmpty())
9550 {
9551 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9552 if (FAILED(rc))
9553 return setError(E_FAIL,
9554 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9555 medium->i_getLocationFull().c_str(),
9556 dev.strBwGroup.c_str(),
9557 mUserData->s.strName.c_str(),
9558 mData->m_strConfigFileFull.c_str());
9559 pBwGroup->i_reference();
9560 }
9561
9562 const Utf8Str controllerName = aStorageController->i_getName();
9563 ComObjPtr<MediumAttachment> pAttachment;
9564 pAttachment.createObject();
9565 rc = pAttachment->init(this,
9566 medium,
9567 controllerName,
9568 dev.lPort,
9569 dev.lDevice,
9570 dev.deviceType,
9571 false,
9572 dev.fPassThrough,
9573 dev.fTempEject,
9574 dev.fNonRotational,
9575 dev.fDiscard,
9576 dev.fHotPluggable,
9577 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9578 if (FAILED(rc)) break;
9579
9580 /* associate the medium with this machine and snapshot */
9581 if (!medium.isNull())
9582 {
9583 AutoCaller medCaller(medium);
9584 if (FAILED(medCaller.rc())) return medCaller.rc();
9585 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9586
9587 if (i_isSnapshotMachine())
9588 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9589 else
9590 rc = medium->i_addBackReference(mData->mUuid);
9591 /* If the medium->addBackReference fails it sets an appropriate
9592 * error message, so no need to do any guesswork here. */
9593
9594 if (puuidRegistry)
9595 // caller wants registry ID to be set on all attached media (OVF import case)
9596 medium->i_addRegistry(*puuidRegistry);
9597 }
9598
9599 if (FAILED(rc))
9600 break;
9601
9602 /* back up mMediumAttachments to let registeredInit() properly rollback
9603 * on failure (= limited accessibility) */
9604 i_setModified(IsModified_Storage);
9605 mMediumAttachments.backup();
9606 mMediumAttachments->push_back(pAttachment);
9607 }
9608
9609 return rc;
9610}
9611
9612/**
9613 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9614 *
9615 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9616 * @param aSnapshot where to return the found snapshot
9617 * @param aSetError true to set extended error info on failure
9618 */
9619HRESULT Machine::i_findSnapshotById(const Guid &aId,
9620 ComObjPtr<Snapshot> &aSnapshot,
9621 bool aSetError /* = false */)
9622{
9623 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9624
9625 if (!mData->mFirstSnapshot)
9626 {
9627 if (aSetError)
9628 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9629 return E_FAIL;
9630 }
9631
9632 if (aId.isZero())
9633 aSnapshot = mData->mFirstSnapshot;
9634 else
9635 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9636
9637 if (!aSnapshot)
9638 {
9639 if (aSetError)
9640 return setError(E_FAIL,
9641 tr("Could not find a snapshot with UUID {%s}"),
9642 aId.toString().c_str());
9643 return E_FAIL;
9644 }
9645
9646 return S_OK;
9647}
9648
9649/**
9650 * Returns the snapshot with the given name or fails of no such snapshot.
9651 *
9652 * @param strName snapshot name to find
9653 * @param aSnapshot where to return the found snapshot
9654 * @param aSetError true to set extended error info on failure
9655 */
9656HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9657 ComObjPtr<Snapshot> &aSnapshot,
9658 bool aSetError /* = false */)
9659{
9660 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9661
9662 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9663
9664 if (!mData->mFirstSnapshot)
9665 {
9666 if (aSetError)
9667 return setError(VBOX_E_OBJECT_NOT_FOUND,
9668 tr("This machine does not have any snapshots"));
9669 return VBOX_E_OBJECT_NOT_FOUND;
9670 }
9671
9672 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9673
9674 if (!aSnapshot)
9675 {
9676 if (aSetError)
9677 return setError(VBOX_E_OBJECT_NOT_FOUND,
9678 tr("Could not find a snapshot named '%s'"), strName.c_str());
9679 return VBOX_E_OBJECT_NOT_FOUND;
9680 }
9681
9682 return S_OK;
9683}
9684
9685/**
9686 * Returns a storage controller object with the given name.
9687 *
9688 * @param aName storage controller name to find
9689 * @param aStorageController where to return the found storage controller
9690 * @param aSetError true to set extended error info on failure
9691 */
9692HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9693 ComObjPtr<StorageController> &aStorageController,
9694 bool aSetError /* = false */)
9695{
9696 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9697
9698 for (StorageControllerList::const_iterator
9699 it = mStorageControllers->begin();
9700 it != mStorageControllers->end();
9701 ++it)
9702 {
9703 if ((*it)->i_getName() == aName)
9704 {
9705 aStorageController = (*it);
9706 return S_OK;
9707 }
9708 }
9709
9710 if (aSetError)
9711 return setError(VBOX_E_OBJECT_NOT_FOUND,
9712 tr("Could not find a storage controller named '%s'"),
9713 aName.c_str());
9714 return VBOX_E_OBJECT_NOT_FOUND;
9715}
9716
9717/**
9718 * Returns a USB controller object with the given name.
9719 *
9720 * @param aName USB controller name to find
9721 * @param aUSBController where to return the found USB controller
9722 * @param aSetError true to set extended error info on failure
9723 */
9724HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9725 ComObjPtr<USBController> &aUSBController,
9726 bool aSetError /* = false */)
9727{
9728 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9729
9730 for (USBControllerList::const_iterator
9731 it = mUSBControllers->begin();
9732 it != mUSBControllers->end();
9733 ++it)
9734 {
9735 if ((*it)->i_getName() == aName)
9736 {
9737 aUSBController = (*it);
9738 return S_OK;
9739 }
9740 }
9741
9742 if (aSetError)
9743 return setError(VBOX_E_OBJECT_NOT_FOUND,
9744 tr("Could not find a storage controller named '%s'"),
9745 aName.c_str());
9746 return VBOX_E_OBJECT_NOT_FOUND;
9747}
9748
9749/**
9750 * Returns the number of USB controller instance of the given type.
9751 *
9752 * @param enmType USB controller type.
9753 */
9754ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9755{
9756 ULONG cCtrls = 0;
9757
9758 for (USBControllerList::const_iterator
9759 it = mUSBControllers->begin();
9760 it != mUSBControllers->end();
9761 ++it)
9762 {
9763 if ((*it)->i_getControllerType() == enmType)
9764 cCtrls++;
9765 }
9766
9767 return cCtrls;
9768}
9769
9770HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9771 MediumAttachmentList &atts)
9772{
9773 AutoCaller autoCaller(this);
9774 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9775
9776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9777
9778 for (MediumAttachmentList::const_iterator
9779 it = mMediumAttachments->begin();
9780 it != mMediumAttachments->end();
9781 ++it)
9782 {
9783 const ComObjPtr<MediumAttachment> &pAtt = *it;
9784 // should never happen, but deal with NULL pointers in the list.
9785 AssertContinue(!pAtt.isNull());
9786
9787 // getControllerName() needs caller+read lock
9788 AutoCaller autoAttCaller(pAtt);
9789 if (FAILED(autoAttCaller.rc()))
9790 {
9791 atts.clear();
9792 return autoAttCaller.rc();
9793 }
9794 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9795
9796 if (pAtt->i_getControllerName() == aName)
9797 atts.push_back(pAtt);
9798 }
9799
9800 return S_OK;
9801}
9802
9803
9804/**
9805 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9806 * file if the machine name was changed and about creating a new settings file
9807 * if this is a new machine.
9808 *
9809 * @note Must be never called directly but only from #saveSettings().
9810 */
9811HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9812{
9813 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9814
9815 HRESULT rc = S_OK;
9816
9817 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9818
9819 /// @todo need to handle primary group change, too
9820
9821 /* attempt to rename the settings file if machine name is changed */
9822 if ( mUserData->s.fNameSync
9823 && mUserData.isBackedUp()
9824 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9825 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9826 )
9827 {
9828 bool dirRenamed = false;
9829 bool fileRenamed = false;
9830
9831 Utf8Str configFile, newConfigFile;
9832 Utf8Str configFilePrev, newConfigFilePrev;
9833 Utf8Str configDir, newConfigDir;
9834
9835 do
9836 {
9837 int vrc = VINF_SUCCESS;
9838
9839 Utf8Str name = mUserData.backedUpData()->s.strName;
9840 Utf8Str newName = mUserData->s.strName;
9841 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9842 if (group == "/")
9843 group.setNull();
9844 Utf8Str newGroup = mUserData->s.llGroups.front();
9845 if (newGroup == "/")
9846 newGroup.setNull();
9847
9848 configFile = mData->m_strConfigFileFull;
9849
9850 /* first, rename the directory if it matches the group and machine name */
9851 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9852 group.c_str(), RTPATH_DELIMITER, name.c_str());
9853 /** @todo hack, make somehow use of ComposeMachineFilename */
9854 if (mUserData->s.fDirectoryIncludesUUID)
9855 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9856 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9857 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9858 /** @todo hack, make somehow use of ComposeMachineFilename */
9859 if (mUserData->s.fDirectoryIncludesUUID)
9860 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9861 configDir = configFile;
9862 configDir.stripFilename();
9863 newConfigDir = configDir;
9864 if ( configDir.length() >= groupPlusName.length()
9865 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9866 groupPlusName.c_str()))
9867 {
9868 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9869 Utf8Str newConfigBaseDir(newConfigDir);
9870 newConfigDir.append(newGroupPlusName);
9871 /* consistency: use \ if appropriate on the platform */
9872 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9873 /* new dir and old dir cannot be equal here because of 'if'
9874 * above and because name != newName */
9875 Assert(configDir != newConfigDir);
9876 if (!fSettingsFileIsNew)
9877 {
9878 /* perform real rename only if the machine is not new */
9879 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9880 if ( vrc == VERR_FILE_NOT_FOUND
9881 || vrc == VERR_PATH_NOT_FOUND)
9882 {
9883 /* create the parent directory, then retry renaming */
9884 Utf8Str parent(newConfigDir);
9885 parent.stripFilename();
9886 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9887 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9888 }
9889 if (RT_FAILURE(vrc))
9890 {
9891 rc = setError(E_FAIL,
9892 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9893 configDir.c_str(),
9894 newConfigDir.c_str(),
9895 vrc);
9896 break;
9897 }
9898 /* delete subdirectories which are no longer needed */
9899 Utf8Str dir(configDir);
9900 dir.stripFilename();
9901 while (dir != newConfigBaseDir && dir != ".")
9902 {
9903 vrc = RTDirRemove(dir.c_str());
9904 if (RT_FAILURE(vrc))
9905 break;
9906 dir.stripFilename();
9907 }
9908 dirRenamed = true;
9909 }
9910 }
9911
9912 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9913 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9914
9915 /* then try to rename the settings file itself */
9916 if (newConfigFile != configFile)
9917 {
9918 /* get the path to old settings file in renamed directory */
9919 configFile = Utf8StrFmt("%s%c%s",
9920 newConfigDir.c_str(),
9921 RTPATH_DELIMITER,
9922 RTPathFilename(configFile.c_str()));
9923 if (!fSettingsFileIsNew)
9924 {
9925 /* perform real rename only if the machine is not new */
9926 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9927 if (RT_FAILURE(vrc))
9928 {
9929 rc = setError(E_FAIL,
9930 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9931 configFile.c_str(),
9932 newConfigFile.c_str(),
9933 vrc);
9934 break;
9935 }
9936 fileRenamed = true;
9937 configFilePrev = configFile;
9938 configFilePrev += "-prev";
9939 newConfigFilePrev = newConfigFile;
9940 newConfigFilePrev += "-prev";
9941 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9942 }
9943 }
9944
9945 // update m_strConfigFileFull amd mConfigFile
9946 mData->m_strConfigFileFull = newConfigFile;
9947 // compute the relative path too
9948 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9949
9950 // store the old and new so that VirtualBox::i_saveSettings() can update
9951 // the media registry
9952 if ( mData->mRegistered
9953 && (configDir != newConfigDir || configFile != newConfigFile))
9954 {
9955 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9956
9957 if (pfNeedsGlobalSaveSettings)
9958 *pfNeedsGlobalSaveSettings = true;
9959 }
9960
9961 // in the saved state file path, replace the old directory with the new directory
9962 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9963 {
9964 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9965 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9966 }
9967
9968 // and do the same thing for the saved state file paths of all the online snapshots
9969 if (mData->mFirstSnapshot)
9970 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9971 newConfigDir.c_str());
9972 }
9973 while (0);
9974
9975 if (FAILED(rc))
9976 {
9977 /* silently try to rename everything back */
9978 if (fileRenamed)
9979 {
9980 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9981 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9982 }
9983 if (dirRenamed)
9984 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9985 }
9986
9987 if (FAILED(rc)) return rc;
9988 }
9989
9990 if (fSettingsFileIsNew)
9991 {
9992 /* create a virgin config file */
9993 int vrc = VINF_SUCCESS;
9994
9995 /* ensure the settings directory exists */
9996 Utf8Str path(mData->m_strConfigFileFull);
9997 path.stripFilename();
9998 if (!RTDirExists(path.c_str()))
9999 {
10000 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10001 if (RT_FAILURE(vrc))
10002 {
10003 return setError(E_FAIL,
10004 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10005 path.c_str(),
10006 vrc);
10007 }
10008 }
10009
10010 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10011 path = Utf8Str(mData->m_strConfigFileFull);
10012 RTFILE f = NIL_RTFILE;
10013 vrc = RTFileOpen(&f, path.c_str(),
10014 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10015 if (RT_FAILURE(vrc))
10016 return setError(E_FAIL,
10017 tr("Could not create the settings file '%s' (%Rrc)"),
10018 path.c_str(),
10019 vrc);
10020 RTFileClose(f);
10021 }
10022
10023 return rc;
10024}
10025
10026/**
10027 * Saves and commits machine data, user data and hardware data.
10028 *
10029 * Note that on failure, the data remains uncommitted.
10030 *
10031 * @a aFlags may combine the following flags:
10032 *
10033 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10034 * Used when saving settings after an operation that makes them 100%
10035 * correspond to the settings from the current snapshot.
10036 * - SaveS_Force: settings will be saved without doing a deep compare of the
10037 * settings structures. This is used when this is called because snapshots
10038 * have changed to avoid the overhead of the deep compare.
10039 *
10040 * @note Must be called from under this object's write lock. Locks children for
10041 * writing.
10042 *
10043 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10044 * initialized to false and that will be set to true by this function if
10045 * the caller must invoke VirtualBox::i_saveSettings() because the global
10046 * settings have changed. This will happen if a machine rename has been
10047 * saved and the global machine and media registries will therefore need
10048 * updating.
10049 * @param aFlags Flags.
10050 */
10051HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10052 int aFlags /*= 0*/)
10053{
10054 LogFlowThisFuncEnter();
10055
10056 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10057
10058 /* make sure child objects are unable to modify the settings while we are
10059 * saving them */
10060 i_ensureNoStateDependencies();
10061
10062 AssertReturn(!i_isSnapshotMachine(),
10063 E_FAIL);
10064
10065 HRESULT rc = S_OK;
10066 bool fNeedsWrite = false;
10067
10068 /* First, prepare to save settings. It will care about renaming the
10069 * settings directory and file if the machine name was changed and about
10070 * creating a new settings file if this is a new machine. */
10071 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10072 if (FAILED(rc)) return rc;
10073
10074 // keep a pointer to the current settings structures
10075 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10076 settings::MachineConfigFile *pNewConfig = NULL;
10077
10078 try
10079 {
10080 // make a fresh one to have everyone write stuff into
10081 pNewConfig = new settings::MachineConfigFile(NULL);
10082 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10083
10084 // now go and copy all the settings data from COM to the settings structures
10085 // (this calls i_saveSettings() on all the COM objects in the machine)
10086 i_copyMachineDataToSettings(*pNewConfig);
10087
10088 if (aFlags & SaveS_ResetCurStateModified)
10089 {
10090 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10091 mData->mCurrentStateModified = FALSE;
10092 fNeedsWrite = true; // always, no need to compare
10093 }
10094 else if (aFlags & SaveS_Force)
10095 {
10096 fNeedsWrite = true; // always, no need to compare
10097 }
10098 else
10099 {
10100 if (!mData->mCurrentStateModified)
10101 {
10102 // do a deep compare of the settings that we just saved with the settings
10103 // previously stored in the config file; this invokes MachineConfigFile::operator==
10104 // which does a deep compare of all the settings, which is expensive but less expensive
10105 // than writing out XML in vain
10106 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10107
10108 // could still be modified if any settings changed
10109 mData->mCurrentStateModified = fAnySettingsChanged;
10110
10111 fNeedsWrite = fAnySettingsChanged;
10112 }
10113 else
10114 fNeedsWrite = true;
10115 }
10116
10117 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10118
10119 if (fNeedsWrite)
10120 // now spit it all out!
10121 pNewConfig->write(mData->m_strConfigFileFull);
10122
10123 mData->pMachineConfigFile = pNewConfig;
10124 delete pOldConfig;
10125 i_commit();
10126
10127 // after saving settings, we are no longer different from the XML on disk
10128 mData->flModifications = 0;
10129 }
10130 catch (HRESULT err)
10131 {
10132 // we assume that error info is set by the thrower
10133 rc = err;
10134
10135 // restore old config
10136 delete pNewConfig;
10137 mData->pMachineConfigFile = pOldConfig;
10138 }
10139 catch (...)
10140 {
10141 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10142 }
10143
10144 if (fNeedsWrite)
10145 {
10146 /* Fire the data change event, even on failure (since we've already
10147 * committed all data). This is done only for SessionMachines because
10148 * mutable Machine instances are always not registered (i.e. private
10149 * to the client process that creates them) and thus don't need to
10150 * inform callbacks. */
10151 if (i_isSessionMachine())
10152 mParent->i_onMachineDataChange(mData->mUuid);
10153 }
10154
10155 LogFlowThisFunc(("rc=%08X\n", rc));
10156 LogFlowThisFuncLeave();
10157 return rc;
10158}
10159
10160/**
10161 * Implementation for saving the machine settings into the given
10162 * settings::MachineConfigFile instance. This copies machine extradata
10163 * from the previous machine config file in the instance data, if any.
10164 *
10165 * This gets called from two locations:
10166 *
10167 * -- Machine::i_saveSettings(), during the regular XML writing;
10168 *
10169 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10170 * exported to OVF and we write the VirtualBox proprietary XML
10171 * into a <vbox:Machine> tag.
10172 *
10173 * This routine fills all the fields in there, including snapshots, *except*
10174 * for the following:
10175 *
10176 * -- fCurrentStateModified. There is some special logic associated with that.
10177 *
10178 * The caller can then call MachineConfigFile::write() or do something else
10179 * with it.
10180 *
10181 * Caller must hold the machine lock!
10182 *
10183 * This throws XML errors and HRESULT, so the caller must have a catch block!
10184 */
10185void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10186{
10187 // deep copy extradata, being extra careful with self assignment (the STL
10188 // map assignment on Mac OS X clang based Xcode isn't checking)
10189 if (&config != mData->pMachineConfigFile)
10190 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10191
10192 config.uuid = mData->mUuid;
10193
10194 // copy name, description, OS type, teleport, UTC etc.
10195 config.machineUserData = mUserData->s;
10196
10197 if ( mData->mMachineState == MachineState_Saved
10198 || mData->mMachineState == MachineState_Restoring
10199 // when doing certain snapshot operations we may or may not have
10200 // a saved state in the current state, so keep everything as is
10201 || ( ( mData->mMachineState == MachineState_Snapshotting
10202 || mData->mMachineState == MachineState_DeletingSnapshot
10203 || mData->mMachineState == MachineState_RestoringSnapshot)
10204 && (!mSSData->strStateFilePath.isEmpty())
10205 )
10206 )
10207 {
10208 Assert(!mSSData->strStateFilePath.isEmpty());
10209 /* try to make the file name relative to the settings file dir */
10210 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10211 }
10212 else
10213 {
10214 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10215 config.strStateFile.setNull();
10216 }
10217
10218 if (mData->mCurrentSnapshot)
10219 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10220 else
10221 config.uuidCurrentSnapshot.clear();
10222
10223 config.timeLastStateChange = mData->mLastStateChange;
10224 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10225 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10226
10227 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10228 if (FAILED(rc)) throw rc;
10229
10230 // save machine's media registry if this is VirtualBox 4.0 or later
10231 if (config.canHaveOwnMediaRegistry())
10232 {
10233 // determine machine folder
10234 Utf8Str strMachineFolder = i_getSettingsFileFull();
10235 strMachineFolder.stripFilename();
10236 mParent->i_saveMediaRegistry(config.mediaRegistry,
10237 i_getId(), // only media with registry ID == machine UUID
10238 strMachineFolder);
10239 // this throws HRESULT
10240 }
10241
10242 // save snapshots
10243 rc = i_saveAllSnapshots(config);
10244 if (FAILED(rc)) throw rc;
10245}
10246
10247/**
10248 * Saves all snapshots of the machine into the given machine config file. Called
10249 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10250 * @param config
10251 * @return
10252 */
10253HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10254{
10255 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10256
10257 HRESULT rc = S_OK;
10258
10259 try
10260 {
10261 config.llFirstSnapshot.clear();
10262
10263 if (mData->mFirstSnapshot)
10264 {
10265 // the settings use a list for "the first snapshot"
10266 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10267
10268 // get reference to the snapshot on the list and work on that
10269 // element straight in the list to avoid excessive copying later
10270 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10271 if (FAILED(rc)) throw rc;
10272 }
10273
10274// if (mType == IsSessionMachine)
10275// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10276
10277 }
10278 catch (HRESULT err)
10279 {
10280 /* we assume that error info is set by the thrower */
10281 rc = err;
10282 }
10283 catch (...)
10284 {
10285 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10286 }
10287
10288 return rc;
10289}
10290
10291/**
10292 * Saves the VM hardware configuration. It is assumed that the
10293 * given node is empty.
10294 *
10295 * @param data Reference to the settings object for the hardware config.
10296 * @param pDbg Pointer to the settings object for the debugging config
10297 * which happens to live in mHWData.
10298 * @param pAutostart Pointer to the settings object for the autostart config
10299 * which happens to live in mHWData.
10300 */
10301HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10302 settings::Autostart *pAutostart)
10303{
10304 HRESULT rc = S_OK;
10305
10306 try
10307 {
10308 /* The hardware version attribute (optional).
10309 Automatically upgrade from 1 to current default hardware version
10310 when there is no saved state. (ugly!) */
10311 if ( mHWData->mHWVersion == "1"
10312 && mSSData->strStateFilePath.isEmpty()
10313 )
10314 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10315
10316 data.strVersion = mHWData->mHWVersion;
10317 data.uuid = mHWData->mHardwareUUID;
10318
10319 // CPU
10320 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10321 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10322 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10323 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10324 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10325 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10326 data.fPAE = !!mHWData->mPAEEnabled;
10327 data.enmLongMode = mHWData->mLongMode;
10328 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10329 data.fAPIC = !!mHWData->mAPIC;
10330 data.fX2APIC = !!mHWData->mX2APIC;
10331 data.cCPUs = mHWData->mCPUCount;
10332 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10333 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10334 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10335 data.strCpuProfile = mHWData->mCpuProfile;
10336
10337 data.llCpus.clear();
10338 if (data.fCpuHotPlug)
10339 {
10340 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10341 {
10342 if (mHWData->mCPUAttached[idx])
10343 {
10344 settings::Cpu cpu;
10345 cpu.ulId = idx;
10346 data.llCpus.push_back(cpu);
10347 }
10348 }
10349 }
10350
10351 /* Standard and Extended CPUID leafs. */
10352 data.llCpuIdLeafs.clear();
10353 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10354 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10355 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10356 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10357 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10358 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10359
10360 // memory
10361 data.ulMemorySizeMB = mHWData->mMemorySize;
10362 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10363
10364 // firmware
10365 data.firmwareType = mHWData->mFirmwareType;
10366
10367 // HID
10368 data.pointingHIDType = mHWData->mPointingHIDType;
10369 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10370
10371 // chipset
10372 data.chipsetType = mHWData->mChipsetType;
10373
10374 // paravirt
10375 data.paravirtProvider = mHWData->mParavirtProvider;
10376 data.strParavirtDebug = mHWData->mParavirtDebug;
10377
10378 // emulated USB card reader
10379 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10380
10381 // HPET
10382 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10383
10384 // boot order
10385 data.mapBootOrder.clear();
10386 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10387 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10388
10389 // display
10390 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10391 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10392 data.cMonitors = mHWData->mMonitorCount;
10393 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10394 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10395 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10396 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10397 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10398 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10399 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10400 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10401 {
10402 if (mHWData->maVideoCaptureScreens[i])
10403 ASMBitSet(&data.u64VideoCaptureScreens, i);
10404 else
10405 ASMBitClear(&data.u64VideoCaptureScreens, i);
10406 }
10407 /* store relative video capture file if possible */
10408 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10409
10410 /* VRDEServer settings (optional) */
10411 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10412 if (FAILED(rc)) throw rc;
10413
10414 /* BIOS (required) */
10415 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10416 if (FAILED(rc)) throw rc;
10417
10418 /* USB Controller (required) */
10419 data.usbSettings.llUSBControllers.clear();
10420 for (USBControllerList::const_iterator
10421 it = mUSBControllers->begin();
10422 it != mUSBControllers->end();
10423 ++it)
10424 {
10425 ComObjPtr<USBController> ctrl = *it;
10426 settings::USBController settingsCtrl;
10427
10428 settingsCtrl.strName = ctrl->i_getName();
10429 settingsCtrl.enmType = ctrl->i_getControllerType();
10430
10431 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10432 }
10433
10434 /* USB device filters (required) */
10435 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10436 if (FAILED(rc)) throw rc;
10437
10438 /* Network adapters (required) */
10439 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10440 data.llNetworkAdapters.clear();
10441 /* Write out only the nominal number of network adapters for this
10442 * chipset type. Since Machine::commit() hasn't been called there
10443 * may be extra NIC settings in the vector. */
10444 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10445 {
10446 settings::NetworkAdapter nic;
10447 nic.ulSlot = (uint32_t)slot;
10448 /* paranoia check... must not be NULL, but must not crash either. */
10449 if (mNetworkAdapters[slot])
10450 {
10451 if (mNetworkAdapters[slot]->i_hasDefaults())
10452 continue;
10453
10454 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10455 if (FAILED(rc)) throw rc;
10456
10457 data.llNetworkAdapters.push_back(nic);
10458 }
10459 }
10460
10461 /* Serial ports */
10462 data.llSerialPorts.clear();
10463 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10464 {
10465 if (mSerialPorts[slot]->i_hasDefaults())
10466 continue;
10467
10468 settings::SerialPort s;
10469 s.ulSlot = slot;
10470 rc = mSerialPorts[slot]->i_saveSettings(s);
10471 if (FAILED(rc)) return rc;
10472
10473 data.llSerialPorts.push_back(s);
10474 }
10475
10476 /* Parallel ports */
10477 data.llParallelPorts.clear();
10478 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10479 {
10480 if (mParallelPorts[slot]->i_hasDefaults())
10481 continue;
10482
10483 settings::ParallelPort p;
10484 p.ulSlot = slot;
10485 rc = mParallelPorts[slot]->i_saveSettings(p);
10486 if (FAILED(rc)) return rc;
10487
10488 data.llParallelPorts.push_back(p);
10489 }
10490
10491 /* Audio adapter */
10492 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10493 if (FAILED(rc)) return rc;
10494
10495 rc = i_saveStorageControllers(data.storage);
10496 if (FAILED(rc)) return rc;
10497
10498 /* Shared folders */
10499 data.llSharedFolders.clear();
10500 for (HWData::SharedFolderList::const_iterator
10501 it = mHWData->mSharedFolders.begin();
10502 it != mHWData->mSharedFolders.end();
10503 ++it)
10504 {
10505 SharedFolder *pSF = *it;
10506 AutoCaller sfCaller(pSF);
10507 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10508 settings::SharedFolder sf;
10509 sf.strName = pSF->i_getName();
10510 sf.strHostPath = pSF->i_getHostPath();
10511 sf.fWritable = !!pSF->i_isWritable();
10512 sf.fAutoMount = !!pSF->i_isAutoMounted();
10513
10514 data.llSharedFolders.push_back(sf);
10515 }
10516
10517 // clipboard
10518 data.clipboardMode = mHWData->mClipboardMode;
10519
10520 // drag'n'drop
10521 data.dndMode = mHWData->mDnDMode;
10522
10523 /* Guest */
10524 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10525
10526 // IO settings
10527 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10528 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10529
10530 /* BandwidthControl (required) */
10531 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10532 if (FAILED(rc)) throw rc;
10533
10534 /* Host PCI devices */
10535 data.pciAttachments.clear();
10536 for (HWData::PCIDeviceAssignmentList::const_iterator
10537 it = mHWData->mPCIDeviceAssignments.begin();
10538 it != mHWData->mPCIDeviceAssignments.end();
10539 ++it)
10540 {
10541 ComObjPtr<PCIDeviceAttachment> pda = *it;
10542 settings::HostPCIDeviceAttachment hpda;
10543
10544 rc = pda->i_saveSettings(hpda);
10545 if (FAILED(rc)) throw rc;
10546
10547 data.pciAttachments.push_back(hpda);
10548 }
10549
10550 // guest properties
10551 data.llGuestProperties.clear();
10552#ifdef VBOX_WITH_GUEST_PROPS
10553 for (HWData::GuestPropertyMap::const_iterator
10554 it = mHWData->mGuestProperties.begin();
10555 it != mHWData->mGuestProperties.end();
10556 ++it)
10557 {
10558 HWData::GuestProperty property = it->second;
10559
10560 /* Remove transient guest properties at shutdown unless we
10561 * are saving state. Note that restoring snapshot intentionally
10562 * keeps them, they will be removed if appropriate once the final
10563 * machine state is set (as crashes etc. need to work). */
10564 if ( ( mData->mMachineState == MachineState_PoweredOff
10565 || mData->mMachineState == MachineState_Aborted
10566 || mData->mMachineState == MachineState_Teleported)
10567 && ( property.mFlags & guestProp::TRANSIENT
10568 || property.mFlags & guestProp::TRANSRESET))
10569 continue;
10570 settings::GuestProperty prop;
10571 prop.strName = it->first;
10572 prop.strValue = property.strValue;
10573 prop.timestamp = property.mTimestamp;
10574 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10575 guestProp::writeFlags(property.mFlags, szFlags);
10576 prop.strFlags = szFlags;
10577
10578 data.llGuestProperties.push_back(prop);
10579 }
10580
10581 /* I presume this doesn't require a backup(). */
10582 mData->mGuestPropertiesModified = FALSE;
10583#endif /* VBOX_WITH_GUEST_PROPS defined */
10584
10585 *pDbg = mHWData->mDebugging;
10586 *pAutostart = mHWData->mAutostart;
10587
10588 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10589 }
10590 catch (std::bad_alloc &)
10591 {
10592 return E_OUTOFMEMORY;
10593 }
10594
10595 AssertComRC(rc);
10596 return rc;
10597}
10598
10599/**
10600 * Saves the storage controller configuration.
10601 *
10602 * @param data storage settings.
10603 */
10604HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10605{
10606 data.llStorageControllers.clear();
10607
10608 for (StorageControllerList::const_iterator
10609 it = mStorageControllers->begin();
10610 it != mStorageControllers->end();
10611 ++it)
10612 {
10613 HRESULT rc;
10614 ComObjPtr<StorageController> pCtl = *it;
10615
10616 settings::StorageController ctl;
10617 ctl.strName = pCtl->i_getName();
10618 ctl.controllerType = pCtl->i_getControllerType();
10619 ctl.storageBus = pCtl->i_getStorageBus();
10620 ctl.ulInstance = pCtl->i_getInstance();
10621 ctl.fBootable = pCtl->i_getBootable();
10622
10623 /* Save the port count. */
10624 ULONG portCount;
10625 rc = pCtl->COMGETTER(PortCount)(&portCount);
10626 ComAssertComRCRet(rc, rc);
10627 ctl.ulPortCount = portCount;
10628
10629 /* Save fUseHostIOCache */
10630 BOOL fUseHostIOCache;
10631 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10632 ComAssertComRCRet(rc, rc);
10633 ctl.fUseHostIOCache = !!fUseHostIOCache;
10634
10635 /* save the devices now. */
10636 rc = i_saveStorageDevices(pCtl, ctl);
10637 ComAssertComRCRet(rc, rc);
10638
10639 data.llStorageControllers.push_back(ctl);
10640 }
10641
10642 return S_OK;
10643}
10644
10645/**
10646 * Saves the hard disk configuration.
10647 */
10648HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10649 settings::StorageController &data)
10650{
10651 MediumAttachmentList atts;
10652
10653 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10654 if (FAILED(rc)) return rc;
10655
10656 data.llAttachedDevices.clear();
10657 for (MediumAttachmentList::const_iterator
10658 it = atts.begin();
10659 it != atts.end();
10660 ++it)
10661 {
10662 settings::AttachedDevice dev;
10663 IMediumAttachment *iA = *it;
10664 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10665 Medium *pMedium = pAttach->i_getMedium();
10666
10667 dev.deviceType = pAttach->i_getType();
10668 dev.lPort = pAttach->i_getPort();
10669 dev.lDevice = pAttach->i_getDevice();
10670 dev.fPassThrough = pAttach->i_getPassthrough();
10671 dev.fHotPluggable = pAttach->i_getHotPluggable();
10672 if (pMedium)
10673 {
10674 if (pMedium->i_isHostDrive())
10675 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10676 else
10677 dev.uuid = pMedium->i_getId();
10678 dev.fTempEject = pAttach->i_getTempEject();
10679 dev.fNonRotational = pAttach->i_getNonRotational();
10680 dev.fDiscard = pAttach->i_getDiscard();
10681 }
10682
10683 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10684
10685 data.llAttachedDevices.push_back(dev);
10686 }
10687
10688 return S_OK;
10689}
10690
10691/**
10692 * Saves machine state settings as defined by aFlags
10693 * (SaveSTS_* values).
10694 *
10695 * @param aFlags Combination of SaveSTS_* flags.
10696 *
10697 * @note Locks objects for writing.
10698 */
10699HRESULT Machine::i_saveStateSettings(int aFlags)
10700{
10701 if (aFlags == 0)
10702 return S_OK;
10703
10704 AutoCaller autoCaller(this);
10705 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10706
10707 /* This object's write lock is also necessary to serialize file access
10708 * (prevent concurrent reads and writes) */
10709 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10710
10711 HRESULT rc = S_OK;
10712
10713 Assert(mData->pMachineConfigFile);
10714
10715 try
10716 {
10717 if (aFlags & SaveSTS_CurStateModified)
10718 mData->pMachineConfigFile->fCurrentStateModified = true;
10719
10720 if (aFlags & SaveSTS_StateFilePath)
10721 {
10722 if (!mSSData->strStateFilePath.isEmpty())
10723 /* try to make the file name relative to the settings file dir */
10724 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10725 else
10726 mData->pMachineConfigFile->strStateFile.setNull();
10727 }
10728
10729 if (aFlags & SaveSTS_StateTimeStamp)
10730 {
10731 Assert( mData->mMachineState != MachineState_Aborted
10732 || mSSData->strStateFilePath.isEmpty());
10733
10734 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10735
10736 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10737/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10738 }
10739
10740 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10741 }
10742 catch (...)
10743 {
10744 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10745 }
10746
10747 return rc;
10748}
10749
10750/**
10751 * Ensures that the given medium is added to a media registry. If this machine
10752 * was created with 4.0 or later, then the machine registry is used. Otherwise
10753 * the global VirtualBox media registry is used.
10754 *
10755 * Caller must NOT hold machine lock, media tree or any medium locks!
10756 *
10757 * @param pMedium
10758 */
10759void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10760{
10761 /* Paranoia checks: do not hold machine or media tree locks. */
10762 AssertReturnVoid(!isWriteLockOnCurrentThread());
10763 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10764
10765 ComObjPtr<Medium> pBase;
10766 {
10767 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10768 pBase = pMedium->i_getBase();
10769 }
10770
10771 /* Paranoia checks: do not hold medium locks. */
10772 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10773 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10774
10775 // decide which medium registry to use now that the medium is attached:
10776 Guid uuid;
10777 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10778 // machine XML is VirtualBox 4.0 or higher:
10779 uuid = i_getId(); // machine UUID
10780 else
10781 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10782
10783 if (pMedium->i_addRegistry(uuid))
10784 mParent->i_markRegistryModified(uuid);
10785
10786 /* For more complex hard disk structures it can happen that the base
10787 * medium isn't yet associated with any medium registry. Do that now. */
10788 if (pMedium != pBase)
10789 {
10790 /* Tree lock needed by Medium::addRegistry when recursing. */
10791 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10792 if (pBase->i_addRegistryRecursive(uuid))
10793 {
10794 treeLock.release();
10795 mParent->i_markRegistryModified(uuid);
10796 }
10797 }
10798}
10799
10800/**
10801 * Creates differencing hard disks for all normal hard disks attached to this
10802 * machine and a new set of attachments to refer to created disks.
10803 *
10804 * Used when taking a snapshot or when deleting the current state. Gets called
10805 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10806 *
10807 * This method assumes that mMediumAttachments contains the original hard disk
10808 * attachments it needs to create diffs for. On success, these attachments will
10809 * be replaced with the created diffs.
10810 *
10811 * Attachments with non-normal hard disks are left as is.
10812 *
10813 * If @a aOnline is @c false then the original hard disks that require implicit
10814 * diffs will be locked for reading. Otherwise it is assumed that they are
10815 * already locked for writing (when the VM was started). Note that in the latter
10816 * case it is responsibility of the caller to lock the newly created diffs for
10817 * writing if this method succeeds.
10818 *
10819 * @param aProgress Progress object to run (must contain at least as
10820 * many operations left as the number of hard disks
10821 * attached).
10822 * @param aWeight Weight of this operation.
10823 * @param aOnline Whether the VM was online prior to this operation.
10824 *
10825 * @note The progress object is not marked as completed, neither on success nor
10826 * on failure. This is a responsibility of the caller.
10827 *
10828 * @note Locks this object and the media tree for writing.
10829 */
10830HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10831 ULONG aWeight,
10832 bool aOnline)
10833{
10834 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10835
10836 AutoCaller autoCaller(this);
10837 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10838
10839 AutoMultiWriteLock2 alock(this->lockHandle(),
10840 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10841
10842 /* must be in a protective state because we release the lock below */
10843 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10844 || mData->mMachineState == MachineState_OnlineSnapshotting
10845 || mData->mMachineState == MachineState_LiveSnapshotting
10846 || mData->mMachineState == MachineState_RestoringSnapshot
10847 || mData->mMachineState == MachineState_DeletingSnapshot
10848 , E_FAIL);
10849
10850 HRESULT rc = S_OK;
10851
10852 // use appropriate locked media map (online or offline)
10853 MediumLockListMap lockedMediaOffline;
10854 MediumLockListMap *lockedMediaMap;
10855 if (aOnline)
10856 lockedMediaMap = &mData->mSession.mLockedMedia;
10857 else
10858 lockedMediaMap = &lockedMediaOffline;
10859
10860 try
10861 {
10862 if (!aOnline)
10863 {
10864 /* lock all attached hard disks early to detect "in use"
10865 * situations before creating actual diffs */
10866 for (MediumAttachmentList::const_iterator
10867 it = mMediumAttachments->begin();
10868 it != mMediumAttachments->end();
10869 ++it)
10870 {
10871 MediumAttachment *pAtt = *it;
10872 if (pAtt->i_getType() == DeviceType_HardDisk)
10873 {
10874 Medium *pMedium = pAtt->i_getMedium();
10875 Assert(pMedium);
10876
10877 MediumLockList *pMediumLockList(new MediumLockList());
10878 alock.release();
10879 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10880 NULL /* pToLockWrite */,
10881 false /* fMediumLockWriteAll */,
10882 NULL,
10883 *pMediumLockList);
10884 alock.acquire();
10885 if (FAILED(rc))
10886 {
10887 delete pMediumLockList;
10888 throw rc;
10889 }
10890 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10891 if (FAILED(rc))
10892 {
10893 throw setError(rc,
10894 tr("Collecting locking information for all attached media failed"));
10895 }
10896 }
10897 }
10898
10899 /* Now lock all media. If this fails, nothing is locked. */
10900 alock.release();
10901 rc = lockedMediaMap->Lock();
10902 alock.acquire();
10903 if (FAILED(rc))
10904 {
10905 throw setError(rc,
10906 tr("Locking of attached media failed"));
10907 }
10908 }
10909
10910 /* remember the current list (note that we don't use backup() since
10911 * mMediumAttachments may be already backed up) */
10912 MediumAttachmentList atts = *mMediumAttachments.data();
10913
10914 /* start from scratch */
10915 mMediumAttachments->clear();
10916
10917 /* go through remembered attachments and create diffs for normal hard
10918 * disks and attach them */
10919 for (MediumAttachmentList::const_iterator
10920 it = atts.begin();
10921 it != atts.end();
10922 ++it)
10923 {
10924 MediumAttachment *pAtt = *it;
10925
10926 DeviceType_T devType = pAtt->i_getType();
10927 Medium *pMedium = pAtt->i_getMedium();
10928
10929 if ( devType != DeviceType_HardDisk
10930 || pMedium == NULL
10931 || pMedium->i_getType() != MediumType_Normal)
10932 {
10933 /* copy the attachment as is */
10934
10935 /** @todo the progress object created in SessionMachine::TakeSnaphot
10936 * only expects operations for hard disks. Later other
10937 * device types need to show up in the progress as well. */
10938 if (devType == DeviceType_HardDisk)
10939 {
10940 if (pMedium == NULL)
10941 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10942 aWeight); // weight
10943 else
10944 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10945 pMedium->i_getBase()->i_getName().c_str()).raw(),
10946 aWeight); // weight
10947 }
10948
10949 mMediumAttachments->push_back(pAtt);
10950 continue;
10951 }
10952
10953 /* need a diff */
10954 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10955 pMedium->i_getBase()->i_getName().c_str()).raw(),
10956 aWeight); // weight
10957
10958 Utf8Str strFullSnapshotFolder;
10959 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10960
10961 ComObjPtr<Medium> diff;
10962 diff.createObject();
10963 // store the diff in the same registry as the parent
10964 // (this cannot fail here because we can't create implicit diffs for
10965 // unregistered images)
10966 Guid uuidRegistryParent;
10967 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10968 Assert(fInRegistry); NOREF(fInRegistry);
10969 rc = diff->init(mParent,
10970 pMedium->i_getPreferredDiffFormat(),
10971 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10972 uuidRegistryParent,
10973 DeviceType_HardDisk);
10974 if (FAILED(rc)) throw rc;
10975
10976 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10977 * the push_back? Looks like we're going to release medium with the
10978 * wrong kind of lock (general issue with if we fail anywhere at all)
10979 * and an orphaned VDI in the snapshots folder. */
10980
10981 /* update the appropriate lock list */
10982 MediumLockList *pMediumLockList;
10983 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10984 AssertComRCThrowRC(rc);
10985 if (aOnline)
10986 {
10987 alock.release();
10988 /* The currently attached medium will be read-only, change
10989 * the lock type to read. */
10990 rc = pMediumLockList->Update(pMedium, false);
10991 alock.acquire();
10992 AssertComRCThrowRC(rc);
10993 }
10994
10995 /* release the locks before the potentially lengthy operation */
10996 alock.release();
10997 rc = pMedium->i_createDiffStorage(diff,
10998 pMedium->i_getPreferredDiffVariant(),
10999 pMediumLockList,
11000 NULL /* aProgress */,
11001 true /* aWait */);
11002 alock.acquire();
11003 if (FAILED(rc)) throw rc;
11004
11005 /* actual lock list update is done in Machine::i_commitMedia */
11006
11007 rc = diff->i_addBackReference(mData->mUuid);
11008 AssertComRCThrowRC(rc);
11009
11010 /* add a new attachment */
11011 ComObjPtr<MediumAttachment> attachment;
11012 attachment.createObject();
11013 rc = attachment->init(this,
11014 diff,
11015 pAtt->i_getControllerName(),
11016 pAtt->i_getPort(),
11017 pAtt->i_getDevice(),
11018 DeviceType_HardDisk,
11019 true /* aImplicit */,
11020 false /* aPassthrough */,
11021 false /* aTempEject */,
11022 pAtt->i_getNonRotational(),
11023 pAtt->i_getDiscard(),
11024 pAtt->i_getHotPluggable(),
11025 pAtt->i_getBandwidthGroup());
11026 if (FAILED(rc)) throw rc;
11027
11028 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11029 AssertComRCThrowRC(rc);
11030 mMediumAttachments->push_back(attachment);
11031 }
11032 }
11033 catch (HRESULT aRC) { rc = aRC; }
11034
11035 /* unlock all hard disks we locked when there is no VM */
11036 if (!aOnline)
11037 {
11038 ErrorInfoKeeper eik;
11039
11040 HRESULT rc1 = lockedMediaMap->Clear();
11041 AssertComRC(rc1);
11042 }
11043
11044 return rc;
11045}
11046
11047/**
11048 * Deletes implicit differencing hard disks created either by
11049 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11050 * mMediumAttachments.
11051 *
11052 * Note that to delete hard disks created by #attachDevice() this method is
11053 * called from #i_rollbackMedia() when the changes are rolled back.
11054 *
11055 * @note Locks this object and the media tree for writing.
11056 */
11057HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11058{
11059 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11060
11061 AutoCaller autoCaller(this);
11062 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11063
11064 AutoMultiWriteLock2 alock(this->lockHandle(),
11065 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11066
11067 /* We absolutely must have backed up state. */
11068 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11069
11070 /* Check if there are any implicitly created diff images. */
11071 bool fImplicitDiffs = false;
11072 for (MediumAttachmentList::const_iterator
11073 it = mMediumAttachments->begin();
11074 it != mMediumAttachments->end();
11075 ++it)
11076 {
11077 const ComObjPtr<MediumAttachment> &pAtt = *it;
11078 if (pAtt->i_isImplicit())
11079 {
11080 fImplicitDiffs = true;
11081 break;
11082 }
11083 }
11084 /* If there is nothing to do, leave early. This saves lots of image locking
11085 * effort. It also avoids a MachineStateChanged event without real reason.
11086 * This is important e.g. when loading a VM config, because there should be
11087 * no events. Otherwise API clients can become thoroughly confused for
11088 * inaccessible VMs (the code for loading VM configs uses this method for
11089 * cleanup if the config makes no sense), as they take such events as an
11090 * indication that the VM is alive, and they would force the VM config to
11091 * be reread, leading to an endless loop. */
11092 if (!fImplicitDiffs)
11093 return S_OK;
11094
11095 HRESULT rc = S_OK;
11096 MachineState_T oldState = mData->mMachineState;
11097
11098 /* will release the lock before the potentially lengthy operation,
11099 * so protect with the special state (unless already protected) */
11100 if ( oldState != MachineState_Snapshotting
11101 && oldState != MachineState_OnlineSnapshotting
11102 && oldState != MachineState_LiveSnapshotting
11103 && oldState != MachineState_RestoringSnapshot
11104 && oldState != MachineState_DeletingSnapshot
11105 && oldState != MachineState_DeletingSnapshotOnline
11106 && oldState != MachineState_DeletingSnapshotPaused
11107 )
11108 i_setMachineState(MachineState_SettingUp);
11109
11110 // use appropriate locked media map (online or offline)
11111 MediumLockListMap lockedMediaOffline;
11112 MediumLockListMap *lockedMediaMap;
11113 if (aOnline)
11114 lockedMediaMap = &mData->mSession.mLockedMedia;
11115 else
11116 lockedMediaMap = &lockedMediaOffline;
11117
11118 try
11119 {
11120 if (!aOnline)
11121 {
11122 /* lock all attached hard disks early to detect "in use"
11123 * situations before deleting actual diffs */
11124 for (MediumAttachmentList::const_iterator
11125 it = mMediumAttachments->begin();
11126 it != mMediumAttachments->end();
11127 ++it)
11128 {
11129 MediumAttachment *pAtt = *it;
11130 if (pAtt->i_getType() == DeviceType_HardDisk)
11131 {
11132 Medium *pMedium = pAtt->i_getMedium();
11133 Assert(pMedium);
11134
11135 MediumLockList *pMediumLockList(new MediumLockList());
11136 alock.release();
11137 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11138 NULL /* pToLockWrite */,
11139 false /* fMediumLockWriteAll */,
11140 NULL,
11141 *pMediumLockList);
11142 alock.acquire();
11143
11144 if (FAILED(rc))
11145 {
11146 delete pMediumLockList;
11147 throw rc;
11148 }
11149
11150 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11151 if (FAILED(rc))
11152 throw rc;
11153 }
11154 }
11155
11156 if (FAILED(rc))
11157 throw rc;
11158 } // end of offline
11159
11160 /* Lock lists are now up to date and include implicitly created media */
11161
11162 /* Go through remembered attachments and delete all implicitly created
11163 * diffs and fix up the attachment information */
11164 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11165 MediumAttachmentList implicitAtts;
11166 for (MediumAttachmentList::const_iterator
11167 it = mMediumAttachments->begin();
11168 it != mMediumAttachments->end();
11169 ++it)
11170 {
11171 ComObjPtr<MediumAttachment> pAtt = *it;
11172 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11173 if (pMedium.isNull())
11174 continue;
11175
11176 // Implicit attachments go on the list for deletion and back references are removed.
11177 if (pAtt->i_isImplicit())
11178 {
11179 /* Deassociate and mark for deletion */
11180 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11181 rc = pMedium->i_removeBackReference(mData->mUuid);
11182 if (FAILED(rc))
11183 throw rc;
11184 implicitAtts.push_back(pAtt);
11185 continue;
11186 }
11187
11188 /* Was this medium attached before? */
11189 if (!i_findAttachment(oldAtts, pMedium))
11190 {
11191 /* no: de-associate */
11192 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11193 rc = pMedium->i_removeBackReference(mData->mUuid);
11194 if (FAILED(rc))
11195 throw rc;
11196 continue;
11197 }
11198 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11199 }
11200
11201 /* If there are implicit attachments to delete, throw away the lock
11202 * map contents (which will unlock all media) since the medium
11203 * attachments will be rolled back. Below we need to completely
11204 * recreate the lock map anyway since it is infinitely complex to
11205 * do this incrementally (would need reconstructing each attachment
11206 * change, which would be extremely hairy). */
11207 if (implicitAtts.size() != 0)
11208 {
11209 ErrorInfoKeeper eik;
11210
11211 HRESULT rc1 = lockedMediaMap->Clear();
11212 AssertComRC(rc1);
11213 }
11214
11215 /* rollback hard disk changes */
11216 mMediumAttachments.rollback();
11217
11218 MultiResult mrc(S_OK);
11219
11220 // Delete unused implicit diffs.
11221 if (implicitAtts.size() != 0)
11222 {
11223 alock.release();
11224
11225 for (MediumAttachmentList::const_iterator
11226 it = implicitAtts.begin();
11227 it != implicitAtts.end();
11228 ++it)
11229 {
11230 // Remove medium associated with this attachment.
11231 ComObjPtr<MediumAttachment> pAtt = *it;
11232 Assert(pAtt);
11233 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11234 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11235 Assert(pMedium);
11236
11237 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11238 // continue on delete failure, just collect error messages
11239 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11240 pMedium->i_getLocationFull().c_str() ));
11241 mrc = rc;
11242 }
11243 // Clear the list of deleted implicit attachments now, while not
11244 // holding the lock, as it will ultimately trigger Medium::uninit()
11245 // calls which assume that the media tree lock isn't held.
11246 implicitAtts.clear();
11247
11248 alock.acquire();
11249
11250 /* if there is a VM recreate media lock map as mentioned above,
11251 * otherwise it is a waste of time and we leave things unlocked */
11252 if (aOnline)
11253 {
11254 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11255 /* must never be NULL, but better safe than sorry */
11256 if (!pMachine.isNull())
11257 {
11258 alock.release();
11259 rc = mData->mSession.mMachine->i_lockMedia();
11260 alock.acquire();
11261 if (FAILED(rc))
11262 throw rc;
11263 }
11264 }
11265 }
11266 }
11267 catch (HRESULT aRC) {rc = aRC;}
11268
11269 if (mData->mMachineState == MachineState_SettingUp)
11270 i_setMachineState(oldState);
11271
11272 /* unlock all hard disks we locked when there is no VM */
11273 if (!aOnline)
11274 {
11275 ErrorInfoKeeper eik;
11276
11277 HRESULT rc1 = lockedMediaMap->Clear();
11278 AssertComRC(rc1);
11279 }
11280
11281 return rc;
11282}
11283
11284
11285/**
11286 * Looks through the given list of media attachments for one with the given parameters
11287 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11288 * can be searched as well if needed.
11289 *
11290 * @param ll
11291 * @param aControllerName
11292 * @param aControllerPort
11293 * @param aDevice
11294 * @return
11295 */
11296MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11297 const Utf8Str &aControllerName,
11298 LONG aControllerPort,
11299 LONG aDevice)
11300{
11301 for (MediumAttachmentList::const_iterator
11302 it = ll.begin();
11303 it != ll.end();
11304 ++it)
11305 {
11306 MediumAttachment *pAttach = *it;
11307 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11308 return pAttach;
11309 }
11310
11311 return NULL;
11312}
11313
11314/**
11315 * Looks through the given list of media attachments for one with the given parameters
11316 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11317 * can be searched as well if needed.
11318 *
11319 * @param ll
11320 * @param pMedium
11321 * @return
11322 */
11323MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11324 ComObjPtr<Medium> pMedium)
11325{
11326 for (MediumAttachmentList::const_iterator
11327 it = ll.begin();
11328 it != ll.end();
11329 ++it)
11330 {
11331 MediumAttachment *pAttach = *it;
11332 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11333 if (pMediumThis == pMedium)
11334 return pAttach;
11335 }
11336
11337 return NULL;
11338}
11339
11340/**
11341 * Looks through the given list of media attachments for one with the given parameters
11342 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11343 * can be searched as well if needed.
11344 *
11345 * @param ll
11346 * @param id
11347 * @return
11348 */
11349MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11350 Guid &id)
11351{
11352 for (MediumAttachmentList::const_iterator
11353 it = ll.begin();
11354 it != ll.end();
11355 ++it)
11356 {
11357 MediumAttachment *pAttach = *it;
11358 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11359 if (pMediumThis->i_getId() == id)
11360 return pAttach;
11361 }
11362
11363 return NULL;
11364}
11365
11366/**
11367 * Main implementation for Machine::DetachDevice. This also gets called
11368 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11369 *
11370 * @param pAttach Medium attachment to detach.
11371 * @param writeLock Machine write lock which the caller must have locked once.
11372 * This may be released temporarily in here.
11373 * @param pSnapshot If NULL, then the detachment is for the current machine.
11374 * Otherwise this is for a SnapshotMachine, and this must be
11375 * its snapshot.
11376 * @return
11377 */
11378HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11379 AutoWriteLock &writeLock,
11380 Snapshot *pSnapshot)
11381{
11382 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11383 DeviceType_T mediumType = pAttach->i_getType();
11384
11385 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11386
11387 if (pAttach->i_isImplicit())
11388 {
11389 /* attempt to implicitly delete the implicitly created diff */
11390
11391 /// @todo move the implicit flag from MediumAttachment to Medium
11392 /// and forbid any hard disk operation when it is implicit. Or maybe
11393 /// a special media state for it to make it even more simple.
11394
11395 Assert(mMediumAttachments.isBackedUp());
11396
11397 /* will release the lock before the potentially lengthy operation, so
11398 * protect with the special state */
11399 MachineState_T oldState = mData->mMachineState;
11400 i_setMachineState(MachineState_SettingUp);
11401
11402 writeLock.release();
11403
11404 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11405 true /*aWait*/);
11406
11407 writeLock.acquire();
11408
11409 i_setMachineState(oldState);
11410
11411 if (FAILED(rc)) return rc;
11412 }
11413
11414 i_setModified(IsModified_Storage);
11415 mMediumAttachments.backup();
11416 mMediumAttachments->remove(pAttach);
11417
11418 if (!oldmedium.isNull())
11419 {
11420 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11421 if (pSnapshot)
11422 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11423 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11424 else if (mediumType != DeviceType_HardDisk)
11425 oldmedium->i_removeBackReference(mData->mUuid);
11426 }
11427
11428 return S_OK;
11429}
11430
11431/**
11432 * Goes thru all media of the given list and
11433 *
11434 * 1) calls i_detachDevice() on each of them for this machine and
11435 * 2) adds all Medium objects found in the process to the given list,
11436 * depending on cleanupMode.
11437 *
11438 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11439 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11440 * media to the list.
11441 *
11442 * This gets called from Machine::Unregister, both for the actual Machine and
11443 * the SnapshotMachine objects that might be found in the snapshots.
11444 *
11445 * Requires caller and locking. The machine lock must be passed in because it
11446 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11447 *
11448 * @param writeLock Machine lock from top-level caller; this gets passed to
11449 * i_detachDevice.
11450 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11451 * object if called for a SnapshotMachine.
11452 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11453 * added to llMedia; if Full, then all media get added;
11454 * otherwise no media get added.
11455 * @param llMedia Caller's list to receive Medium objects which got detached so
11456 * caller can close() them, depending on cleanupMode.
11457 * @return
11458 */
11459HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11460 Snapshot *pSnapshot,
11461 CleanupMode_T cleanupMode,
11462 MediaList &llMedia)
11463{
11464 Assert(isWriteLockOnCurrentThread());
11465
11466 HRESULT rc;
11467
11468 // make a temporary list because i_detachDevice invalidates iterators into
11469 // mMediumAttachments
11470 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11471
11472 for (MediumAttachmentList::iterator
11473 it = llAttachments2.begin();
11474 it != llAttachments2.end();
11475 ++it)
11476 {
11477 ComObjPtr<MediumAttachment> &pAttach = *it;
11478 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11479
11480 if (!pMedium.isNull())
11481 {
11482 AutoCaller mac(pMedium);
11483 if (FAILED(mac.rc())) return mac.rc();
11484 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11485 DeviceType_T devType = pMedium->i_getDeviceType();
11486 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11487 && devType == DeviceType_HardDisk)
11488 || (cleanupMode == CleanupMode_Full)
11489 )
11490 {
11491 llMedia.push_back(pMedium);
11492 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11493 /* Not allowed to keep this lock as below we need the parent
11494 * medium lock, and the lock order is parent to child. */
11495 lock.release();
11496 /*
11497 * Search for medias which are not attached to any machine, but
11498 * in the chain to an attached disk. Mediums are only consided
11499 * if they are:
11500 * - have only one child
11501 * - no references to any machines
11502 * - are of normal medium type
11503 */
11504 while (!pParent.isNull())
11505 {
11506 AutoCaller mac1(pParent);
11507 if (FAILED(mac1.rc())) return mac1.rc();
11508 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11509 if (pParent->i_getChildren().size() == 1)
11510 {
11511 if ( pParent->i_getMachineBackRefCount() == 0
11512 && pParent->i_getType() == MediumType_Normal
11513 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11514 llMedia.push_back(pParent);
11515 }
11516 else
11517 break;
11518 pParent = pParent->i_getParent();
11519 }
11520 }
11521 }
11522
11523 // real machine: then we need to use the proper method
11524 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11525
11526 if (FAILED(rc))
11527 return rc;
11528 }
11529
11530 return S_OK;
11531}
11532
11533/**
11534 * Perform deferred hard disk detachments.
11535 *
11536 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11537 * changed (not backed up).
11538 *
11539 * If @a aOnline is @c true then this method will also unlock the old hard
11540 * disks for which the new implicit diffs were created and will lock these new
11541 * diffs for writing.
11542 *
11543 * @param aOnline Whether the VM was online prior to this operation.
11544 *
11545 * @note Locks this object for writing!
11546 */
11547void Machine::i_commitMedia(bool aOnline /*= false*/)
11548{
11549 AutoCaller autoCaller(this);
11550 AssertComRCReturnVoid(autoCaller.rc());
11551
11552 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11553
11554 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11555
11556 HRESULT rc = S_OK;
11557
11558 /* no attach/detach operations -- nothing to do */
11559 if (!mMediumAttachments.isBackedUp())
11560 return;
11561
11562 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11563 bool fMediaNeedsLocking = false;
11564
11565 /* enumerate new attachments */
11566 for (MediumAttachmentList::const_iterator
11567 it = mMediumAttachments->begin();
11568 it != mMediumAttachments->end();
11569 ++it)
11570 {
11571 MediumAttachment *pAttach = *it;
11572
11573 pAttach->i_commit();
11574
11575 Medium *pMedium = pAttach->i_getMedium();
11576 bool fImplicit = pAttach->i_isImplicit();
11577
11578 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11579 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11580 fImplicit));
11581
11582 /** @todo convert all this Machine-based voodoo to MediumAttachment
11583 * based commit logic. */
11584 if (fImplicit)
11585 {
11586 /* convert implicit attachment to normal */
11587 pAttach->i_setImplicit(false);
11588
11589 if ( aOnline
11590 && pMedium
11591 && pAttach->i_getType() == DeviceType_HardDisk
11592 )
11593 {
11594 /* update the appropriate lock list */
11595 MediumLockList *pMediumLockList;
11596 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11597 AssertComRC(rc);
11598 if (pMediumLockList)
11599 {
11600 /* unlock if there's a need to change the locking */
11601 if (!fMediaNeedsLocking)
11602 {
11603 rc = mData->mSession.mLockedMedia.Unlock();
11604 AssertComRC(rc);
11605 fMediaNeedsLocking = true;
11606 }
11607 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11608 AssertComRC(rc);
11609 rc = pMediumLockList->Append(pMedium, true);
11610 AssertComRC(rc);
11611 }
11612 }
11613
11614 continue;
11615 }
11616
11617 if (pMedium)
11618 {
11619 /* was this medium attached before? */
11620 for (MediumAttachmentList::iterator
11621 oldIt = oldAtts.begin();
11622 oldIt != oldAtts.end();
11623 ++oldIt)
11624 {
11625 MediumAttachment *pOldAttach = *oldIt;
11626 if (pOldAttach->i_getMedium() == pMedium)
11627 {
11628 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11629
11630 /* yes: remove from old to avoid de-association */
11631 oldAtts.erase(oldIt);
11632 break;
11633 }
11634 }
11635 }
11636 }
11637
11638 /* enumerate remaining old attachments and de-associate from the
11639 * current machine state */
11640 for (MediumAttachmentList::const_iterator
11641 it = oldAtts.begin();
11642 it != oldAtts.end();
11643 ++it)
11644 {
11645 MediumAttachment *pAttach = *it;
11646 Medium *pMedium = pAttach->i_getMedium();
11647
11648 /* Detach only hard disks, since DVD/floppy media is detached
11649 * instantly in MountMedium. */
11650 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11651 {
11652 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11653
11654 /* now de-associate from the current machine state */
11655 rc = pMedium->i_removeBackReference(mData->mUuid);
11656 AssertComRC(rc);
11657
11658 if (aOnline)
11659 {
11660 /* unlock since medium is not used anymore */
11661 MediumLockList *pMediumLockList;
11662 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11663 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11664 {
11665 /* this happens for online snapshots, there the attachment
11666 * is changing, but only to a diff image created under
11667 * the old one, so there is no separate lock list */
11668 Assert(!pMediumLockList);
11669 }
11670 else
11671 {
11672 AssertComRC(rc);
11673 if (pMediumLockList)
11674 {
11675 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11676 AssertComRC(rc);
11677 }
11678 }
11679 }
11680 }
11681 }
11682
11683 /* take media locks again so that the locking state is consistent */
11684 if (fMediaNeedsLocking)
11685 {
11686 Assert(aOnline);
11687 rc = mData->mSession.mLockedMedia.Lock();
11688 AssertComRC(rc);
11689 }
11690
11691 /* commit the hard disk changes */
11692 mMediumAttachments.commit();
11693
11694 if (i_isSessionMachine())
11695 {
11696 /*
11697 * Update the parent machine to point to the new owner.
11698 * This is necessary because the stored parent will point to the
11699 * session machine otherwise and cause crashes or errors later
11700 * when the session machine gets invalid.
11701 */
11702 /** @todo Change the MediumAttachment class to behave like any other
11703 * class in this regard by creating peer MediumAttachment
11704 * objects for session machines and share the data with the peer
11705 * machine.
11706 */
11707 for (MediumAttachmentList::const_iterator
11708 it = mMediumAttachments->begin();
11709 it != mMediumAttachments->end();
11710 ++it)
11711 (*it)->i_updateParentMachine(mPeer);
11712
11713 /* attach new data to the primary machine and reshare it */
11714 mPeer->mMediumAttachments.attach(mMediumAttachments);
11715 }
11716
11717 return;
11718}
11719
11720/**
11721 * Perform deferred deletion of implicitly created diffs.
11722 *
11723 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11724 * changed (not backed up).
11725 *
11726 * @note Locks this object for writing!
11727 */
11728void Machine::i_rollbackMedia()
11729{
11730 AutoCaller autoCaller(this);
11731 AssertComRCReturnVoid(autoCaller.rc());
11732
11733 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11734 LogFlowThisFunc(("Entering rollbackMedia\n"));
11735
11736 HRESULT rc = S_OK;
11737
11738 /* no attach/detach operations -- nothing to do */
11739 if (!mMediumAttachments.isBackedUp())
11740 return;
11741
11742 /* enumerate new attachments */
11743 for (MediumAttachmentList::const_iterator
11744 it = mMediumAttachments->begin();
11745 it != mMediumAttachments->end();
11746 ++it)
11747 {
11748 MediumAttachment *pAttach = *it;
11749 /* Fix up the backrefs for DVD/floppy media. */
11750 if (pAttach->i_getType() != DeviceType_HardDisk)
11751 {
11752 Medium *pMedium = pAttach->i_getMedium();
11753 if (pMedium)
11754 {
11755 rc = pMedium->i_removeBackReference(mData->mUuid);
11756 AssertComRC(rc);
11757 }
11758 }
11759
11760 (*it)->i_rollback();
11761
11762 pAttach = *it;
11763 /* Fix up the backrefs for DVD/floppy media. */
11764 if (pAttach->i_getType() != DeviceType_HardDisk)
11765 {
11766 Medium *pMedium = pAttach->i_getMedium();
11767 if (pMedium)
11768 {
11769 rc = pMedium->i_addBackReference(mData->mUuid);
11770 AssertComRC(rc);
11771 }
11772 }
11773 }
11774
11775 /** @todo convert all this Machine-based voodoo to MediumAttachment
11776 * based rollback logic. */
11777 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11778
11779 return;
11780}
11781
11782/**
11783 * Returns true if the settings file is located in the directory named exactly
11784 * as the machine; this means, among other things, that the machine directory
11785 * should be auto-renamed.
11786 *
11787 * @param aSettingsDir if not NULL, the full machine settings file directory
11788 * name will be assigned there.
11789 *
11790 * @note Doesn't lock anything.
11791 * @note Not thread safe (must be called from this object's lock).
11792 */
11793bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11794{
11795 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11796 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11797 if (aSettingsDir)
11798 *aSettingsDir = strMachineDirName;
11799 strMachineDirName.stripPath(); // vmname
11800 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11801 strConfigFileOnly.stripPath() // vmname.vbox
11802 .stripSuffix(); // vmname
11803 /** @todo hack, make somehow use of ComposeMachineFilename */
11804 if (mUserData->s.fDirectoryIncludesUUID)
11805 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11806
11807 AssertReturn(!strMachineDirName.isEmpty(), false);
11808 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11809
11810 return strMachineDirName == strConfigFileOnly;
11811}
11812
11813/**
11814 * Discards all changes to machine settings.
11815 *
11816 * @param aNotify Whether to notify the direct session about changes or not.
11817 *
11818 * @note Locks objects for writing!
11819 */
11820void Machine::i_rollback(bool aNotify)
11821{
11822 AutoCaller autoCaller(this);
11823 AssertComRCReturn(autoCaller.rc(), (void)0);
11824
11825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11826
11827 if (!mStorageControllers.isNull())
11828 {
11829 if (mStorageControllers.isBackedUp())
11830 {
11831 /* unitialize all new devices (absent in the backed up list). */
11832 StorageControllerList *backedList = mStorageControllers.backedUpData();
11833 for (StorageControllerList::const_iterator
11834 it = mStorageControllers->begin();
11835 it != mStorageControllers->end();
11836 ++it)
11837 {
11838 if ( std::find(backedList->begin(), backedList->end(), *it)
11839 == backedList->end()
11840 )
11841 {
11842 (*it)->uninit();
11843 }
11844 }
11845
11846 /* restore the list */
11847 mStorageControllers.rollback();
11848 }
11849
11850 /* rollback any changes to devices after restoring the list */
11851 if (mData->flModifications & IsModified_Storage)
11852 {
11853 for (StorageControllerList::const_iterator
11854 it = mStorageControllers->begin();
11855 it != mStorageControllers->end();
11856 ++it)
11857 {
11858 (*it)->i_rollback();
11859 }
11860 }
11861 }
11862
11863 if (!mUSBControllers.isNull())
11864 {
11865 if (mUSBControllers.isBackedUp())
11866 {
11867 /* unitialize all new devices (absent in the backed up list). */
11868 USBControllerList *backedList = mUSBControllers.backedUpData();
11869 for (USBControllerList::const_iterator
11870 it = mUSBControllers->begin();
11871 it != mUSBControllers->end();
11872 ++it)
11873 {
11874 if ( std::find(backedList->begin(), backedList->end(), *it)
11875 == backedList->end()
11876 )
11877 {
11878 (*it)->uninit();
11879 }
11880 }
11881
11882 /* restore the list */
11883 mUSBControllers.rollback();
11884 }
11885
11886 /* rollback any changes to devices after restoring the list */
11887 if (mData->flModifications & IsModified_USB)
11888 {
11889 for (USBControllerList::const_iterator
11890 it = mUSBControllers->begin();
11891 it != mUSBControllers->end();
11892 ++it)
11893 {
11894 (*it)->i_rollback();
11895 }
11896 }
11897 }
11898
11899 mUserData.rollback();
11900
11901 mHWData.rollback();
11902
11903 if (mData->flModifications & IsModified_Storage)
11904 i_rollbackMedia();
11905
11906 if (mBIOSSettings)
11907 mBIOSSettings->i_rollback();
11908
11909 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11910 mVRDEServer->i_rollback();
11911
11912 if (mAudioAdapter)
11913 mAudioAdapter->i_rollback();
11914
11915 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11916 mUSBDeviceFilters->i_rollback();
11917
11918 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11919 mBandwidthControl->i_rollback();
11920
11921 if (!mHWData.isNull())
11922 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11923 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11924 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11925 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11926
11927 if (mData->flModifications & IsModified_NetworkAdapters)
11928 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11929 if ( mNetworkAdapters[slot]
11930 && mNetworkAdapters[slot]->i_isModified())
11931 {
11932 mNetworkAdapters[slot]->i_rollback();
11933 networkAdapters[slot] = mNetworkAdapters[slot];
11934 }
11935
11936 if (mData->flModifications & IsModified_SerialPorts)
11937 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11938 if ( mSerialPorts[slot]
11939 && mSerialPorts[slot]->i_isModified())
11940 {
11941 mSerialPorts[slot]->i_rollback();
11942 serialPorts[slot] = mSerialPorts[slot];
11943 }
11944
11945 if (mData->flModifications & IsModified_ParallelPorts)
11946 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11947 if ( mParallelPorts[slot]
11948 && mParallelPorts[slot]->i_isModified())
11949 {
11950 mParallelPorts[slot]->i_rollback();
11951 parallelPorts[slot] = mParallelPorts[slot];
11952 }
11953
11954 if (aNotify)
11955 {
11956 /* inform the direct session about changes */
11957
11958 ComObjPtr<Machine> that = this;
11959 uint32_t flModifications = mData->flModifications;
11960 alock.release();
11961
11962 if (flModifications & IsModified_SharedFolders)
11963 that->i_onSharedFolderChange();
11964
11965 if (flModifications & IsModified_VRDEServer)
11966 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11967 if (flModifications & IsModified_USB)
11968 that->i_onUSBControllerChange();
11969
11970 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11971 if (networkAdapters[slot])
11972 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11973 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11974 if (serialPorts[slot])
11975 that->i_onSerialPortChange(serialPorts[slot]);
11976 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11977 if (parallelPorts[slot])
11978 that->i_onParallelPortChange(parallelPorts[slot]);
11979
11980 if (flModifications & IsModified_Storage)
11981 that->i_onStorageControllerChange();
11982
11983#if 0
11984 if (flModifications & IsModified_BandwidthControl)
11985 that->onBandwidthControlChange();
11986#endif
11987 }
11988}
11989
11990/**
11991 * Commits all the changes to machine settings.
11992 *
11993 * Note that this operation is supposed to never fail.
11994 *
11995 * @note Locks this object and children for writing.
11996 */
11997void Machine::i_commit()
11998{
11999 AutoCaller autoCaller(this);
12000 AssertComRCReturnVoid(autoCaller.rc());
12001
12002 AutoCaller peerCaller(mPeer);
12003 AssertComRCReturnVoid(peerCaller.rc());
12004
12005 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12006
12007 /*
12008 * use safe commit to ensure Snapshot machines (that share mUserData)
12009 * will still refer to a valid memory location
12010 */
12011 mUserData.commitCopy();
12012
12013 mHWData.commit();
12014
12015 if (mMediumAttachments.isBackedUp())
12016 i_commitMedia(Global::IsOnline(mData->mMachineState));
12017
12018 mBIOSSettings->i_commit();
12019 mVRDEServer->i_commit();
12020 mAudioAdapter->i_commit();
12021 mUSBDeviceFilters->i_commit();
12022 mBandwidthControl->i_commit();
12023
12024 /* Since mNetworkAdapters is a list which might have been changed (resized)
12025 * without using the Backupable<> template we need to handle the copying
12026 * of the list entries manually, including the creation of peers for the
12027 * new objects. */
12028 bool commitNetworkAdapters = false;
12029 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12030 if (mPeer)
12031 {
12032 /* commit everything, even the ones which will go away */
12033 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12034 mNetworkAdapters[slot]->i_commit();
12035 /* copy over the new entries, creating a peer and uninit the original */
12036 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12037 for (size_t slot = 0; slot < newSize; slot++)
12038 {
12039 /* look if this adapter has a peer device */
12040 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12041 if (!peer)
12042 {
12043 /* no peer means the adapter is a newly created one;
12044 * create a peer owning data this data share it with */
12045 peer.createObject();
12046 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12047 }
12048 mPeer->mNetworkAdapters[slot] = peer;
12049 }
12050 /* uninit any no longer needed network adapters */
12051 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12052 mNetworkAdapters[slot]->uninit();
12053 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12054 {
12055 if (mPeer->mNetworkAdapters[slot])
12056 mPeer->mNetworkAdapters[slot]->uninit();
12057 }
12058 /* Keep the original network adapter count until this point, so that
12059 * discarding a chipset type change will not lose settings. */
12060 mNetworkAdapters.resize(newSize);
12061 mPeer->mNetworkAdapters.resize(newSize);
12062 }
12063 else
12064 {
12065 /* we have no peer (our parent is the newly created machine);
12066 * just commit changes to the network adapters */
12067 commitNetworkAdapters = true;
12068 }
12069 if (commitNetworkAdapters)
12070 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12071 mNetworkAdapters[slot]->i_commit();
12072
12073 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12074 mSerialPorts[slot]->i_commit();
12075 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12076 mParallelPorts[slot]->i_commit();
12077
12078 bool commitStorageControllers = false;
12079
12080 if (mStorageControllers.isBackedUp())
12081 {
12082 mStorageControllers.commit();
12083
12084 if (mPeer)
12085 {
12086 /* Commit all changes to new controllers (this will reshare data with
12087 * peers for those who have peers) */
12088 StorageControllerList *newList = new StorageControllerList();
12089 for (StorageControllerList::const_iterator
12090 it = mStorageControllers->begin();
12091 it != mStorageControllers->end();
12092 ++it)
12093 {
12094 (*it)->i_commit();
12095
12096 /* look if this controller has a peer device */
12097 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12098 if (!peer)
12099 {
12100 /* no peer means the device is a newly created one;
12101 * create a peer owning data this device share it with */
12102 peer.createObject();
12103 peer->init(mPeer, *it, true /* aReshare */);
12104 }
12105 else
12106 {
12107 /* remove peer from the old list */
12108 mPeer->mStorageControllers->remove(peer);
12109 }
12110 /* and add it to the new list */
12111 newList->push_back(peer);
12112 }
12113
12114 /* uninit old peer's controllers that are left */
12115 for (StorageControllerList::const_iterator
12116 it = mPeer->mStorageControllers->begin();
12117 it != mPeer->mStorageControllers->end();
12118 ++it)
12119 {
12120 (*it)->uninit();
12121 }
12122
12123 /* attach new list of controllers to our peer */
12124 mPeer->mStorageControllers.attach(newList);
12125 }
12126 else
12127 {
12128 /* we have no peer (our parent is the newly created machine);
12129 * just commit changes to devices */
12130 commitStorageControllers = true;
12131 }
12132 }
12133 else
12134 {
12135 /* the list of controllers itself is not changed,
12136 * just commit changes to controllers themselves */
12137 commitStorageControllers = true;
12138 }
12139
12140 if (commitStorageControllers)
12141 {
12142 for (StorageControllerList::const_iterator
12143 it = mStorageControllers->begin();
12144 it != mStorageControllers->end();
12145 ++it)
12146 {
12147 (*it)->i_commit();
12148 }
12149 }
12150
12151 bool commitUSBControllers = false;
12152
12153 if (mUSBControllers.isBackedUp())
12154 {
12155 mUSBControllers.commit();
12156
12157 if (mPeer)
12158 {
12159 /* Commit all changes to new controllers (this will reshare data with
12160 * peers for those who have peers) */
12161 USBControllerList *newList = new USBControllerList();
12162 for (USBControllerList::const_iterator
12163 it = mUSBControllers->begin();
12164 it != mUSBControllers->end();
12165 ++it)
12166 {
12167 (*it)->i_commit();
12168
12169 /* look if this controller has a peer device */
12170 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12171 if (!peer)
12172 {
12173 /* no peer means the device is a newly created one;
12174 * create a peer owning data this device share it with */
12175 peer.createObject();
12176 peer->init(mPeer, *it, true /* aReshare */);
12177 }
12178 else
12179 {
12180 /* remove peer from the old list */
12181 mPeer->mUSBControllers->remove(peer);
12182 }
12183 /* and add it to the new list */
12184 newList->push_back(peer);
12185 }
12186
12187 /* uninit old peer's controllers that are left */
12188 for (USBControllerList::const_iterator
12189 it = mPeer->mUSBControllers->begin();
12190 it != mPeer->mUSBControllers->end();
12191 ++it)
12192 {
12193 (*it)->uninit();
12194 }
12195
12196 /* attach new list of controllers to our peer */
12197 mPeer->mUSBControllers.attach(newList);
12198 }
12199 else
12200 {
12201 /* we have no peer (our parent is the newly created machine);
12202 * just commit changes to devices */
12203 commitUSBControllers = true;
12204 }
12205 }
12206 else
12207 {
12208 /* the list of controllers itself is not changed,
12209 * just commit changes to controllers themselves */
12210 commitUSBControllers = true;
12211 }
12212
12213 if (commitUSBControllers)
12214 {
12215 for (USBControllerList::const_iterator
12216 it = mUSBControllers->begin();
12217 it != mUSBControllers->end();
12218 ++it)
12219 {
12220 (*it)->i_commit();
12221 }
12222 }
12223
12224 if (i_isSessionMachine())
12225 {
12226 /* attach new data to the primary machine and reshare it */
12227 mPeer->mUserData.attach(mUserData);
12228 mPeer->mHWData.attach(mHWData);
12229 /* mmMediumAttachments is reshared by fixupMedia */
12230 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12231 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12232 }
12233}
12234
12235/**
12236 * Copies all the hardware data from the given machine.
12237 *
12238 * Currently, only called when the VM is being restored from a snapshot. In
12239 * particular, this implies that the VM is not running during this method's
12240 * call.
12241 *
12242 * @note This method must be called from under this object's lock.
12243 *
12244 * @note This method doesn't call #i_commit(), so all data remains backed up and
12245 * unsaved.
12246 */
12247void Machine::i_copyFrom(Machine *aThat)
12248{
12249 AssertReturnVoid(!i_isSnapshotMachine());
12250 AssertReturnVoid(aThat->i_isSnapshotMachine());
12251
12252 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12253
12254 mHWData.assignCopy(aThat->mHWData);
12255
12256 // create copies of all shared folders (mHWData after attaching a copy
12257 // contains just references to original objects)
12258 for (HWData::SharedFolderList::iterator
12259 it = mHWData->mSharedFolders.begin();
12260 it != mHWData->mSharedFolders.end();
12261 ++it)
12262 {
12263 ComObjPtr<SharedFolder> folder;
12264 folder.createObject();
12265 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12266 AssertComRC(rc);
12267 *it = folder;
12268 }
12269
12270 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12271 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12272 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12273 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12274 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12275
12276 /* create private copies of all controllers */
12277 mStorageControllers.backup();
12278 mStorageControllers->clear();
12279 for (StorageControllerList::const_iterator
12280 it = aThat->mStorageControllers->begin();
12281 it != aThat->mStorageControllers->end();
12282 ++it)
12283 {
12284 ComObjPtr<StorageController> ctrl;
12285 ctrl.createObject();
12286 ctrl->initCopy(this, *it);
12287 mStorageControllers->push_back(ctrl);
12288 }
12289
12290 /* create private copies of all USB controllers */
12291 mUSBControllers.backup();
12292 mUSBControllers->clear();
12293 for (USBControllerList::const_iterator
12294 it = aThat->mUSBControllers->begin();
12295 it != aThat->mUSBControllers->end();
12296 ++it)
12297 {
12298 ComObjPtr<USBController> ctrl;
12299 ctrl.createObject();
12300 ctrl->initCopy(this, *it);
12301 mUSBControllers->push_back(ctrl);
12302 }
12303
12304 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12305 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12306 {
12307 if (mNetworkAdapters[slot].isNotNull())
12308 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12309 else
12310 {
12311 unconst(mNetworkAdapters[slot]).createObject();
12312 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12313 }
12314 }
12315 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12316 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12317 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12318 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12319}
12320
12321/**
12322 * Returns whether the given storage controller is hotplug capable.
12323 *
12324 * @returns true if the controller supports hotplugging
12325 * false otherwise.
12326 * @param enmCtrlType The controller type to check for.
12327 */
12328bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12329{
12330 ComPtr<ISystemProperties> systemProperties;
12331 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12332 if (FAILED(rc))
12333 return false;
12334
12335 BOOL aHotplugCapable = FALSE;
12336 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12337
12338 return RT_BOOL(aHotplugCapable);
12339}
12340
12341#ifdef VBOX_WITH_RESOURCE_USAGE_API
12342
12343void Machine::i_getDiskList(MediaList &list)
12344{
12345 for (MediumAttachmentList::const_iterator
12346 it = mMediumAttachments->begin();
12347 it != mMediumAttachments->end();
12348 ++it)
12349 {
12350 MediumAttachment *pAttach = *it;
12351 /* just in case */
12352 AssertContinue(pAttach);
12353
12354 AutoCaller localAutoCallerA(pAttach);
12355 if (FAILED(localAutoCallerA.rc())) continue;
12356
12357 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12358
12359 if (pAttach->i_getType() == DeviceType_HardDisk)
12360 list.push_back(pAttach->i_getMedium());
12361 }
12362}
12363
12364void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12365{
12366 AssertReturnVoid(isWriteLockOnCurrentThread());
12367 AssertPtrReturnVoid(aCollector);
12368
12369 pm::CollectorHAL *hal = aCollector->getHAL();
12370 /* Create sub metrics */
12371 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12372 "Percentage of processor time spent in user mode by the VM process.");
12373 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12374 "Percentage of processor time spent in kernel mode by the VM process.");
12375 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12376 "Size of resident portion of VM process in memory.");
12377 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12378 "Actual size of all VM disks combined.");
12379 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12380 "Network receive rate.");
12381 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12382 "Network transmit rate.");
12383 /* Create and register base metrics */
12384 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12385 cpuLoadUser, cpuLoadKernel);
12386 aCollector->registerBaseMetric(cpuLoad);
12387 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12388 ramUsageUsed);
12389 aCollector->registerBaseMetric(ramUsage);
12390 MediaList disks;
12391 i_getDiskList(disks);
12392 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12393 diskUsageUsed);
12394 aCollector->registerBaseMetric(diskUsage);
12395
12396 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12397 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12398 new pm::AggregateAvg()));
12399 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12400 new pm::AggregateMin()));
12401 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12402 new pm::AggregateMax()));
12403 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12404 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12405 new pm::AggregateAvg()));
12406 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12407 new pm::AggregateMin()));
12408 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12409 new pm::AggregateMax()));
12410
12411 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12412 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12413 new pm::AggregateAvg()));
12414 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12415 new pm::AggregateMin()));
12416 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12417 new pm::AggregateMax()));
12418
12419 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12420 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12421 new pm::AggregateAvg()));
12422 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12423 new pm::AggregateMin()));
12424 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12425 new pm::AggregateMax()));
12426
12427
12428 /* Guest metrics collector */
12429 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12430 aCollector->registerGuest(mCollectorGuest);
12431 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12432
12433 /* Create sub metrics */
12434 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12435 "Percentage of processor time spent in user mode as seen by the guest.");
12436 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12437 "Percentage of processor time spent in kernel mode as seen by the guest.");
12438 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12439 "Percentage of processor time spent idling as seen by the guest.");
12440
12441 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12442 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12443 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12444 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12445 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12446 pm::SubMetric *guestMemCache = new pm::SubMetric(
12447 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12448
12449 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12450 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12451
12452 /* Create and register base metrics */
12453 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12454 machineNetRx, machineNetTx);
12455 aCollector->registerBaseMetric(machineNetRate);
12456
12457 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12458 guestLoadUser, guestLoadKernel, guestLoadIdle);
12459 aCollector->registerBaseMetric(guestCpuLoad);
12460
12461 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12462 guestMemTotal, guestMemFree,
12463 guestMemBalloon, guestMemShared,
12464 guestMemCache, guestPagedTotal);
12465 aCollector->registerBaseMetric(guestCpuMem);
12466
12467 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12468 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12469 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12470 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12471
12472 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12473 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12474 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12475 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12476
12477 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12478 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12479 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12480 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12481
12482 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12483 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12484 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12485 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12486
12487 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12488 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12489 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12490 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12491
12492 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12493 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12494 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12495 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12496
12497 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12498 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12499 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12500 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12501
12502 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12503 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12504 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12505 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12506
12507 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12508 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12509 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12510 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12511
12512 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12513 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12514 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12515 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12516
12517 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12518 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12519 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12520 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12521}
12522
12523void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12524{
12525 AssertReturnVoid(isWriteLockOnCurrentThread());
12526
12527 if (aCollector)
12528 {
12529 aCollector->unregisterMetricsFor(aMachine);
12530 aCollector->unregisterBaseMetricsFor(aMachine);
12531 }
12532}
12533
12534#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12535
12536
12537////////////////////////////////////////////////////////////////////////////////
12538
12539DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12540
12541HRESULT SessionMachine::FinalConstruct()
12542{
12543 LogFlowThisFunc(("\n"));
12544
12545 mClientToken = NULL;
12546
12547 return BaseFinalConstruct();
12548}
12549
12550void SessionMachine::FinalRelease()
12551{
12552 LogFlowThisFunc(("\n"));
12553
12554 Assert(!mClientToken);
12555 /* paranoia, should not hang around any more */
12556 if (mClientToken)
12557 {
12558 delete mClientToken;
12559 mClientToken = NULL;
12560 }
12561
12562 uninit(Uninit::Unexpected);
12563
12564 BaseFinalRelease();
12565}
12566
12567/**
12568 * @note Must be called only by Machine::LockMachine() from its own write lock.
12569 */
12570HRESULT SessionMachine::init(Machine *aMachine)
12571{
12572 LogFlowThisFuncEnter();
12573 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12574
12575 AssertReturn(aMachine, E_INVALIDARG);
12576
12577 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12578
12579 /* Enclose the state transition NotReady->InInit->Ready */
12580 AutoInitSpan autoInitSpan(this);
12581 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12582
12583 HRESULT rc = S_OK;
12584
12585 RT_ZERO(mAuthLibCtx);
12586
12587 /* create the machine client token */
12588 try
12589 {
12590 mClientToken = new ClientToken(aMachine, this);
12591 if (!mClientToken->isReady())
12592 {
12593 delete mClientToken;
12594 mClientToken = NULL;
12595 rc = E_FAIL;
12596 }
12597 }
12598 catch (std::bad_alloc &)
12599 {
12600 rc = E_OUTOFMEMORY;
12601 }
12602 if (FAILED(rc))
12603 return rc;
12604
12605 /* memorize the peer Machine */
12606 unconst(mPeer) = aMachine;
12607 /* share the parent pointer */
12608 unconst(mParent) = aMachine->mParent;
12609
12610 /* take the pointers to data to share */
12611 mData.share(aMachine->mData);
12612 mSSData.share(aMachine->mSSData);
12613
12614 mUserData.share(aMachine->mUserData);
12615 mHWData.share(aMachine->mHWData);
12616 mMediumAttachments.share(aMachine->mMediumAttachments);
12617
12618 mStorageControllers.allocate();
12619 for (StorageControllerList::const_iterator
12620 it = aMachine->mStorageControllers->begin();
12621 it != aMachine->mStorageControllers->end();
12622 ++it)
12623 {
12624 ComObjPtr<StorageController> ctl;
12625 ctl.createObject();
12626 ctl->init(this, *it);
12627 mStorageControllers->push_back(ctl);
12628 }
12629
12630 mUSBControllers.allocate();
12631 for (USBControllerList::const_iterator
12632 it = aMachine->mUSBControllers->begin();
12633 it != aMachine->mUSBControllers->end();
12634 ++it)
12635 {
12636 ComObjPtr<USBController> ctl;
12637 ctl.createObject();
12638 ctl->init(this, *it);
12639 mUSBControllers->push_back(ctl);
12640 }
12641
12642 unconst(mBIOSSettings).createObject();
12643 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12644 /* create another VRDEServer object that will be mutable */
12645 unconst(mVRDEServer).createObject();
12646 mVRDEServer->init(this, aMachine->mVRDEServer);
12647 /* create another audio adapter object that will be mutable */
12648 unconst(mAudioAdapter).createObject();
12649 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12650 /* create a list of serial ports that will be mutable */
12651 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12652 {
12653 unconst(mSerialPorts[slot]).createObject();
12654 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12655 }
12656 /* create a list of parallel ports that will be mutable */
12657 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12658 {
12659 unconst(mParallelPorts[slot]).createObject();
12660 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12661 }
12662
12663 /* create another USB device filters object that will be mutable */
12664 unconst(mUSBDeviceFilters).createObject();
12665 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12666
12667 /* create a list of network adapters that will be mutable */
12668 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12669 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12670 {
12671 unconst(mNetworkAdapters[slot]).createObject();
12672 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12673 }
12674
12675 /* create another bandwidth control object that will be mutable */
12676 unconst(mBandwidthControl).createObject();
12677 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12678
12679 /* default is to delete saved state on Saved -> PoweredOff transition */
12680 mRemoveSavedState = true;
12681
12682 /* Confirm a successful initialization when it's the case */
12683 autoInitSpan.setSucceeded();
12684
12685 miNATNetworksStarted = 0;
12686
12687 LogFlowThisFuncLeave();
12688 return rc;
12689}
12690
12691/**
12692 * Uninitializes this session object. If the reason is other than
12693 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12694 * or the client watcher code.
12695 *
12696 * @param aReason uninitialization reason
12697 *
12698 * @note Locks mParent + this object for writing.
12699 */
12700void SessionMachine::uninit(Uninit::Reason aReason)
12701{
12702 LogFlowThisFuncEnter();
12703 LogFlowThisFunc(("reason=%d\n", aReason));
12704
12705 /*
12706 * Strongly reference ourselves to prevent this object deletion after
12707 * mData->mSession.mMachine.setNull() below (which can release the last
12708 * reference and call the destructor). Important: this must be done before
12709 * accessing any members (and before AutoUninitSpan that does it as well).
12710 * This self reference will be released as the very last step on return.
12711 */
12712 ComObjPtr<SessionMachine> selfRef;
12713 if (aReason != Uninit::Unexpected)
12714 selfRef = this;
12715
12716 /* Enclose the state transition Ready->InUninit->NotReady */
12717 AutoUninitSpan autoUninitSpan(this);
12718 if (autoUninitSpan.uninitDone())
12719 {
12720 LogFlowThisFunc(("Already uninitialized\n"));
12721 LogFlowThisFuncLeave();
12722 return;
12723 }
12724
12725 if (autoUninitSpan.initFailed())
12726 {
12727 /* We've been called by init() because it's failed. It's not really
12728 * necessary (nor it's safe) to perform the regular uninit sequence
12729 * below, the following is enough.
12730 */
12731 LogFlowThisFunc(("Initialization failed.\n"));
12732 /* destroy the machine client token */
12733 if (mClientToken)
12734 {
12735 delete mClientToken;
12736 mClientToken = NULL;
12737 }
12738 uninitDataAndChildObjects();
12739 mData.free();
12740 unconst(mParent) = NULL;
12741 unconst(mPeer) = NULL;
12742 LogFlowThisFuncLeave();
12743 return;
12744 }
12745
12746 MachineState_T lastState;
12747 {
12748 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12749 lastState = mData->mMachineState;
12750 }
12751 NOREF(lastState);
12752
12753#ifdef VBOX_WITH_USB
12754 // release all captured USB devices, but do this before requesting the locks below
12755 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12756 {
12757 /* Console::captureUSBDevices() is called in the VM process only after
12758 * setting the machine state to Starting or Restoring.
12759 * Console::detachAllUSBDevices() will be called upon successful
12760 * termination. So, we need to release USB devices only if there was
12761 * an abnormal termination of a running VM.
12762 *
12763 * This is identical to SessionMachine::DetachAllUSBDevices except
12764 * for the aAbnormal argument. */
12765 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12766 AssertComRC(rc);
12767 NOREF(rc);
12768
12769 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12770 if (service)
12771 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12772 }
12773#endif /* VBOX_WITH_USB */
12774
12775 // we need to lock this object in uninit() because the lock is shared
12776 // with mPeer (as well as data we modify below). mParent lock is needed
12777 // by several calls to it.
12778 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12779
12780#ifdef VBOX_WITH_RESOURCE_USAGE_API
12781 /*
12782 * It is safe to call Machine::i_unregisterMetrics() here because
12783 * PerformanceCollector::samplerCallback no longer accesses guest methods
12784 * holding the lock.
12785 */
12786 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12787 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12788 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12789 if (mCollectorGuest)
12790 {
12791 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12792 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12793 mCollectorGuest = NULL;
12794 }
12795#endif
12796
12797 if (aReason == Uninit::Abnormal)
12798 {
12799 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12800
12801 /* reset the state to Aborted */
12802 if (mData->mMachineState != MachineState_Aborted)
12803 i_setMachineState(MachineState_Aborted);
12804 }
12805
12806 // any machine settings modified?
12807 if (mData->flModifications)
12808 {
12809 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12810 i_rollback(false /* aNotify */);
12811 }
12812
12813 mData->mSession.mPID = NIL_RTPROCESS;
12814
12815 if (aReason == Uninit::Unexpected)
12816 {
12817 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12818 * client watcher thread to update the set of machines that have open
12819 * sessions. */
12820 mParent->i_updateClientWatcher();
12821 }
12822
12823 /* uninitialize all remote controls */
12824 if (mData->mSession.mRemoteControls.size())
12825 {
12826 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12827 mData->mSession.mRemoteControls.size()));
12828
12829 /* Always restart a the beginning, since the iterator is invalidated
12830 * by using erase(). */
12831 for (Data::Session::RemoteControlList::iterator
12832 it = mData->mSession.mRemoteControls.begin();
12833 it != mData->mSession.mRemoteControls.end();
12834 it = mData->mSession.mRemoteControls.begin())
12835 {
12836 ComPtr<IInternalSessionControl> pControl = *it;
12837 mData->mSession.mRemoteControls.erase(it);
12838 multilock.release();
12839 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12840 HRESULT rc = pControl->Uninitialize();
12841 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12842 if (FAILED(rc))
12843 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12844 multilock.acquire();
12845 }
12846 mData->mSession.mRemoteControls.clear();
12847 }
12848
12849 /* Remove all references to the NAT network service. The service will stop
12850 * if all references (also from other VMs) are removed. */
12851 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12852 {
12853 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12854 {
12855 BOOL enabled;
12856 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12857 if ( FAILED(hrc)
12858 || !enabled)
12859 continue;
12860
12861 NetworkAttachmentType_T type;
12862 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12863 if ( SUCCEEDED(hrc)
12864 && type == NetworkAttachmentType_NATNetwork)
12865 {
12866 Bstr name;
12867 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12868 if (SUCCEEDED(hrc))
12869 {
12870 multilock.release();
12871 Utf8Str strName(name);
12872 LogRel(("VM '%s' stops using NAT network '%s'\n",
12873 mUserData->s.strName.c_str(), strName.c_str()));
12874 mParent->i_natNetworkRefDec(strName);
12875 multilock.acquire();
12876 }
12877 }
12878 }
12879 }
12880
12881 /*
12882 * An expected uninitialization can come only from #i_checkForDeath().
12883 * Otherwise it means that something's gone really wrong (for example,
12884 * the Session implementation has released the VirtualBox reference
12885 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12886 * etc). However, it's also possible, that the client releases the IPC
12887 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12888 * but the VirtualBox release event comes first to the server process.
12889 * This case is practically possible, so we should not assert on an
12890 * unexpected uninit, just log a warning.
12891 */
12892
12893 if (aReason == Uninit::Unexpected)
12894 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12895
12896 if (aReason != Uninit::Normal)
12897 {
12898 mData->mSession.mDirectControl.setNull();
12899 }
12900 else
12901 {
12902 /* this must be null here (see #OnSessionEnd()) */
12903 Assert(mData->mSession.mDirectControl.isNull());
12904 Assert(mData->mSession.mState == SessionState_Unlocking);
12905 Assert(!mData->mSession.mProgress.isNull());
12906 }
12907 if (mData->mSession.mProgress)
12908 {
12909 if (aReason == Uninit::Normal)
12910 mData->mSession.mProgress->i_notifyComplete(S_OK);
12911 else
12912 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12913 COM_IIDOF(ISession),
12914 getComponentName(),
12915 tr("The VM session was aborted"));
12916 mData->mSession.mProgress.setNull();
12917 }
12918
12919 if (mConsoleTaskData.mProgress)
12920 {
12921 Assert(aReason == Uninit::Abnormal);
12922 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12923 COM_IIDOF(ISession),
12924 getComponentName(),
12925 tr("The VM session was aborted"));
12926 mConsoleTaskData.mProgress.setNull();
12927 }
12928
12929 /* remove the association between the peer machine and this session machine */
12930 Assert( (SessionMachine*)mData->mSession.mMachine == this
12931 || aReason == Uninit::Unexpected);
12932
12933 /* reset the rest of session data */
12934 mData->mSession.mLockType = LockType_Null;
12935 mData->mSession.mMachine.setNull();
12936 mData->mSession.mState = SessionState_Unlocked;
12937 mData->mSession.mName.setNull();
12938
12939 /* destroy the machine client token before leaving the exclusive lock */
12940 if (mClientToken)
12941 {
12942 delete mClientToken;
12943 mClientToken = NULL;
12944 }
12945
12946 /* fire an event */
12947 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12948
12949 uninitDataAndChildObjects();
12950
12951 /* free the essential data structure last */
12952 mData.free();
12953
12954 /* release the exclusive lock before setting the below two to NULL */
12955 multilock.release();
12956
12957 unconst(mParent) = NULL;
12958 unconst(mPeer) = NULL;
12959
12960 AuthLibUnload(&mAuthLibCtx);
12961
12962 LogFlowThisFuncLeave();
12963}
12964
12965// util::Lockable interface
12966////////////////////////////////////////////////////////////////////////////////
12967
12968/**
12969 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12970 * with the primary Machine instance (mPeer).
12971 */
12972RWLockHandle *SessionMachine::lockHandle() const
12973{
12974 AssertReturn(mPeer != NULL, NULL);
12975 return mPeer->lockHandle();
12976}
12977
12978// IInternalMachineControl methods
12979////////////////////////////////////////////////////////////////////////////////
12980
12981/**
12982 * Passes collected guest statistics to performance collector object
12983 */
12984HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12985 ULONG aCpuKernel, ULONG aCpuIdle,
12986 ULONG aMemTotal, ULONG aMemFree,
12987 ULONG aMemBalloon, ULONG aMemShared,
12988 ULONG aMemCache, ULONG aPageTotal,
12989 ULONG aAllocVMM, ULONG aFreeVMM,
12990 ULONG aBalloonedVMM, ULONG aSharedVMM,
12991 ULONG aVmNetRx, ULONG aVmNetTx)
12992{
12993#ifdef VBOX_WITH_RESOURCE_USAGE_API
12994 if (mCollectorGuest)
12995 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12996 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12997 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12998 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12999
13000 return S_OK;
13001#else
13002 NOREF(aValidStats);
13003 NOREF(aCpuUser);
13004 NOREF(aCpuKernel);
13005 NOREF(aCpuIdle);
13006 NOREF(aMemTotal);
13007 NOREF(aMemFree);
13008 NOREF(aMemBalloon);
13009 NOREF(aMemShared);
13010 NOREF(aMemCache);
13011 NOREF(aPageTotal);
13012 NOREF(aAllocVMM);
13013 NOREF(aFreeVMM);
13014 NOREF(aBalloonedVMM);
13015 NOREF(aSharedVMM);
13016 NOREF(aVmNetRx);
13017 NOREF(aVmNetTx);
13018 return E_NOTIMPL;
13019#endif
13020}
13021
13022////////////////////////////////////////////////////////////////////////////////
13023//
13024// SessionMachine task records
13025//
13026////////////////////////////////////////////////////////////////////////////////
13027
13028/**
13029 * Task record for saving the machine state.
13030 */
13031class SessionMachine::SaveStateTask
13032 : public Machine::Task
13033{
13034public:
13035 SaveStateTask(SessionMachine *m,
13036 Progress *p,
13037 const Utf8Str &t,
13038 Reason_T enmReason,
13039 const Utf8Str &strStateFilePath)
13040 : Task(m, p, t),
13041 m_enmReason(enmReason),
13042 m_strStateFilePath(strStateFilePath)
13043 {}
13044
13045private:
13046 void handler()
13047 {
13048 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13049 }
13050
13051 Reason_T m_enmReason;
13052 Utf8Str m_strStateFilePath;
13053
13054 friend class SessionMachine;
13055};
13056
13057/**
13058 * Task thread implementation for SessionMachine::SaveState(), called from
13059 * SessionMachine::taskHandler().
13060 *
13061 * @note Locks this object for writing.
13062 *
13063 * @param task
13064 * @return
13065 */
13066void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13067{
13068 LogFlowThisFuncEnter();
13069
13070 AutoCaller autoCaller(this);
13071 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13072 if (FAILED(autoCaller.rc()))
13073 {
13074 /* we might have been uninitialized because the session was accidentally
13075 * closed by the client, so don't assert */
13076 HRESULT rc = setError(E_FAIL,
13077 tr("The session has been accidentally closed"));
13078 task.m_pProgress->i_notifyComplete(rc);
13079 LogFlowThisFuncLeave();
13080 return;
13081 }
13082
13083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13084
13085 HRESULT rc = S_OK;
13086
13087 try
13088 {
13089 ComPtr<IInternalSessionControl> directControl;
13090 if (mData->mSession.mLockType == LockType_VM)
13091 directControl = mData->mSession.mDirectControl;
13092 if (directControl.isNull())
13093 throw setError(VBOX_E_INVALID_VM_STATE,
13094 tr("Trying to save state without a running VM"));
13095 alock.release();
13096 BOOL fSuspendedBySave;
13097 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13098 Assert(!fSuspendedBySave);
13099 alock.acquire();
13100
13101 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13102 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13103 throw E_FAIL);
13104
13105 if (SUCCEEDED(rc))
13106 {
13107 mSSData->strStateFilePath = task.m_strStateFilePath;
13108
13109 /* save all VM settings */
13110 rc = i_saveSettings(NULL);
13111 // no need to check whether VirtualBox.xml needs saving also since
13112 // we can't have a name change pending at this point
13113 }
13114 else
13115 {
13116 // On failure, set the state to the state we had at the beginning.
13117 i_setMachineState(task.m_machineStateBackup);
13118 i_updateMachineStateOnClient();
13119
13120 // Delete the saved state file (might have been already created).
13121 // No need to check whether this is shared with a snapshot here
13122 // because we certainly created a fresh saved state file here.
13123 RTFileDelete(task.m_strStateFilePath.c_str());
13124 }
13125 }
13126 catch (HRESULT aRC) { rc = aRC; }
13127
13128 task.m_pProgress->i_notifyComplete(rc);
13129
13130 LogFlowThisFuncLeave();
13131}
13132
13133/**
13134 * @note Locks this object for writing.
13135 */
13136HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13137{
13138 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13139}
13140
13141HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13142{
13143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13144
13145 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13146 if (FAILED(rc)) return rc;
13147
13148 if ( mData->mMachineState != MachineState_Running
13149 && mData->mMachineState != MachineState_Paused
13150 )
13151 return setError(VBOX_E_INVALID_VM_STATE,
13152 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13153 Global::stringifyMachineState(mData->mMachineState));
13154
13155 ComObjPtr<Progress> pProgress;
13156 pProgress.createObject();
13157 rc = pProgress->init(i_getVirtualBox(),
13158 static_cast<IMachine *>(this) /* aInitiator */,
13159 tr("Saving the execution state of the virtual machine"),
13160 FALSE /* aCancelable */);
13161 if (FAILED(rc))
13162 return rc;
13163
13164 Utf8Str strStateFilePath;
13165 i_composeSavedStateFilename(strStateFilePath);
13166
13167 /* create and start the task on a separate thread (note that it will not
13168 * start working until we release alock) */
13169 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13170 rc = pTask->createThread();
13171 if (FAILED(rc))
13172 return rc;
13173
13174 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13175 i_setMachineState(MachineState_Saving);
13176 i_updateMachineStateOnClient();
13177
13178 pProgress.queryInterfaceTo(aProgress.asOutParam());
13179
13180 return S_OK;
13181}
13182
13183/**
13184 * @note Locks this object for writing.
13185 */
13186HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13187{
13188 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13189
13190 HRESULT rc = i_checkStateDependency(MutableStateDep);
13191 if (FAILED(rc)) return rc;
13192
13193 if ( mData->mMachineState != MachineState_PoweredOff
13194 && mData->mMachineState != MachineState_Teleported
13195 && mData->mMachineState != MachineState_Aborted
13196 )
13197 return setError(VBOX_E_INVALID_VM_STATE,
13198 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13199 Global::stringifyMachineState(mData->mMachineState));
13200
13201 com::Utf8Str stateFilePathFull;
13202 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13203 if (RT_FAILURE(vrc))
13204 return setError(VBOX_E_FILE_ERROR,
13205 tr("Invalid saved state file path '%s' (%Rrc)"),
13206 aSavedStateFile.c_str(),
13207 vrc);
13208
13209 mSSData->strStateFilePath = stateFilePathFull;
13210
13211 /* The below i_setMachineState() will detect the state transition and will
13212 * update the settings file */
13213
13214 return i_setMachineState(MachineState_Saved);
13215}
13216
13217/**
13218 * @note Locks this object for writing.
13219 */
13220HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13221{
13222 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13223
13224 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13225 if (FAILED(rc)) return rc;
13226
13227 if (mData->mMachineState != MachineState_Saved)
13228 return setError(VBOX_E_INVALID_VM_STATE,
13229 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13230 Global::stringifyMachineState(mData->mMachineState));
13231
13232 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13233
13234 /*
13235 * Saved -> PoweredOff transition will be detected in the SessionMachine
13236 * and properly handled.
13237 */
13238 rc = i_setMachineState(MachineState_PoweredOff);
13239 return rc;
13240}
13241
13242
13243/**
13244 * @note Locks the same as #i_setMachineState() does.
13245 */
13246HRESULT SessionMachine::updateState(MachineState_T aState)
13247{
13248 return i_setMachineState(aState);
13249}
13250
13251/**
13252 * @note Locks this object for writing.
13253 */
13254HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13255{
13256 IProgress *pProgress(aProgress);
13257
13258 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13259
13260 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13261
13262 if (mData->mSession.mState != SessionState_Locked)
13263 return VBOX_E_INVALID_OBJECT_STATE;
13264
13265 if (!mData->mSession.mProgress.isNull())
13266 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13267
13268 /* If we didn't reference the NAT network service yet, add a reference to
13269 * force a start */
13270 if (miNATNetworksStarted < 1)
13271 {
13272 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13273 {
13274 BOOL enabled;
13275 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13276 if ( FAILED(hrc)
13277 || !enabled)
13278 continue;
13279
13280 NetworkAttachmentType_T type;
13281 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13282 if ( SUCCEEDED(hrc)
13283 && type == NetworkAttachmentType_NATNetwork)
13284 {
13285 Bstr name;
13286 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13287 if (SUCCEEDED(hrc))
13288 {
13289 Utf8Str strName(name);
13290 LogRel(("VM '%s' starts using NAT network '%s'\n",
13291 mUserData->s.strName.c_str(), strName.c_str()));
13292 mPeer->lockHandle()->unlockWrite();
13293 mParent->i_natNetworkRefInc(strName);
13294#ifdef RT_LOCK_STRICT
13295 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13296#else
13297 mPeer->lockHandle()->lockWrite();
13298#endif
13299 }
13300 }
13301 }
13302 miNATNetworksStarted++;
13303 }
13304
13305 LogFlowThisFunc(("returns S_OK.\n"));
13306 return S_OK;
13307}
13308
13309/**
13310 * @note Locks this object for writing.
13311 */
13312HRESULT SessionMachine::endPowerUp(LONG aResult)
13313{
13314 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13315
13316 if (mData->mSession.mState != SessionState_Locked)
13317 return VBOX_E_INVALID_OBJECT_STATE;
13318
13319 /* Finalize the LaunchVMProcess progress object. */
13320 if (mData->mSession.mProgress)
13321 {
13322 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13323 mData->mSession.mProgress.setNull();
13324 }
13325
13326 if (SUCCEEDED((HRESULT)aResult))
13327 {
13328#ifdef VBOX_WITH_RESOURCE_USAGE_API
13329 /* The VM has been powered up successfully, so it makes sense
13330 * now to offer the performance metrics for a running machine
13331 * object. Doing it earlier wouldn't be safe. */
13332 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13333 mData->mSession.mPID);
13334#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13335 }
13336
13337 return S_OK;
13338}
13339
13340/**
13341 * @note Locks this object for writing.
13342 */
13343HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13344{
13345 LogFlowThisFuncEnter();
13346
13347 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13348
13349 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13350 E_FAIL);
13351
13352 /* create a progress object to track operation completion */
13353 ComObjPtr<Progress> pProgress;
13354 pProgress.createObject();
13355 pProgress->init(i_getVirtualBox(),
13356 static_cast<IMachine *>(this) /* aInitiator */,
13357 tr("Stopping the virtual machine"),
13358 FALSE /* aCancelable */);
13359
13360 /* fill in the console task data */
13361 mConsoleTaskData.mLastState = mData->mMachineState;
13362 mConsoleTaskData.mProgress = pProgress;
13363
13364 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13365 i_setMachineState(MachineState_Stopping);
13366
13367 pProgress.queryInterfaceTo(aProgress.asOutParam());
13368
13369 return S_OK;
13370}
13371
13372/**
13373 * @note Locks this object for writing.
13374 */
13375HRESULT SessionMachine::endPoweringDown(LONG aResult,
13376 const com::Utf8Str &aErrMsg)
13377{
13378 LogFlowThisFuncEnter();
13379
13380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13381
13382 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13383 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13384 && mConsoleTaskData.mLastState != MachineState_Null,
13385 E_FAIL);
13386
13387 /*
13388 * On failure, set the state to the state we had when BeginPoweringDown()
13389 * was called (this is expected by Console::PowerDown() and the associated
13390 * task). On success the VM process already changed the state to
13391 * MachineState_PoweredOff, so no need to do anything.
13392 */
13393 if (FAILED(aResult))
13394 i_setMachineState(mConsoleTaskData.mLastState);
13395
13396 /* notify the progress object about operation completion */
13397 Assert(mConsoleTaskData.mProgress);
13398 if (SUCCEEDED(aResult))
13399 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13400 else
13401 {
13402 if (aErrMsg.length())
13403 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13404 COM_IIDOF(ISession),
13405 getComponentName(),
13406 aErrMsg.c_str());
13407 else
13408 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13409 }
13410
13411 /* clear out the temporary saved state data */
13412 mConsoleTaskData.mLastState = MachineState_Null;
13413 mConsoleTaskData.mProgress.setNull();
13414
13415 LogFlowThisFuncLeave();
13416 return S_OK;
13417}
13418
13419
13420/**
13421 * Goes through the USB filters of the given machine to see if the given
13422 * device matches any filter or not.
13423 *
13424 * @note Locks the same as USBController::hasMatchingFilter() does.
13425 */
13426HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13427 BOOL *aMatched,
13428 ULONG *aMaskedInterfaces)
13429{
13430 LogFlowThisFunc(("\n"));
13431
13432#ifdef VBOX_WITH_USB
13433 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13434#else
13435 NOREF(aDevice);
13436 NOREF(aMaskedInterfaces);
13437 *aMatched = FALSE;
13438#endif
13439
13440 return S_OK;
13441}
13442
13443/**
13444 * @note Locks the same as Host::captureUSBDevice() does.
13445 */
13446HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13447{
13448 LogFlowThisFunc(("\n"));
13449
13450#ifdef VBOX_WITH_USB
13451 /* if captureDeviceForVM() fails, it must have set extended error info */
13452 clearError();
13453 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13454 if (FAILED(rc)) return rc;
13455
13456 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13457 AssertReturn(service, E_FAIL);
13458 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13459#else
13460 NOREF(aId);
13461 return E_NOTIMPL;
13462#endif
13463}
13464
13465/**
13466 * @note Locks the same as Host::detachUSBDevice() does.
13467 */
13468HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13469 BOOL aDone)
13470{
13471 LogFlowThisFunc(("\n"));
13472
13473#ifdef VBOX_WITH_USB
13474 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13475 AssertReturn(service, E_FAIL);
13476 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13477#else
13478 NOREF(aId);
13479 NOREF(aDone);
13480 return E_NOTIMPL;
13481#endif
13482}
13483
13484/**
13485 * Inserts all machine filters to the USB proxy service and then calls
13486 * Host::autoCaptureUSBDevices().
13487 *
13488 * Called by Console from the VM process upon VM startup.
13489 *
13490 * @note Locks what called methods lock.
13491 */
13492HRESULT SessionMachine::autoCaptureUSBDevices()
13493{
13494 LogFlowThisFunc(("\n"));
13495
13496#ifdef VBOX_WITH_USB
13497 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13498 AssertComRC(rc);
13499 NOREF(rc);
13500
13501 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13502 AssertReturn(service, E_FAIL);
13503 return service->autoCaptureDevicesForVM(this);
13504#else
13505 return S_OK;
13506#endif
13507}
13508
13509/**
13510 * Removes all machine filters from the USB proxy service and then calls
13511 * Host::detachAllUSBDevices().
13512 *
13513 * Called by Console from the VM process upon normal VM termination or by
13514 * SessionMachine::uninit() upon abnormal VM termination (from under the
13515 * Machine/SessionMachine lock).
13516 *
13517 * @note Locks what called methods lock.
13518 */
13519HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13520{
13521 LogFlowThisFunc(("\n"));
13522
13523#ifdef VBOX_WITH_USB
13524 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13525 AssertComRC(rc);
13526 NOREF(rc);
13527
13528 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13529 AssertReturn(service, E_FAIL);
13530 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13531#else
13532 NOREF(aDone);
13533 return S_OK;
13534#endif
13535}
13536
13537/**
13538 * @note Locks this object for writing.
13539 */
13540HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13541 ComPtr<IProgress> &aProgress)
13542{
13543 LogFlowThisFuncEnter();
13544
13545 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13546 /*
13547 * We don't assert below because it might happen that a non-direct session
13548 * informs us it is closed right after we've been uninitialized -- it's ok.
13549 */
13550
13551 /* get IInternalSessionControl interface */
13552 ComPtr<IInternalSessionControl> control(aSession);
13553
13554 ComAssertRet(!control.isNull(), E_INVALIDARG);
13555
13556 /* Creating a Progress object requires the VirtualBox lock, and
13557 * thus locking it here is required by the lock order rules. */
13558 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13559
13560 if (control == mData->mSession.mDirectControl)
13561 {
13562 /* The direct session is being normally closed by the client process
13563 * ----------------------------------------------------------------- */
13564
13565 /* go to the closing state (essential for all open*Session() calls and
13566 * for #i_checkForDeath()) */
13567 Assert(mData->mSession.mState == SessionState_Locked);
13568 mData->mSession.mState = SessionState_Unlocking;
13569
13570 /* set direct control to NULL to release the remote instance */
13571 mData->mSession.mDirectControl.setNull();
13572 LogFlowThisFunc(("Direct control is set to NULL\n"));
13573
13574 if (mData->mSession.mProgress)
13575 {
13576 /* finalize the progress, someone might wait if a frontend
13577 * closes the session before powering on the VM. */
13578 mData->mSession.mProgress->notifyComplete(E_FAIL,
13579 COM_IIDOF(ISession),
13580 getComponentName(),
13581 tr("The VM session was closed before any attempt to power it on"));
13582 mData->mSession.mProgress.setNull();
13583 }
13584
13585 /* Create the progress object the client will use to wait until
13586 * #i_checkForDeath() is called to uninitialize this session object after
13587 * it releases the IPC semaphore.
13588 * Note! Because we're "reusing" mProgress here, this must be a proxy
13589 * object just like for LaunchVMProcess. */
13590 Assert(mData->mSession.mProgress.isNull());
13591 ComObjPtr<ProgressProxy> progress;
13592 progress.createObject();
13593 ComPtr<IUnknown> pPeer(mPeer);
13594 progress->init(mParent, pPeer,
13595 Bstr(tr("Closing session")).raw(),
13596 FALSE /* aCancelable */);
13597 progress.queryInterfaceTo(aProgress.asOutParam());
13598 mData->mSession.mProgress = progress;
13599 }
13600 else
13601 {
13602 /* the remote session is being normally closed */
13603 bool found = false;
13604 for (Data::Session::RemoteControlList::iterator
13605 it = mData->mSession.mRemoteControls.begin();
13606 it != mData->mSession.mRemoteControls.end();
13607 ++it)
13608 {
13609 if (control == *it)
13610 {
13611 found = true;
13612 // This MUST be erase(it), not remove(*it) as the latter
13613 // triggers a very nasty use after free due to the place where
13614 // the value "lives".
13615 mData->mSession.mRemoteControls.erase(it);
13616 break;
13617 }
13618 }
13619 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13620 E_INVALIDARG);
13621 }
13622
13623 /* signal the client watcher thread, because the client is going away */
13624 mParent->i_updateClientWatcher();
13625
13626 LogFlowThisFuncLeave();
13627 return S_OK;
13628}
13629
13630HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13631 std::vector<com::Utf8Str> &aValues,
13632 std::vector<LONG64> &aTimestamps,
13633 std::vector<com::Utf8Str> &aFlags)
13634{
13635 LogFlowThisFunc(("\n"));
13636
13637#ifdef VBOX_WITH_GUEST_PROPS
13638 using namespace guestProp;
13639
13640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13641
13642 size_t cEntries = mHWData->mGuestProperties.size();
13643 aNames.resize(cEntries);
13644 aValues.resize(cEntries);
13645 aTimestamps.resize(cEntries);
13646 aFlags.resize(cEntries);
13647
13648 size_t i = 0;
13649 for (HWData::GuestPropertyMap::const_iterator
13650 it = mHWData->mGuestProperties.begin();
13651 it != mHWData->mGuestProperties.end();
13652 ++it, ++i)
13653 {
13654 char szFlags[MAX_FLAGS_LEN + 1];
13655 aNames[i] = it->first;
13656 aValues[i] = it->second.strValue;
13657 aTimestamps[i] = it->second.mTimestamp;
13658
13659 /* If it is NULL, keep it NULL. */
13660 if (it->second.mFlags)
13661 {
13662 writeFlags(it->second.mFlags, szFlags);
13663 aFlags[i] = szFlags;
13664 }
13665 else
13666 aFlags[i] = "";
13667 }
13668 return S_OK;
13669#else
13670 ReturnComNotImplemented();
13671#endif
13672}
13673
13674HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13675 const com::Utf8Str &aValue,
13676 LONG64 aTimestamp,
13677 const com::Utf8Str &aFlags)
13678{
13679 LogFlowThisFunc(("\n"));
13680
13681#ifdef VBOX_WITH_GUEST_PROPS
13682 using namespace guestProp;
13683
13684 try
13685 {
13686 /*
13687 * Convert input up front.
13688 */
13689 uint32_t fFlags = NILFLAG;
13690 if (aFlags.length())
13691 {
13692 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13693 AssertRCReturn(vrc, E_INVALIDARG);
13694 }
13695
13696 /*
13697 * Now grab the object lock, validate the state and do the update.
13698 */
13699
13700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13701
13702 if (!Global::IsOnline(mData->mMachineState))
13703 {
13704 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13705 VBOX_E_INVALID_VM_STATE);
13706 }
13707
13708 i_setModified(IsModified_MachineData);
13709 mHWData.backup();
13710
13711 bool fDelete = !aValue.length();
13712 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13713 if (it != mHWData->mGuestProperties.end())
13714 {
13715 if (!fDelete)
13716 {
13717 it->second.strValue = aValue;
13718 it->second.mTimestamp = aTimestamp;
13719 it->second.mFlags = fFlags;
13720 }
13721 else
13722 mHWData->mGuestProperties.erase(it);
13723
13724 mData->mGuestPropertiesModified = TRUE;
13725 }
13726 else if (!fDelete)
13727 {
13728 HWData::GuestProperty prop;
13729 prop.strValue = aValue;
13730 prop.mTimestamp = aTimestamp;
13731 prop.mFlags = fFlags;
13732
13733 mHWData->mGuestProperties[aName] = prop;
13734 mData->mGuestPropertiesModified = TRUE;
13735 }
13736
13737 alock.release();
13738
13739 mParent->i_onGuestPropertyChange(mData->mUuid,
13740 Bstr(aName).raw(),
13741 Bstr(aValue).raw(),
13742 Bstr(aFlags).raw());
13743 }
13744 catch (...)
13745 {
13746 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13747 }
13748 return S_OK;
13749#else
13750 ReturnComNotImplemented();
13751#endif
13752}
13753
13754
13755HRESULT SessionMachine::lockMedia()
13756{
13757 AutoMultiWriteLock2 alock(this->lockHandle(),
13758 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13759
13760 AssertReturn( mData->mMachineState == MachineState_Starting
13761 || mData->mMachineState == MachineState_Restoring
13762 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13763
13764 clearError();
13765 alock.release();
13766 return i_lockMedia();
13767}
13768
13769HRESULT SessionMachine::unlockMedia()
13770{
13771 HRESULT hrc = i_unlockMedia();
13772 return hrc;
13773}
13774
13775HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13776 ComPtr<IMediumAttachment> &aNewAttachment)
13777{
13778 // request the host lock first, since might be calling Host methods for getting host drives;
13779 // next, protect the media tree all the while we're in here, as well as our member variables
13780 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13781 this->lockHandle(),
13782 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13783
13784 IMediumAttachment *iAttach = aAttachment;
13785 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13786
13787 Utf8Str ctrlName;
13788 LONG lPort;
13789 LONG lDevice;
13790 bool fTempEject;
13791 {
13792 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13793
13794 /* Need to query the details first, as the IMediumAttachment reference
13795 * might be to the original settings, which we are going to change. */
13796 ctrlName = pAttach->i_getControllerName();
13797 lPort = pAttach->i_getPort();
13798 lDevice = pAttach->i_getDevice();
13799 fTempEject = pAttach->i_getTempEject();
13800 }
13801
13802 if (!fTempEject)
13803 {
13804 /* Remember previously mounted medium. The medium before taking the
13805 * backup is not necessarily the same thing. */
13806 ComObjPtr<Medium> oldmedium;
13807 oldmedium = pAttach->i_getMedium();
13808
13809 i_setModified(IsModified_Storage);
13810 mMediumAttachments.backup();
13811
13812 // The backup operation makes the pAttach reference point to the
13813 // old settings. Re-get the correct reference.
13814 pAttach = i_findAttachment(*mMediumAttachments.data(),
13815 ctrlName,
13816 lPort,
13817 lDevice);
13818
13819 {
13820 AutoCaller autoAttachCaller(this);
13821 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13822
13823 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13824 if (!oldmedium.isNull())
13825 oldmedium->i_removeBackReference(mData->mUuid);
13826
13827 pAttach->i_updateMedium(NULL);
13828 pAttach->i_updateEjected();
13829 }
13830
13831 i_setModified(IsModified_Storage);
13832 }
13833 else
13834 {
13835 {
13836 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13837 pAttach->i_updateEjected();
13838 }
13839 }
13840
13841 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13842
13843 return S_OK;
13844}
13845
13846HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13847 com::Utf8Str &aResult)
13848{
13849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13850
13851 HRESULT hr = S_OK;
13852
13853 if (!mAuthLibCtx.hAuthLibrary)
13854 {
13855 /* Load the external authentication library. */
13856 Bstr authLibrary;
13857 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13858
13859 Utf8Str filename = authLibrary;
13860
13861 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13862 if (RT_FAILURE(rc))
13863 {
13864 hr = setError(E_FAIL,
13865 tr("Could not load the external authentication library '%s' (%Rrc)"),
13866 filename.c_str(), rc);
13867 }
13868 }
13869
13870 /* The auth library might need the machine lock. */
13871 alock.release();
13872
13873 if (FAILED(hr))
13874 return hr;
13875
13876 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13877 {
13878 enum VRDEAuthParams
13879 {
13880 parmUuid = 1,
13881 parmGuestJudgement,
13882 parmUser,
13883 parmPassword,
13884 parmDomain,
13885 parmClientId
13886 };
13887
13888 AuthResult result = AuthResultAccessDenied;
13889
13890 Guid uuid(aAuthParams[parmUuid]);
13891 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13892 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13893
13894 result = AuthLibAuthenticate(&mAuthLibCtx,
13895 uuid.raw(), guestJudgement,
13896 aAuthParams[parmUser].c_str(),
13897 aAuthParams[parmPassword].c_str(),
13898 aAuthParams[parmDomain].c_str(),
13899 u32ClientId);
13900
13901 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13902 size_t cbPassword = aAuthParams[parmPassword].length();
13903 if (cbPassword)
13904 {
13905 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13906 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13907 }
13908
13909 if (result == AuthResultAccessGranted)
13910 aResult = "granted";
13911 else
13912 aResult = "denied";
13913
13914 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13915 aAuthParams[parmUser].c_str(), aResult.c_str()));
13916 }
13917 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13918 {
13919 enum VRDEAuthDisconnectParams
13920 {
13921 parmUuid = 1,
13922 parmClientId
13923 };
13924
13925 Guid uuid(aAuthParams[parmUuid]);
13926 uint32_t u32ClientId = 0;
13927 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13928 }
13929 else
13930 {
13931 hr = E_INVALIDARG;
13932 }
13933
13934 return hr;
13935}
13936
13937// public methods only for internal purposes
13938/////////////////////////////////////////////////////////////////////////////
13939
13940#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13941/**
13942 * Called from the client watcher thread to check for expected or unexpected
13943 * death of the client process that has a direct session to this machine.
13944 *
13945 * On Win32 and on OS/2, this method is called only when we've got the
13946 * mutex (i.e. the client has either died or terminated normally) so it always
13947 * returns @c true (the client is terminated, the session machine is
13948 * uninitialized).
13949 *
13950 * On other platforms, the method returns @c true if the client process has
13951 * terminated normally or abnormally and the session machine was uninitialized,
13952 * and @c false if the client process is still alive.
13953 *
13954 * @note Locks this object for writing.
13955 */
13956bool SessionMachine::i_checkForDeath()
13957{
13958 Uninit::Reason reason;
13959 bool terminated = false;
13960
13961 /* Enclose autoCaller with a block because calling uninit() from under it
13962 * will deadlock. */
13963 {
13964 AutoCaller autoCaller(this);
13965 if (!autoCaller.isOk())
13966 {
13967 /* return true if not ready, to cause the client watcher to exclude
13968 * the corresponding session from watching */
13969 LogFlowThisFunc(("Already uninitialized!\n"));
13970 return true;
13971 }
13972
13973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13974
13975 /* Determine the reason of death: if the session state is Closing here,
13976 * everything is fine. Otherwise it means that the client did not call
13977 * OnSessionEnd() before it released the IPC semaphore. This may happen
13978 * either because the client process has abnormally terminated, or
13979 * because it simply forgot to call ISession::Close() before exiting. We
13980 * threat the latter also as an abnormal termination (see
13981 * Session::uninit() for details). */
13982 reason = mData->mSession.mState == SessionState_Unlocking ?
13983 Uninit::Normal :
13984 Uninit::Abnormal;
13985
13986 if (mClientToken)
13987 terminated = mClientToken->release();
13988 } /* AutoCaller block */
13989
13990 if (terminated)
13991 uninit(reason);
13992
13993 return terminated;
13994}
13995
13996void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13997{
13998 LogFlowThisFunc(("\n"));
13999
14000 strTokenId.setNull();
14001
14002 AutoCaller autoCaller(this);
14003 AssertComRCReturnVoid(autoCaller.rc());
14004
14005 Assert(mClientToken);
14006 if (mClientToken)
14007 mClientToken->getId(strTokenId);
14008}
14009#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14010IToken *SessionMachine::i_getToken()
14011{
14012 LogFlowThisFunc(("\n"));
14013
14014 AutoCaller autoCaller(this);
14015 AssertComRCReturn(autoCaller.rc(), NULL);
14016
14017 Assert(mClientToken);
14018 if (mClientToken)
14019 return mClientToken->getToken();
14020 else
14021 return NULL;
14022}
14023#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14024
14025Machine::ClientToken *SessionMachine::i_getClientToken()
14026{
14027 LogFlowThisFunc(("\n"));
14028
14029 AutoCaller autoCaller(this);
14030 AssertComRCReturn(autoCaller.rc(), NULL);
14031
14032 return mClientToken;
14033}
14034
14035
14036/**
14037 * @note Locks this object for reading.
14038 */
14039HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14040{
14041 LogFlowThisFunc(("\n"));
14042
14043 AutoCaller autoCaller(this);
14044 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14045
14046 ComPtr<IInternalSessionControl> directControl;
14047 {
14048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14049 if (mData->mSession.mLockType == LockType_VM)
14050 directControl = mData->mSession.mDirectControl;
14051 }
14052
14053 /* ignore notifications sent after #OnSessionEnd() is called */
14054 if (!directControl)
14055 return S_OK;
14056
14057 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14058}
14059
14060/**
14061 * @note Locks this object for reading.
14062 */
14063HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14064 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14065 IN_BSTR aGuestIp, LONG aGuestPort)
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 * instead acting like callback we ask IVirtualBox deliver corresponding event
14084 */
14085
14086 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14087 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14088 return S_OK;
14089}
14090
14091/**
14092 * @note Locks this object for reading.
14093 */
14094HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14095{
14096 LogFlowThisFunc(("\n"));
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14100
14101 ComPtr<IInternalSessionControl> directControl;
14102 {
14103 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14104 if (mData->mSession.mLockType == LockType_VM)
14105 directControl = mData->mSession.mDirectControl;
14106 }
14107
14108 /* ignore notifications sent after #OnSessionEnd() is called */
14109 if (!directControl)
14110 return S_OK;
14111
14112 return directControl->OnSerialPortChange(serialPort);
14113}
14114
14115/**
14116 * @note Locks this object for reading.
14117 */
14118HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14119{
14120 LogFlowThisFunc(("\n"));
14121
14122 AutoCaller autoCaller(this);
14123 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14124
14125 ComPtr<IInternalSessionControl> directControl;
14126 {
14127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14128 if (mData->mSession.mLockType == LockType_VM)
14129 directControl = mData->mSession.mDirectControl;
14130 }
14131
14132 /* ignore notifications sent after #OnSessionEnd() is called */
14133 if (!directControl)
14134 return S_OK;
14135
14136 return directControl->OnParallelPortChange(parallelPort);
14137}
14138
14139/**
14140 * @note Locks this object for reading.
14141 */
14142HRESULT SessionMachine::i_onStorageControllerChange()
14143{
14144 LogFlowThisFunc(("\n"));
14145
14146 AutoCaller autoCaller(this);
14147 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14148
14149 ComPtr<IInternalSessionControl> directControl;
14150 {
14151 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14152 if (mData->mSession.mLockType == LockType_VM)
14153 directControl = mData->mSession.mDirectControl;
14154 }
14155
14156 /* ignore notifications sent after #OnSessionEnd() is called */
14157 if (!directControl)
14158 return S_OK;
14159
14160 return directControl->OnStorageControllerChange();
14161}
14162
14163/**
14164 * @note Locks this object for reading.
14165 */
14166HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14167{
14168 LogFlowThisFunc(("\n"));
14169
14170 AutoCaller autoCaller(this);
14171 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14172
14173 ComPtr<IInternalSessionControl> directControl;
14174 {
14175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14176 if (mData->mSession.mLockType == LockType_VM)
14177 directControl = mData->mSession.mDirectControl;
14178 }
14179
14180 /* ignore notifications sent after #OnSessionEnd() is called */
14181 if (!directControl)
14182 return S_OK;
14183
14184 return directControl->OnMediumChange(aAttachment, aForce);
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnCPUChange(aCPU, aRemove);
14209}
14210
14211HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14212{
14213 LogFlowThisFunc(("\n"));
14214
14215 AutoCaller autoCaller(this);
14216 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14217
14218 ComPtr<IInternalSessionControl> directControl;
14219 {
14220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14221 if (mData->mSession.mLockType == LockType_VM)
14222 directControl = mData->mSession.mDirectControl;
14223 }
14224
14225 /* ignore notifications sent after #OnSessionEnd() is called */
14226 if (!directControl)
14227 return S_OK;
14228
14229 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14230}
14231
14232/**
14233 * @note Locks this object for reading.
14234 */
14235HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14236{
14237 LogFlowThisFunc(("\n"));
14238
14239 AutoCaller autoCaller(this);
14240 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14241
14242 ComPtr<IInternalSessionControl> directControl;
14243 {
14244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14245 if (mData->mSession.mLockType == LockType_VM)
14246 directControl = mData->mSession.mDirectControl;
14247 }
14248
14249 /* ignore notifications sent after #OnSessionEnd() is called */
14250 if (!directControl)
14251 return S_OK;
14252
14253 return directControl->OnVRDEServerChange(aRestart);
14254}
14255
14256/**
14257 * @note Locks this object for reading.
14258 */
14259HRESULT SessionMachine::i_onVideoCaptureChange()
14260{
14261 LogFlowThisFunc(("\n"));
14262
14263 AutoCaller autoCaller(this);
14264 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14265
14266 ComPtr<IInternalSessionControl> directControl;
14267 {
14268 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14269 if (mData->mSession.mLockType == LockType_VM)
14270 directControl = mData->mSession.mDirectControl;
14271 }
14272
14273 /* ignore notifications sent after #OnSessionEnd() is called */
14274 if (!directControl)
14275 return S_OK;
14276
14277 return directControl->OnVideoCaptureChange();
14278}
14279
14280/**
14281 * @note Locks this object for reading.
14282 */
14283HRESULT SessionMachine::i_onUSBControllerChange()
14284{
14285 LogFlowThisFunc(("\n"));
14286
14287 AutoCaller autoCaller(this);
14288 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14289
14290 ComPtr<IInternalSessionControl> directControl;
14291 {
14292 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14293 if (mData->mSession.mLockType == LockType_VM)
14294 directControl = mData->mSession.mDirectControl;
14295 }
14296
14297 /* ignore notifications sent after #OnSessionEnd() is called */
14298 if (!directControl)
14299 return S_OK;
14300
14301 return directControl->OnUSBControllerChange();
14302}
14303
14304/**
14305 * @note Locks this object for reading.
14306 */
14307HRESULT SessionMachine::i_onSharedFolderChange()
14308{
14309 LogFlowThisFunc(("\n"));
14310
14311 AutoCaller autoCaller(this);
14312 AssertComRCReturnRC(autoCaller.rc());
14313
14314 ComPtr<IInternalSessionControl> directControl;
14315 {
14316 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14317 if (mData->mSession.mLockType == LockType_VM)
14318 directControl = mData->mSession.mDirectControl;
14319 }
14320
14321 /* ignore notifications sent after #OnSessionEnd() is called */
14322 if (!directControl)
14323 return S_OK;
14324
14325 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14326}
14327
14328/**
14329 * @note Locks this object for reading.
14330 */
14331HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14332{
14333 LogFlowThisFunc(("\n"));
14334
14335 AutoCaller autoCaller(this);
14336 AssertComRCReturnRC(autoCaller.rc());
14337
14338 ComPtr<IInternalSessionControl> directControl;
14339 {
14340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14341 if (mData->mSession.mLockType == LockType_VM)
14342 directControl = mData->mSession.mDirectControl;
14343 }
14344
14345 /* ignore notifications sent after #OnSessionEnd() is called */
14346 if (!directControl)
14347 return S_OK;
14348
14349 return directControl->OnClipboardModeChange(aClipboardMode);
14350}
14351
14352/**
14353 * @note Locks this object for reading.
14354 */
14355HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14356{
14357 LogFlowThisFunc(("\n"));
14358
14359 AutoCaller autoCaller(this);
14360 AssertComRCReturnRC(autoCaller.rc());
14361
14362 ComPtr<IInternalSessionControl> directControl;
14363 {
14364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14365 if (mData->mSession.mLockType == LockType_VM)
14366 directControl = mData->mSession.mDirectControl;
14367 }
14368
14369 /* ignore notifications sent after #OnSessionEnd() is called */
14370 if (!directControl)
14371 return S_OK;
14372
14373 return directControl->OnDnDModeChange(aDnDMode);
14374}
14375
14376/**
14377 * @note Locks this object for reading.
14378 */
14379HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14385
14386 ComPtr<IInternalSessionControl> directControl;
14387 {
14388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14389 if (mData->mSession.mLockType == LockType_VM)
14390 directControl = mData->mSession.mDirectControl;
14391 }
14392
14393 /* ignore notifications sent after #OnSessionEnd() is called */
14394 if (!directControl)
14395 return S_OK;
14396
14397 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14398}
14399
14400/**
14401 * @note Locks this object for reading.
14402 */
14403HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14404{
14405 LogFlowThisFunc(("\n"));
14406
14407 AutoCaller autoCaller(this);
14408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14409
14410 ComPtr<IInternalSessionControl> directControl;
14411 {
14412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14413 if (mData->mSession.mLockType == LockType_VM)
14414 directControl = mData->mSession.mDirectControl;
14415 }
14416
14417 /* ignore notifications sent after #OnSessionEnd() is called */
14418 if (!directControl)
14419 return S_OK;
14420
14421 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14422}
14423
14424/**
14425 * Returns @c true if this machine's USB controller reports it has a matching
14426 * filter for the given USB device and @c false otherwise.
14427 *
14428 * @note locks this object for reading.
14429 */
14430bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14431{
14432 AutoCaller autoCaller(this);
14433 /* silently return if not ready -- this method may be called after the
14434 * direct machine session has been called */
14435 if (!autoCaller.isOk())
14436 return false;
14437
14438#ifdef VBOX_WITH_USB
14439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14440
14441 switch (mData->mMachineState)
14442 {
14443 case MachineState_Starting:
14444 case MachineState_Restoring:
14445 case MachineState_TeleportingIn:
14446 case MachineState_Paused:
14447 case MachineState_Running:
14448 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14449 * elsewhere... */
14450 alock.release();
14451 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14452 default: break;
14453 }
14454#else
14455 NOREF(aDevice);
14456 NOREF(aMaskedIfs);
14457#endif
14458 return false;
14459}
14460
14461/**
14462 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14463 */
14464HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14465 IVirtualBoxErrorInfo *aError,
14466 ULONG aMaskedIfs,
14467 const com::Utf8Str &aCaptureFilename)
14468{
14469 LogFlowThisFunc(("\n"));
14470
14471 AutoCaller autoCaller(this);
14472
14473 /* This notification may happen after the machine object has been
14474 * uninitialized (the session was closed), so don't assert. */
14475 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14476
14477 ComPtr<IInternalSessionControl> directControl;
14478 {
14479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14480 if (mData->mSession.mLockType == LockType_VM)
14481 directControl = mData->mSession.mDirectControl;
14482 }
14483
14484 /* fail on notifications sent after #OnSessionEnd() is called, it is
14485 * expected by the caller */
14486 if (!directControl)
14487 return E_FAIL;
14488
14489 /* No locks should be held at this point. */
14490 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14491 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14492
14493 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14494}
14495
14496/**
14497 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14498 */
14499HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14500 IVirtualBoxErrorInfo *aError)
14501{
14502 LogFlowThisFunc(("\n"));
14503
14504 AutoCaller autoCaller(this);
14505
14506 /* This notification may happen after the machine object has been
14507 * uninitialized (the session was closed), so don't assert. */
14508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14509
14510 ComPtr<IInternalSessionControl> directControl;
14511 {
14512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14513 if (mData->mSession.mLockType == LockType_VM)
14514 directControl = mData->mSession.mDirectControl;
14515 }
14516
14517 /* fail on notifications sent after #OnSessionEnd() is called, it is
14518 * expected by the caller */
14519 if (!directControl)
14520 return E_FAIL;
14521
14522 /* No locks should be held at this point. */
14523 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14524 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14525
14526 return directControl->OnUSBDeviceDetach(aId, aError);
14527}
14528
14529// protected methods
14530/////////////////////////////////////////////////////////////////////////////
14531
14532/**
14533 * Deletes the given file if it is no longer in use by either the current machine state
14534 * (if the machine is "saved") or any of the machine's snapshots.
14535 *
14536 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14537 * but is different for each SnapshotMachine. When calling this, the order of calling this
14538 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14539 * is therefore critical. I know, it's all rather messy.
14540 *
14541 * @param strStateFile
14542 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14543 * the test for whether the saved state file is in use.
14544 */
14545void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14546 Snapshot *pSnapshotToIgnore)
14547{
14548 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14549 if ( (strStateFile.isNotEmpty())
14550 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14551 )
14552 // ... and it must also not be shared with other snapshots
14553 if ( !mData->mFirstSnapshot
14554 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14555 // this checks the SnapshotMachine's state file paths
14556 )
14557 RTFileDelete(strStateFile.c_str());
14558}
14559
14560/**
14561 * Locks the attached media.
14562 *
14563 * All attached hard disks are locked for writing and DVD/floppy are locked for
14564 * reading. Parents of attached hard disks (if any) are locked for reading.
14565 *
14566 * This method also performs accessibility check of all media it locks: if some
14567 * media is inaccessible, the method will return a failure and a bunch of
14568 * extended error info objects per each inaccessible medium.
14569 *
14570 * Note that this method is atomic: if it returns a success, all media are
14571 * locked as described above; on failure no media is locked at all (all
14572 * succeeded individual locks will be undone).
14573 *
14574 * The caller is responsible for doing the necessary state sanity checks.
14575 *
14576 * The locks made by this method must be undone by calling #unlockMedia() when
14577 * no more needed.
14578 */
14579HRESULT SessionMachine::i_lockMedia()
14580{
14581 AutoCaller autoCaller(this);
14582 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14583
14584 AutoMultiWriteLock2 alock(this->lockHandle(),
14585 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14586
14587 /* bail out if trying to lock things with already set up locking */
14588 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14589
14590 MultiResult mrc(S_OK);
14591
14592 /* Collect locking information for all medium objects attached to the VM. */
14593 for (MediumAttachmentList::const_iterator
14594 it = mMediumAttachments->begin();
14595 it != mMediumAttachments->end();
14596 ++it)
14597 {
14598 MediumAttachment *pAtt = *it;
14599 DeviceType_T devType = pAtt->i_getType();
14600 Medium *pMedium = pAtt->i_getMedium();
14601
14602 MediumLockList *pMediumLockList(new MediumLockList());
14603 // There can be attachments without a medium (floppy/dvd), and thus
14604 // it's impossible to create a medium lock list. It still makes sense
14605 // to have the empty medium lock list in the map in case a medium is
14606 // attached later.
14607 if (pMedium != NULL)
14608 {
14609 MediumType_T mediumType = pMedium->i_getType();
14610 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14611 || mediumType == MediumType_Shareable;
14612 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14613
14614 alock.release();
14615 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14616 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14617 false /* fMediumLockWriteAll */,
14618 NULL,
14619 *pMediumLockList);
14620 alock.acquire();
14621 if (FAILED(mrc))
14622 {
14623 delete pMediumLockList;
14624 mData->mSession.mLockedMedia.Clear();
14625 break;
14626 }
14627 }
14628
14629 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14630 if (FAILED(rc))
14631 {
14632 mData->mSession.mLockedMedia.Clear();
14633 mrc = setError(rc,
14634 tr("Collecting locking information for all attached media failed"));
14635 break;
14636 }
14637 }
14638
14639 if (SUCCEEDED(mrc))
14640 {
14641 /* Now lock all media. If this fails, nothing is locked. */
14642 alock.release();
14643 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14644 alock.acquire();
14645 if (FAILED(rc))
14646 {
14647 mrc = setError(rc,
14648 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14649 }
14650 }
14651
14652 return mrc;
14653}
14654
14655/**
14656 * Undoes the locks made by by #lockMedia().
14657 */
14658HRESULT SessionMachine::i_unlockMedia()
14659{
14660 AutoCaller autoCaller(this);
14661 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14662
14663 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14664
14665 /* we may be holding important error info on the current thread;
14666 * preserve it */
14667 ErrorInfoKeeper eik;
14668
14669 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14670 AssertComRC(rc);
14671 return rc;
14672}
14673
14674/**
14675 * Helper to change the machine state (reimplementation).
14676 *
14677 * @note Locks this object for writing.
14678 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14679 * it can cause crashes in random places due to unexpectedly committing
14680 * the current settings. The caller is responsible for that. The call
14681 * to saveStateSettings is fine, because this method does not commit.
14682 */
14683HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14684{
14685 LogFlowThisFuncEnter();
14686 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14687
14688 AutoCaller autoCaller(this);
14689 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14690
14691 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14692
14693 MachineState_T oldMachineState = mData->mMachineState;
14694
14695 AssertMsgReturn(oldMachineState != aMachineState,
14696 ("oldMachineState=%s, aMachineState=%s\n",
14697 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14698 E_FAIL);
14699
14700 HRESULT rc = S_OK;
14701
14702 int stsFlags = 0;
14703 bool deleteSavedState = false;
14704
14705 /* detect some state transitions */
14706
14707 if ( ( oldMachineState == MachineState_Saved
14708 && aMachineState == MachineState_Restoring)
14709 || ( ( oldMachineState == MachineState_PoweredOff
14710 || oldMachineState == MachineState_Teleported
14711 || oldMachineState == MachineState_Aborted
14712 )
14713 && ( aMachineState == MachineState_TeleportingIn
14714 || aMachineState == MachineState_Starting
14715 )
14716 )
14717 )
14718 {
14719 /* The EMT thread is about to start */
14720
14721 /* Nothing to do here for now... */
14722
14723 /// @todo NEWMEDIA don't let mDVDDrive and other children
14724 /// change anything when in the Starting/Restoring state
14725 }
14726 else if ( ( oldMachineState == MachineState_Running
14727 || oldMachineState == MachineState_Paused
14728 || oldMachineState == MachineState_Teleporting
14729 || oldMachineState == MachineState_OnlineSnapshotting
14730 || oldMachineState == MachineState_LiveSnapshotting
14731 || oldMachineState == MachineState_Stuck
14732 || oldMachineState == MachineState_Starting
14733 || oldMachineState == MachineState_Stopping
14734 || oldMachineState == MachineState_Saving
14735 || oldMachineState == MachineState_Restoring
14736 || oldMachineState == MachineState_TeleportingPausedVM
14737 || oldMachineState == MachineState_TeleportingIn
14738 )
14739 && ( aMachineState == MachineState_PoweredOff
14740 || aMachineState == MachineState_Saved
14741 || aMachineState == MachineState_Teleported
14742 || aMachineState == MachineState_Aborted
14743 )
14744 )
14745 {
14746 /* The EMT thread has just stopped, unlock attached media. Note that as
14747 * opposed to locking that is done from Console, we do unlocking here
14748 * because the VM process may have aborted before having a chance to
14749 * properly unlock all media it locked. */
14750
14751 unlockMedia();
14752 }
14753
14754 if (oldMachineState == MachineState_Restoring)
14755 {
14756 if (aMachineState != MachineState_Saved)
14757 {
14758 /*
14759 * delete the saved state file once the machine has finished
14760 * restoring from it (note that Console sets the state from
14761 * Restoring to Saved if the VM couldn't restore successfully,
14762 * to give the user an ability to fix an error and retry --
14763 * we keep the saved state file in this case)
14764 */
14765 deleteSavedState = true;
14766 }
14767 }
14768 else if ( oldMachineState == MachineState_Saved
14769 && ( aMachineState == MachineState_PoweredOff
14770 || aMachineState == MachineState_Aborted
14771 || aMachineState == MachineState_Teleported
14772 )
14773 )
14774 {
14775 /*
14776 * delete the saved state after SessionMachine::ForgetSavedState() is called
14777 * or if the VM process (owning a direct VM session) crashed while the
14778 * VM was Saved
14779 */
14780
14781 /// @todo (dmik)
14782 // Not sure that deleting the saved state file just because of the
14783 // client death before it attempted to restore the VM is a good
14784 // thing. But when it crashes we need to go to the Aborted state
14785 // which cannot have the saved state file associated... The only
14786 // way to fix this is to make the Aborted condition not a VM state
14787 // but a bool flag: i.e., when a crash occurs, set it to true and
14788 // change the state to PoweredOff or Saved depending on the
14789 // saved state presence.
14790
14791 deleteSavedState = true;
14792 mData->mCurrentStateModified = TRUE;
14793 stsFlags |= SaveSTS_CurStateModified;
14794 }
14795
14796 if ( aMachineState == MachineState_Starting
14797 || aMachineState == MachineState_Restoring
14798 || aMachineState == MachineState_TeleportingIn
14799 )
14800 {
14801 /* set the current state modified flag to indicate that the current
14802 * state is no more identical to the state in the
14803 * current snapshot */
14804 if (!mData->mCurrentSnapshot.isNull())
14805 {
14806 mData->mCurrentStateModified = TRUE;
14807 stsFlags |= SaveSTS_CurStateModified;
14808 }
14809 }
14810
14811 if (deleteSavedState)
14812 {
14813 if (mRemoveSavedState)
14814 {
14815 Assert(!mSSData->strStateFilePath.isEmpty());
14816
14817 // it is safe to delete the saved state file if ...
14818 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14819 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14820 // ... none of the snapshots share the saved state file
14821 )
14822 RTFileDelete(mSSData->strStateFilePath.c_str());
14823 }
14824
14825 mSSData->strStateFilePath.setNull();
14826 stsFlags |= SaveSTS_StateFilePath;
14827 }
14828
14829 /* redirect to the underlying peer machine */
14830 mPeer->i_setMachineState(aMachineState);
14831
14832 if ( oldMachineState != MachineState_RestoringSnapshot
14833 && ( aMachineState == MachineState_PoweredOff
14834 || aMachineState == MachineState_Teleported
14835 || aMachineState == MachineState_Aborted
14836 || aMachineState == MachineState_Saved))
14837 {
14838 /* the machine has stopped execution
14839 * (or the saved state file was adopted) */
14840 stsFlags |= SaveSTS_StateTimeStamp;
14841 }
14842
14843 if ( ( oldMachineState == MachineState_PoweredOff
14844 || oldMachineState == MachineState_Aborted
14845 || oldMachineState == MachineState_Teleported
14846 )
14847 && aMachineState == MachineState_Saved)
14848 {
14849 /* the saved state file was adopted */
14850 Assert(!mSSData->strStateFilePath.isEmpty());
14851 stsFlags |= SaveSTS_StateFilePath;
14852 }
14853
14854#ifdef VBOX_WITH_GUEST_PROPS
14855 if ( aMachineState == MachineState_PoweredOff
14856 || aMachineState == MachineState_Aborted
14857 || aMachineState == MachineState_Teleported)
14858 {
14859 /* Make sure any transient guest properties get removed from the
14860 * property store on shutdown. */
14861 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14862
14863 /* remove it from the settings representation */
14864 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14865 for (settings::GuestPropertiesList::iterator
14866 it = llGuestProperties.begin();
14867 it != llGuestProperties.end();
14868 /*nothing*/)
14869 {
14870 const settings::GuestProperty &prop = *it;
14871 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14872 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14873 {
14874 it = llGuestProperties.erase(it);
14875 fNeedsSaving = true;
14876 }
14877 else
14878 {
14879 ++it;
14880 }
14881 }
14882
14883 /* Additionally remove it from the HWData representation. Required to
14884 * keep everything in sync, as this is what the API keeps using. */
14885 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14886 for (HWData::GuestPropertyMap::iterator
14887 it = llHWGuestProperties.begin();
14888 it != llHWGuestProperties.end();
14889 /*nothing*/)
14890 {
14891 uint32_t fFlags = it->second.mFlags;
14892 if ( fFlags & guestProp::TRANSIENT
14893 || fFlags & guestProp::TRANSRESET)
14894 {
14895 /* iterator where we need to continue after the erase call
14896 * (C++03 is a fact still, and it doesn't return the iterator
14897 * which would allow continuing) */
14898 HWData::GuestPropertyMap::iterator it2 = it;
14899 ++it2;
14900 llHWGuestProperties.erase(it);
14901 it = it2;
14902 fNeedsSaving = true;
14903 }
14904 else
14905 {
14906 ++it;
14907 }
14908 }
14909
14910 if (fNeedsSaving)
14911 {
14912 mData->mCurrentStateModified = TRUE;
14913 stsFlags |= SaveSTS_CurStateModified;
14914 }
14915 }
14916#endif /* VBOX_WITH_GUEST_PROPS */
14917
14918 rc = i_saveStateSettings(stsFlags);
14919
14920 if ( ( oldMachineState != MachineState_PoweredOff
14921 && oldMachineState != MachineState_Aborted
14922 && oldMachineState != MachineState_Teleported
14923 )
14924 && ( aMachineState == MachineState_PoweredOff
14925 || aMachineState == MachineState_Aborted
14926 || aMachineState == MachineState_Teleported
14927 )
14928 )
14929 {
14930 /* we've been shut down for any reason */
14931 /* no special action so far */
14932 }
14933
14934 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14935 LogFlowThisFuncLeave();
14936 return rc;
14937}
14938
14939/**
14940 * Sends the current machine state value to the VM process.
14941 *
14942 * @note Locks this object for reading, then calls a client process.
14943 */
14944HRESULT SessionMachine::i_updateMachineStateOnClient()
14945{
14946 AutoCaller autoCaller(this);
14947 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14948
14949 ComPtr<IInternalSessionControl> directControl;
14950 {
14951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14952 AssertReturn(!!mData, E_FAIL);
14953 if (mData->mSession.mLockType == LockType_VM)
14954 directControl = mData->mSession.mDirectControl;
14955
14956 /* directControl may be already set to NULL here in #OnSessionEnd()
14957 * called too early by the direct session process while there is still
14958 * some operation (like deleting the snapshot) in progress. The client
14959 * process in this case is waiting inside Session::close() for the
14960 * "end session" process object to complete, while #uninit() called by
14961 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14962 * operation to complete. For now, we accept this inconsistent behavior
14963 * and simply do nothing here. */
14964
14965 if (mData->mSession.mState == SessionState_Unlocking)
14966 return S_OK;
14967 }
14968
14969 /* ignore notifications sent after #OnSessionEnd() is called */
14970 if (!directControl)
14971 return S_OK;
14972
14973 return directControl->UpdateMachineState(mData->mMachineState);
14974}
14975
14976
14977/*static*/
14978HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14979{
14980 va_list args;
14981 va_start(args, pcszMsg);
14982 HRESULT rc = setErrorInternal(aResultCode,
14983 getStaticClassIID(),
14984 getStaticComponentName(),
14985 Utf8Str(pcszMsg, args),
14986 false /* aWarning */,
14987 true /* aLogIt */);
14988 va_end(args);
14989 return rc;
14990}
14991
14992
14993HRESULT Machine::updateState(MachineState_T aState)
14994{
14995 NOREF(aState);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15000{
15001 NOREF(aProgress);
15002 ReturnComNotImplemented();
15003}
15004
15005HRESULT Machine::endPowerUp(LONG aResult)
15006{
15007 NOREF(aResult);
15008 ReturnComNotImplemented();
15009}
15010
15011HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15012{
15013 NOREF(aProgress);
15014 ReturnComNotImplemented();
15015}
15016
15017HRESULT Machine::endPoweringDown(LONG aResult,
15018 const com::Utf8Str &aErrMsg)
15019{
15020 NOREF(aResult);
15021 NOREF(aErrMsg);
15022 ReturnComNotImplemented();
15023}
15024
15025HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15026 BOOL *aMatched,
15027 ULONG *aMaskedInterfaces)
15028{
15029 NOREF(aDevice);
15030 NOREF(aMatched);
15031 NOREF(aMaskedInterfaces);
15032 ReturnComNotImplemented();
15033
15034}
15035
15036HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15037{
15038 NOREF(aId); NOREF(aCaptureFilename);
15039 ReturnComNotImplemented();
15040}
15041
15042HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15043 BOOL aDone)
15044{
15045 NOREF(aId);
15046 NOREF(aDone);
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::autoCaptureUSBDevices()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15056{
15057 NOREF(aDone);
15058 ReturnComNotImplemented();
15059}
15060
15061HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15062 ComPtr<IProgress> &aProgress)
15063{
15064 NOREF(aSession);
15065 NOREF(aProgress);
15066 ReturnComNotImplemented();
15067}
15068
15069HRESULT Machine::finishOnlineMergeMedium()
15070{
15071 ReturnComNotImplemented();
15072}
15073
15074HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15075 std::vector<com::Utf8Str> &aValues,
15076 std::vector<LONG64> &aTimestamps,
15077 std::vector<com::Utf8Str> &aFlags)
15078{
15079 NOREF(aNames);
15080 NOREF(aValues);
15081 NOREF(aTimestamps);
15082 NOREF(aFlags);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15087 const com::Utf8Str &aValue,
15088 LONG64 aTimestamp,
15089 const com::Utf8Str &aFlags)
15090{
15091 NOREF(aName);
15092 NOREF(aValue);
15093 NOREF(aTimestamp);
15094 NOREF(aFlags);
15095 ReturnComNotImplemented();
15096}
15097
15098HRESULT Machine::lockMedia()
15099{
15100 ReturnComNotImplemented();
15101}
15102
15103HRESULT Machine::unlockMedia()
15104{
15105 ReturnComNotImplemented();
15106}
15107
15108HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15109 ComPtr<IMediumAttachment> &aNewAttachment)
15110{
15111 NOREF(aAttachment);
15112 NOREF(aNewAttachment);
15113 ReturnComNotImplemented();
15114}
15115
15116HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15117 ULONG aCpuUser,
15118 ULONG aCpuKernel,
15119 ULONG aCpuIdle,
15120 ULONG aMemTotal,
15121 ULONG aMemFree,
15122 ULONG aMemBalloon,
15123 ULONG aMemShared,
15124 ULONG aMemCache,
15125 ULONG aPagedTotal,
15126 ULONG aMemAllocTotal,
15127 ULONG aMemFreeTotal,
15128 ULONG aMemBalloonTotal,
15129 ULONG aMemSharedTotal,
15130 ULONG aVmNetRx,
15131 ULONG aVmNetTx)
15132{
15133 NOREF(aValidStats);
15134 NOREF(aCpuUser);
15135 NOREF(aCpuKernel);
15136 NOREF(aCpuIdle);
15137 NOREF(aMemTotal);
15138 NOREF(aMemFree);
15139 NOREF(aMemBalloon);
15140 NOREF(aMemShared);
15141 NOREF(aMemCache);
15142 NOREF(aPagedTotal);
15143 NOREF(aMemAllocTotal);
15144 NOREF(aMemFreeTotal);
15145 NOREF(aMemBalloonTotal);
15146 NOREF(aMemSharedTotal);
15147 NOREF(aVmNetRx);
15148 NOREF(aVmNetTx);
15149 ReturnComNotImplemented();
15150}
15151
15152HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15153 com::Utf8Str &aResult)
15154{
15155 NOREF(aAuthParams);
15156 NOREF(aResult);
15157 ReturnComNotImplemented();
15158}
15159
15160HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15161{
15162 NOREF(aFlags);
15163 ReturnComNotImplemented();
15164}
15165
15166/* This isn't handled entirely by the wrapper generator yet. */
15167#ifdef VBOX_WITH_XPCOM
15168NS_DECL_CLASSINFO(SessionMachine)
15169NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15170
15171NS_DECL_CLASSINFO(SnapshotMachine)
15172NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15173#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