VirtualBox

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

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

Main/VirtualBox+Machine: Change medium registry behavior, put new media into global registry until it is associated with a VM. This avoids the annoying glitch that the media registry can forget something unexpectedly if VBoxSVC terminates.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 526.6 KB
Line 
1/* $Id: MachineImpl.cpp 72205 2018-05-14 18:37:50Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/* Make sure all the stdint.h macros are included - must come first! */
19#ifndef __STDC_LIMIT_MACROS
20# define __STDC_LIMIT_MACROS
21#endif
22#ifndef __STDC_CONSTANT_MACROS
23# define __STDC_CONSTANT_MACROS
24#endif
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47#include "MachineImplMoveVM.h"
48
49// generated header
50#include "VBoxEvents.h"
51
52#ifdef VBOX_WITH_USB
53# include "USBProxyService.h"
54#endif
55
56#include "AutoCaller.h"
57#include "HashedPw.h"
58#include "Performance.h"
59
60#include <iprt/asm.h>
61#include <iprt/path.h>
62#include <iprt/dir.h>
63#include <iprt/env.h>
64#include <iprt/lockvalidator.h>
65#include <iprt/process.h>
66#include <iprt/cpp/utils.h>
67#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
68#include <iprt/sha.h>
69#include <iprt/string.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mAPIC = true;
197 mX2APIC = false;
198 mIBPBOnVMExit = false;
199 mIBPBOnVMEntry = false;
200 mSpecCtrl = false;
201 mSpecCtrlByHost = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mDnDMode = DnDMode_Disabled;
217
218 mFirmwareType = FirmwareType_BIOS;
219 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
220 mPointingHIDType = PointingHIDType_PS2Mouse;
221 mChipsetType = ChipsetType_PIIX3;
222 mParavirtProvider = ParavirtProvider_Default;
223 mEmulatedUSBCardReaderEnabled = FALSE;
224
225 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
226 mCPUAttached[i] = false;
227
228 mIOCacheEnabled = true;
229 mIOCacheSize = 5; /* 5MB */
230}
231
232Machine::HWData::~HWData()
233{
234}
235
236/////////////////////////////////////////////////////////////////////////////
237// Machine class
238/////////////////////////////////////////////////////////////////////////////
239
240// constructor / destructor
241/////////////////////////////////////////////////////////////////////////////
242
243Machine::Machine() :
244#ifdef VBOX_WITH_RESOURCE_USAGE_API
245 mCollectorGuest(NULL),
246#endif
247 mPeer(NULL),
248 mParent(NULL),
249 mSerialPorts(),
250 mParallelPorts(),
251 uRegistryNeedsSaving(0)
252{}
253
254Machine::~Machine()
255{}
256
257HRESULT Machine::FinalConstruct()
258{
259 LogFlowThisFunc(("\n"));
260 return BaseFinalConstruct();
261}
262
263void Machine::FinalRelease()
264{
265 LogFlowThisFunc(("\n"));
266 uninit();
267 BaseFinalRelease();
268}
269
270/**
271 * Initializes a new machine instance; this init() variant creates a new, empty machine.
272 * This gets called from VirtualBox::CreateMachine().
273 *
274 * @param aParent Associated parent object
275 * @param strConfigFile Local file system path to the VM settings file (can
276 * be relative to the VirtualBox config directory).
277 * @param strName name for the machine
278 * @param llGroups list of groups for the machine
279 * @param aOsType OS Type of this machine or NULL.
280 * @param aId UUID for the new machine.
281 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
282 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
283 * scheme (includes the UUID).
284 *
285 * @return Success indicator. if not S_OK, the machine object is invalid
286 */
287HRESULT Machine::init(VirtualBox *aParent,
288 const Utf8Str &strConfigFile,
289 const Utf8Str &strName,
290 const StringsList &llGroups,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 mUserData->s.llGroups = llGroups;
327
328 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
329 // the "name sync" flag determines whether the machine directory gets renamed along
330 // with the machine file; say so if the settings file name is the same as the
331 // settings file parent directory (machine directory)
332 mUserData->s.fNameSync = i_isInOwnDir();
333
334 // initialize the default snapshots folder
335 rc = COMSETTER(SnapshotFolder)(NULL);
336 AssertComRC(rc);
337
338 if (aOsType)
339 {
340 /* Store OS type */
341 mUserData->s.strOsType = aOsType->i_id();
342
343 /* Apply BIOS defaults */
344 mBIOSSettings->i_applyDefaults(aOsType);
345
346 /* Let the OS type select 64-bit ness. */
347 mHWData->mLongMode = aOsType->i_is64Bit()
348 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
349
350 /* Let the OS type enable the X2APIC */
351 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
352 }
353
354 /* Apply network adapters defaults */
355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
356 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
357
358 /* Apply serial port defaults */
359 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
360 mSerialPorts[slot]->i_applyDefaults(aOsType);
361
362 /* Apply parallel port defaults */
363 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
364 mParallelPorts[slot]->i_applyDefaults();
365
366 /* At this point the changing of the current state modification
367 * flag is allowed. */
368 i_allowStateModification();
369
370 /* commit all changes made during the initialization */
371 i_commit();
372 }
373
374 /* Confirm a successful initialization when it's the case */
375 if (SUCCEEDED(rc))
376 {
377 if (mData->mAccessible)
378 autoInitSpan.setSucceeded();
379 else
380 autoInitSpan.setLimited();
381 }
382
383 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
384 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
385 mData->mRegistered,
386 mData->mAccessible,
387 rc));
388
389 LogFlowThisFuncLeave();
390
391 return rc;
392}
393
394/**
395 * Initializes a new instance with data from machine XML (formerly Init_Registered).
396 * Gets called in two modes:
397 *
398 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
399 * UUID is specified and we mark the machine as "registered";
400 *
401 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
402 * and the machine remains unregistered until RegisterMachine() is called.
403 *
404 * @param aParent Associated parent object
405 * @param strConfigFile Local file system path to the VM settings file (can
406 * be relative to the VirtualBox config directory).
407 * @param aId UUID of the machine or NULL (see above).
408 *
409 * @return Success indicator. if not S_OK, the machine object is invalid
410 */
411HRESULT Machine::initFromSettings(VirtualBox *aParent,
412 const Utf8Str &strConfigFile,
413 const Guid *aId)
414{
415 LogFlowThisFuncEnter();
416 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
417
418 /* Enclose the state transition NotReady->InInit->Ready */
419 AutoInitSpan autoInitSpan(this);
420 AssertReturn(autoInitSpan.isOk(), E_FAIL);
421
422 HRESULT rc = initImpl(aParent, strConfigFile);
423 if (FAILED(rc)) return rc;
424
425 if (aId)
426 {
427 // loading a registered VM:
428 unconst(mData->mUuid) = *aId;
429 mData->mRegistered = TRUE;
430 // now load the settings from XML:
431 rc = i_registeredInit();
432 // this calls initDataAndChildObjects() and loadSettings()
433 }
434 else
435 {
436 // opening an unregistered VM (VirtualBox::OpenMachine()):
437 rc = initDataAndChildObjects();
438
439 if (SUCCEEDED(rc))
440 {
441 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
442 mData->mAccessible = TRUE;
443
444 try
445 {
446 // load and parse machine XML; this will throw on XML or logic errors
447 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
448
449 // reject VM UUID duplicates, they can happen if someone
450 // tries to register an already known VM config again
451 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
452 true /* fPermitInaccessible */,
453 false /* aDoSetError */,
454 NULL) != VBOX_E_OBJECT_NOT_FOUND)
455 {
456 throw setError(E_FAIL,
457 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
458 mData->m_strConfigFile.c_str());
459 }
460
461 // use UUID from machine config
462 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
463
464 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
465 NULL /* puuidRegistry */);
466 if (FAILED(rc)) throw rc;
467
468 /* At this point the changing of the current state modification
469 * flag is allowed. */
470 i_allowStateModification();
471
472 i_commit();
473 }
474 catch (HRESULT err)
475 {
476 /* we assume that error info is set by the thrower */
477 rc = err;
478 }
479 catch (...)
480 {
481 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
482 }
483 }
484 }
485
486 /* Confirm a successful initialization when it's the case */
487 if (SUCCEEDED(rc))
488 {
489 if (mData->mAccessible)
490 autoInitSpan.setSucceeded();
491 else
492 {
493 autoInitSpan.setLimited();
494
495 // uninit media from this machine's media registry, or else
496 // reloading the settings will fail
497 mParent->i_unregisterMachineMedia(i_getId());
498 }
499 }
500
501 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
502 "rc=%08X\n",
503 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
504 mData->mRegistered, mData->mAccessible, rc));
505
506 LogFlowThisFuncLeave();
507
508 return rc;
509}
510
511/**
512 * Initializes a new instance from a machine config that is already in memory
513 * (import OVF case). Since we are importing, the UUID in the machine
514 * config is ignored and we always generate a fresh one.
515 *
516 * @param aParent Associated parent object.
517 * @param strName Name for the new machine; this overrides what is specified in config and is used
518 * for the settings file as well.
519 * @param config Machine configuration loaded and parsed from XML.
520 *
521 * @return Success indicator. if not S_OK, the machine object is invalid
522 */
523HRESULT Machine::init(VirtualBox *aParent,
524 const Utf8Str &strName,
525 const settings::MachineConfigFile &config)
526{
527 LogFlowThisFuncEnter();
528
529 /* Enclose the state transition NotReady->InInit->Ready */
530 AutoInitSpan autoInitSpan(this);
531 AssertReturn(autoInitSpan.isOk(), E_FAIL);
532
533 Utf8Str strConfigFile;
534 aParent->i_getDefaultMachineFolder(strConfigFile);
535 strConfigFile.append(RTPATH_DELIMITER);
536 strConfigFile.append(strName);
537 strConfigFile.append(RTPATH_DELIMITER);
538 strConfigFile.append(strName);
539 strConfigFile.append(".vbox");
540
541 HRESULT rc = initImpl(aParent, strConfigFile);
542 if (FAILED(rc)) return rc;
543
544 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
545 if (FAILED(rc)) return rc;
546
547 rc = initDataAndChildObjects();
548
549 if (SUCCEEDED(rc))
550 {
551 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
552 mData->mAccessible = TRUE;
553
554 // create empty machine config for instance data
555 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
556
557 // generate fresh UUID, ignore machine config
558 unconst(mData->mUuid).create();
559
560 rc = i_loadMachineDataFromSettings(config,
561 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
562
563 // override VM name as well, it may be different
564 mUserData->s.strName = strName;
565
566 if (SUCCEEDED(rc))
567 {
568 /* At this point the changing of the current state modification
569 * flag is allowed. */
570 i_allowStateModification();
571
572 /* commit all changes made during the initialization */
573 i_commit();
574 }
575 }
576
577 /* Confirm a successful initialization when it's the case */
578 if (SUCCEEDED(rc))
579 {
580 if (mData->mAccessible)
581 autoInitSpan.setSucceeded();
582 else
583 {
584 /* Ignore all errors from unregistering, they would destroy
585- * the more interesting error information we already have,
586- * pinpointing the issue with the VM config. */
587 ErrorInfoKeeper eik;
588
589 autoInitSpan.setLimited();
590
591 // uninit media from this machine's media registry, or else
592 // reloading the settings will fail
593 mParent->i_unregisterMachineMedia(i_getId());
594 }
595 }
596
597 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
598 "rc=%08X\n",
599 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
600 mData->mRegistered, mData->mAccessible, rc));
601
602 LogFlowThisFuncLeave();
603
604 return rc;
605}
606
607/**
608 * Shared code between the various init() implementations.
609 * @param aParent The VirtualBox object.
610 * @param strConfigFile Settings file.
611 * @return
612 */
613HRESULT Machine::initImpl(VirtualBox *aParent,
614 const Utf8Str &strConfigFile)
615{
616 LogFlowThisFuncEnter();
617
618 AssertReturn(aParent, E_INVALIDARG);
619 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
620
621 HRESULT rc = S_OK;
622
623 /* share the parent weakly */
624 unconst(mParent) = aParent;
625
626 /* allocate the essential machine data structure (the rest will be
627 * allocated later by initDataAndChildObjects() */
628 mData.allocate();
629
630 /* memorize the config file name (as provided) */
631 mData->m_strConfigFile = strConfigFile;
632
633 /* get the full file name */
634 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
635 if (RT_FAILURE(vrc1))
636 return setError(VBOX_E_FILE_ERROR,
637 tr("Invalid machine settings file name '%s' (%Rrc)"),
638 strConfigFile.c_str(),
639 vrc1);
640
641 LogFlowThisFuncLeave();
642
643 return rc;
644}
645
646/**
647 * Tries to create a machine settings file in the path stored in the machine
648 * instance data. Used when a new machine is created to fail gracefully if
649 * the settings file could not be written (e.g. because machine dir is read-only).
650 * @return
651 */
652HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
653{
654 HRESULT rc = S_OK;
655
656 // when we create a new machine, we must be able to create the settings file
657 RTFILE f = NIL_RTFILE;
658 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
659 if ( RT_SUCCESS(vrc)
660 || vrc == VERR_SHARING_VIOLATION
661 )
662 {
663 if (RT_SUCCESS(vrc))
664 RTFileClose(f);
665 if (!fForceOverwrite)
666 rc = setError(VBOX_E_FILE_ERROR,
667 tr("Machine settings file '%s' already exists"),
668 mData->m_strConfigFileFull.c_str());
669 else
670 {
671 /* try to delete the config file, as otherwise the creation
672 * of a new settings file will fail. */
673 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
674 if (RT_FAILURE(vrc2))
675 rc = setError(VBOX_E_FILE_ERROR,
676 tr("Could not delete the existing settings file '%s' (%Rrc)"),
677 mData->m_strConfigFileFull.c_str(), vrc2);
678 }
679 }
680 else if ( vrc != VERR_FILE_NOT_FOUND
681 && vrc != VERR_PATH_NOT_FOUND
682 )
683 rc = setError(VBOX_E_FILE_ERROR,
684 tr("Invalid machine settings file name '%s' (%Rrc)"),
685 mData->m_strConfigFileFull.c_str(),
686 vrc);
687 return rc;
688}
689
690/**
691 * Initializes the registered machine by loading the settings file.
692 * This method is separated from #init() in order to make it possible to
693 * retry the operation after VirtualBox startup instead of refusing to
694 * startup the whole VirtualBox server in case if the settings file of some
695 * registered VM is invalid or inaccessible.
696 *
697 * @note Must be always called from this object's write lock
698 * (unless called from #init() that doesn't need any locking).
699 * @note Locks the mUSBController method for writing.
700 * @note Subclasses must not call this method.
701 */
702HRESULT Machine::i_registeredInit()
703{
704 AssertReturn(!i_isSessionMachine(), E_FAIL);
705 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
706 AssertReturn(mData->mUuid.isValid(), E_FAIL);
707 AssertReturn(!mData->mAccessible, E_FAIL);
708
709 HRESULT rc = initDataAndChildObjects();
710
711 if (SUCCEEDED(rc))
712 {
713 /* Temporarily reset the registered flag in order to let setters
714 * potentially called from loadSettings() succeed (isMutable() used in
715 * all setters will return FALSE for a Machine instance if mRegistered
716 * is TRUE). */
717 mData->mRegistered = FALSE;
718
719 try
720 {
721 // load and parse machine XML; this will throw on XML or logic errors
722 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
723
724 if (mData->mUuid != mData->pMachineConfigFile->uuid)
725 throw setError(E_FAIL,
726 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
727 mData->pMachineConfigFile->uuid.raw(),
728 mData->m_strConfigFileFull.c_str(),
729 mData->mUuid.toString().c_str(),
730 mParent->i_settingsFilePath().c_str());
731
732 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
733 NULL /* const Guid *puuidRegistry */);
734 if (FAILED(rc)) throw rc;
735 }
736 catch (HRESULT err)
737 {
738 /* we assume that error info is set by the thrower */
739 rc = err;
740 }
741 catch (...)
742 {
743 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
744 }
745
746 /* Restore the registered flag (even on failure) */
747 mData->mRegistered = TRUE;
748 }
749
750 if (SUCCEEDED(rc))
751 {
752 /* Set mAccessible to TRUE only if we successfully locked and loaded
753 * the settings file */
754 mData->mAccessible = TRUE;
755
756 /* commit all changes made during loading the settings file */
757 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
758 /// @todo r=klaus for some reason the settings loading logic backs up
759 // the settings, and therefore a commit is needed. Should probably be changed.
760 }
761 else
762 {
763 /* If the machine is registered, then, instead of returning a
764 * failure, we mark it as inaccessible and set the result to
765 * success to give it a try later */
766
767 /* fetch the current error info */
768 mData->mAccessError = com::ErrorInfo();
769 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
770
771 /* rollback all changes */
772 i_rollback(false /* aNotify */);
773
774 // uninit media from this machine's media registry, or else
775 // reloading the settings will fail
776 mParent->i_unregisterMachineMedia(i_getId());
777
778 /* uninitialize the common part to make sure all data is reset to
779 * default (null) values */
780 uninitDataAndChildObjects();
781
782 rc = S_OK;
783 }
784
785 return rc;
786}
787
788/**
789 * Uninitializes the instance.
790 * Called either from FinalRelease() or by the parent when it gets destroyed.
791 *
792 * @note The caller of this method must make sure that this object
793 * a) doesn't have active callers on the current thread and b) is not locked
794 * by the current thread; otherwise uninit() will hang either a) due to
795 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
796 * a dead-lock caused by this thread waiting for all callers on the other
797 * threads are done but preventing them from doing so by holding a lock.
798 */
799void Machine::uninit()
800{
801 LogFlowThisFuncEnter();
802
803 Assert(!isWriteLockOnCurrentThread());
804
805 Assert(!uRegistryNeedsSaving);
806 if (uRegistryNeedsSaving)
807 {
808 AutoCaller autoCaller(this);
809 if (SUCCEEDED(autoCaller.rc()))
810 {
811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
812 i_saveSettings(NULL, Machine::SaveS_Force);
813 }
814 }
815
816 /* Enclose the state transition Ready->InUninit->NotReady */
817 AutoUninitSpan autoUninitSpan(this);
818 if (autoUninitSpan.uninitDone())
819 return;
820
821 Assert(!i_isSnapshotMachine());
822 Assert(!i_isSessionMachine());
823 Assert(!!mData);
824
825 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
826 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
827
828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
829
830 if (!mData->mSession.mMachine.isNull())
831 {
832 /* Theoretically, this can only happen if the VirtualBox server has been
833 * terminated while there were clients running that owned open direct
834 * sessions. Since in this case we are definitely called by
835 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
836 * won't happen on the client watcher thread (because it has a
837 * VirtualBox caller for the duration of the
838 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
839 * cannot happen until the VirtualBox caller is released). This is
840 * important, because SessionMachine::uninit() cannot correctly operate
841 * after we return from this method (it expects the Machine instance is
842 * still valid). We'll call it ourselves below.
843 */
844 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
845 (SessionMachine*)mData->mSession.mMachine));
846
847 if (Global::IsOnlineOrTransient(mData->mMachineState))
848 {
849 Log1WarningThisFunc(("Setting state to Aborted!\n"));
850 /* set machine state using SessionMachine reimplementation */
851 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
852 }
853
854 /*
855 * Uninitialize SessionMachine using public uninit() to indicate
856 * an unexpected uninitialization.
857 */
858 mData->mSession.mMachine->uninit();
859 /* SessionMachine::uninit() must set mSession.mMachine to null */
860 Assert(mData->mSession.mMachine.isNull());
861 }
862
863 // uninit media from this machine's media registry, if they're still there
864 Guid uuidMachine(i_getId());
865
866 /* the lock is no more necessary (SessionMachine is uninitialized) */
867 alock.release();
868
869 /* XXX This will fail with
870 * "cannot be closed because it is still attached to 1 virtual machines"
871 * because at this point we did not call uninitDataAndChildObjects() yet
872 * and therefore also removeBackReference() for all these mediums was not called! */
873
874 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
875 mParent->i_unregisterMachineMedia(uuidMachine);
876
877 // has machine been modified?
878 if (mData->flModifications)
879 {
880 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
881 i_rollback(false /* aNotify */);
882 }
883
884 if (mData->mAccessible)
885 uninitDataAndChildObjects();
886
887 /* free the essential data structure last */
888 mData.free();
889
890 LogFlowThisFuncLeave();
891}
892
893// Wrapped IMachine properties
894/////////////////////////////////////////////////////////////////////////////
895HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
896{
897 /* mParent is constant during life time, no need to lock */
898 ComObjPtr<VirtualBox> pVirtualBox(mParent);
899 aParent = pVirtualBox;
900
901 return S_OK;
902}
903
904
905HRESULT Machine::getAccessible(BOOL *aAccessible)
906{
907 /* In some cases (medium registry related), it is necessary to be able to
908 * go through the list of all machines. Happens when an inaccessible VM
909 * has a sensible medium registry. */
910 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
912
913 HRESULT rc = S_OK;
914
915 if (!mData->mAccessible)
916 {
917 /* try to initialize the VM once more if not accessible */
918
919 AutoReinitSpan autoReinitSpan(this);
920 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
921
922#ifdef DEBUG
923 LogFlowThisFunc(("Dumping media backreferences\n"));
924 mParent->i_dumpAllBackRefs();
925#endif
926
927 if (mData->pMachineConfigFile)
928 {
929 // reset the XML file to force loadSettings() (called from i_registeredInit())
930 // to parse it again; the file might have changed
931 delete mData->pMachineConfigFile;
932 mData->pMachineConfigFile = NULL;
933 }
934
935 rc = i_registeredInit();
936
937 if (SUCCEEDED(rc) && mData->mAccessible)
938 {
939 autoReinitSpan.setSucceeded();
940
941 /* make sure interesting parties will notice the accessibility
942 * state change */
943 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
944 mParent->i_onMachineDataChange(mData->mUuid);
945 }
946 }
947
948 if (SUCCEEDED(rc))
949 *aAccessible = mData->mAccessible;
950
951 LogFlowThisFuncLeave();
952
953 return rc;
954}
955
956HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
957{
958 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
959
960 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
961 {
962 /* return shortly */
963 aAccessError = NULL;
964 return S_OK;
965 }
966
967 HRESULT rc = S_OK;
968
969 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
970 rc = errorInfo.createObject();
971 if (SUCCEEDED(rc))
972 {
973 errorInfo->init(mData->mAccessError.getResultCode(),
974 mData->mAccessError.getInterfaceID().ref(),
975 Utf8Str(mData->mAccessError.getComponent()).c_str(),
976 Utf8Str(mData->mAccessError.getText()));
977 aAccessError = errorInfo;
978 }
979
980 return rc;
981}
982
983HRESULT Machine::getName(com::Utf8Str &aName)
984{
985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
986
987 aName = mUserData->s.strName;
988
989 return S_OK;
990}
991
992HRESULT Machine::setName(const com::Utf8Str &aName)
993{
994 // prohibit setting a UUID only as the machine name, or else it can
995 // never be found by findMachine()
996 Guid test(aName);
997
998 if (test.isValid())
999 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1000
1001 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1002
1003 HRESULT rc = i_checkStateDependency(MutableStateDep);
1004 if (FAILED(rc)) return rc;
1005
1006 i_setModified(IsModified_MachineData);
1007 mUserData.backup();
1008 mUserData->s.strName = aName;
1009
1010 return S_OK;
1011}
1012
1013HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1014{
1015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1016
1017 aDescription = mUserData->s.strDescription;
1018
1019 return S_OK;
1020}
1021
1022HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1023{
1024 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1025
1026 // this can be done in principle in any state as it doesn't affect the VM
1027 // significantly, but play safe by not messing around while complex
1028 // activities are going on
1029 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1030 if (FAILED(rc)) return rc;
1031
1032 i_setModified(IsModified_MachineData);
1033 mUserData.backup();
1034 mUserData->s.strDescription = aDescription;
1035
1036 return S_OK;
1037}
1038
1039HRESULT Machine::getId(com::Guid &aId)
1040{
1041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1042
1043 aId = mData->mUuid;
1044
1045 return S_OK;
1046}
1047
1048HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1049{
1050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1051 aGroups.resize(mUserData->s.llGroups.size());
1052 size_t i = 0;
1053 for (StringsList::const_iterator
1054 it = mUserData->s.llGroups.begin();
1055 it != mUserData->s.llGroups.end();
1056 ++it, ++i)
1057 aGroups[i] = (*it);
1058
1059 return S_OK;
1060}
1061
1062HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1063{
1064 StringsList llGroups;
1065 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1066 if (FAILED(rc))
1067 return rc;
1068
1069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1070
1071 rc = i_checkStateDependency(MutableOrSavedStateDep);
1072 if (FAILED(rc)) return rc;
1073
1074 i_setModified(IsModified_MachineData);
1075 mUserData.backup();
1076 mUserData->s.llGroups = llGroups;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1082{
1083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1084
1085 aOSTypeId = mUserData->s.strOsType;
1086
1087 return S_OK;
1088}
1089
1090HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1091{
1092 /* look up the object by Id to check it is valid */
1093 ComObjPtr<GuestOSType> pGuestOSType;
1094 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1095 pGuestOSType);
1096 if (FAILED(rc)) return rc;
1097
1098 /* when setting, always use the "etalon" value for consistency -- lookup
1099 * by ID is case-insensitive and the input value may have different case */
1100 Utf8Str osTypeId = pGuestOSType->i_id();
1101
1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1103
1104 rc = i_checkStateDependency(MutableStateDep);
1105 if (FAILED(rc)) return rc;
1106
1107 i_setModified(IsModified_MachineData);
1108 mUserData.backup();
1109 mUserData->s.strOsType = osTypeId;
1110
1111 return S_OK;
1112}
1113
1114HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1115{
1116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1117
1118 *aFirmwareType = mHWData->mFirmwareType;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1124{
1125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 HRESULT rc = i_checkStateDependency(MutableStateDep);
1128 if (FAILED(rc)) return rc;
1129
1130 i_setModified(IsModified_MachineData);
1131 mHWData.backup();
1132 mHWData->mFirmwareType = aFirmwareType;
1133
1134 return S_OK;
1135}
1136
1137HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1138{
1139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1140
1141 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1142
1143 return S_OK;
1144}
1145
1146HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1147{
1148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1149
1150 HRESULT rc = i_checkStateDependency(MutableStateDep);
1151 if (FAILED(rc)) return rc;
1152
1153 i_setModified(IsModified_MachineData);
1154 mHWData.backup();
1155 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1156
1157 return S_OK;
1158}
1159
1160HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1161{
1162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1163
1164 *aPointingHIDType = mHWData->mPointingHIDType;
1165
1166 return S_OK;
1167}
1168
1169HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1170{
1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1172
1173 HRESULT rc = i_checkStateDependency(MutableStateDep);
1174 if (FAILED(rc)) return rc;
1175
1176 i_setModified(IsModified_MachineData);
1177 mHWData.backup();
1178 mHWData->mPointingHIDType = aPointingHIDType;
1179
1180 return S_OK;
1181}
1182
1183HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1184{
1185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1186
1187 *aChipsetType = mHWData->mChipsetType;
1188
1189 return S_OK;
1190}
1191
1192HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1193{
1194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1195
1196 HRESULT rc = i_checkStateDependency(MutableStateDep);
1197 if (FAILED(rc)) return rc;
1198
1199 if (aChipsetType != mHWData->mChipsetType)
1200 {
1201 i_setModified(IsModified_MachineData);
1202 mHWData.backup();
1203 mHWData->mChipsetType = aChipsetType;
1204
1205 // Resize network adapter array, to be finalized on commit/rollback.
1206 // We must not throw away entries yet, otherwise settings are lost
1207 // without a way to roll back.
1208 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1209 size_t oldCount = mNetworkAdapters.size();
1210 if (newCount > oldCount)
1211 {
1212 mNetworkAdapters.resize(newCount);
1213 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1214 {
1215 unconst(mNetworkAdapters[slot]).createObject();
1216 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1217 }
1218 }
1219 }
1220
1221 return S_OK;
1222}
1223
1224HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1225{
1226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1227
1228 aParavirtDebug = mHWData->mParavirtDebug;
1229 return S_OK;
1230}
1231
1232HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1233{
1234 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 HRESULT rc = i_checkStateDependency(MutableStateDep);
1237 if (FAILED(rc)) return rc;
1238
1239 /** @todo Parse/validate options? */
1240 if (aParavirtDebug != mHWData->mParavirtDebug)
1241 {
1242 i_setModified(IsModified_MachineData);
1243 mHWData.backup();
1244 mHWData->mParavirtDebug = aParavirtDebug;
1245 }
1246
1247 return S_OK;
1248}
1249
1250HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1251{
1252 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1253
1254 *aParavirtProvider = mHWData->mParavirtProvider;
1255
1256 return S_OK;
1257}
1258
1259HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1260{
1261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1262
1263 HRESULT rc = i_checkStateDependency(MutableStateDep);
1264 if (FAILED(rc)) return rc;
1265
1266 if (aParavirtProvider != mHWData->mParavirtProvider)
1267 {
1268 i_setModified(IsModified_MachineData);
1269 mHWData.backup();
1270 mHWData->mParavirtProvider = aParavirtProvider;
1271 }
1272
1273 return S_OK;
1274}
1275
1276HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1277{
1278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1279
1280 *aParavirtProvider = mHWData->mParavirtProvider;
1281 switch (mHWData->mParavirtProvider)
1282 {
1283 case ParavirtProvider_None:
1284 case ParavirtProvider_HyperV:
1285 case ParavirtProvider_KVM:
1286 case ParavirtProvider_Minimal:
1287 break;
1288
1289 /* Resolve dynamic provider types to the effective types. */
1290 default:
1291 {
1292 ComObjPtr<GuestOSType> pGuestOSType;
1293 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1294 pGuestOSType);
1295 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1296
1297 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1298 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1299
1300 switch (mHWData->mParavirtProvider)
1301 {
1302 case ParavirtProvider_Legacy:
1303 {
1304 if (fOsXGuest)
1305 *aParavirtProvider = ParavirtProvider_Minimal;
1306 else
1307 *aParavirtProvider = ParavirtProvider_None;
1308 break;
1309 }
1310
1311 case ParavirtProvider_Default:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else if ( mUserData->s.strOsType == "Windows10"
1316 || mUserData->s.strOsType == "Windows10_64"
1317 || mUserData->s.strOsType == "Windows81"
1318 || mUserData->s.strOsType == "Windows81_64"
1319 || mUserData->s.strOsType == "Windows8"
1320 || mUserData->s.strOsType == "Windows8_64"
1321 || mUserData->s.strOsType == "Windows7"
1322 || mUserData->s.strOsType == "Windows7_64"
1323 || mUserData->s.strOsType == "WindowsVista"
1324 || mUserData->s.strOsType == "WindowsVista_64"
1325 || mUserData->s.strOsType == "Windows2012"
1326 || mUserData->s.strOsType == "Windows2012_64"
1327 || mUserData->s.strOsType == "Windows2008"
1328 || mUserData->s.strOsType == "Windows2008_64")
1329 {
1330 *aParavirtProvider = ParavirtProvider_HyperV;
1331 }
1332 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1333 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1334 || mUserData->s.strOsType == "Linux"
1335 || mUserData->s.strOsType == "Linux_64"
1336 || mUserData->s.strOsType == "ArchLinux"
1337 || mUserData->s.strOsType == "ArchLinux_64"
1338 || mUserData->s.strOsType == "Debian"
1339 || mUserData->s.strOsType == "Debian_64"
1340 || mUserData->s.strOsType == "Fedora"
1341 || mUserData->s.strOsType == "Fedora_64"
1342 || mUserData->s.strOsType == "Gentoo"
1343 || mUserData->s.strOsType == "Gentoo_64"
1344 || mUserData->s.strOsType == "Mandriva"
1345 || mUserData->s.strOsType == "Mandriva_64"
1346 || mUserData->s.strOsType == "OpenSUSE"
1347 || mUserData->s.strOsType == "OpenSUSE_64"
1348 || mUserData->s.strOsType == "Oracle"
1349 || mUserData->s.strOsType == "Oracle_64"
1350 || mUserData->s.strOsType == "RedHat"
1351 || mUserData->s.strOsType == "RedHat_64"
1352 || mUserData->s.strOsType == "Turbolinux"
1353 || mUserData->s.strOsType == "Turbolinux_64"
1354 || mUserData->s.strOsType == "Ubuntu"
1355 || mUserData->s.strOsType == "Ubuntu_64"
1356 || mUserData->s.strOsType == "Xandros"
1357 || mUserData->s.strOsType == "Xandros_64")
1358 {
1359 *aParavirtProvider = ParavirtProvider_KVM;
1360 }
1361 else
1362 *aParavirtProvider = ParavirtProvider_None;
1363 break;
1364 }
1365
1366 default: AssertFailedBreak(); /* Shut up MSC. */
1367 }
1368 break;
1369 }
1370 }
1371
1372 Assert( *aParavirtProvider == ParavirtProvider_None
1373 || *aParavirtProvider == ParavirtProvider_Minimal
1374 || *aParavirtProvider == ParavirtProvider_HyperV
1375 || *aParavirtProvider == ParavirtProvider_KVM);
1376 return S_OK;
1377}
1378
1379HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1380{
1381 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1382
1383 aHardwareVersion = mHWData->mHWVersion;
1384
1385 return S_OK;
1386}
1387
1388HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1389{
1390 /* check known version */
1391 Utf8Str hwVersion = aHardwareVersion;
1392 if ( hwVersion.compare("1") != 0
1393 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1394 return setError(E_INVALIDARG,
1395 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1396
1397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1398
1399 HRESULT rc = i_checkStateDependency(MutableStateDep);
1400 if (FAILED(rc)) return rc;
1401
1402 i_setModified(IsModified_MachineData);
1403 mHWData.backup();
1404 mHWData->mHWVersion = aHardwareVersion;
1405
1406 return S_OK;
1407}
1408
1409HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1410{
1411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1412
1413 if (!mHWData->mHardwareUUID.isZero())
1414 aHardwareUUID = mHWData->mHardwareUUID;
1415 else
1416 aHardwareUUID = mData->mUuid;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1422{
1423 if (!aHardwareUUID.isValid())
1424 return E_INVALIDARG;
1425
1426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 HRESULT rc = i_checkStateDependency(MutableStateDep);
1429 if (FAILED(rc)) return rc;
1430
1431 i_setModified(IsModified_MachineData);
1432 mHWData.backup();
1433 if (aHardwareUUID == mData->mUuid)
1434 mHWData->mHardwareUUID.clear();
1435 else
1436 mHWData->mHardwareUUID = aHardwareUUID;
1437
1438 return S_OK;
1439}
1440
1441HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1442{
1443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 *aMemorySize = mHWData->mMemorySize;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::setMemorySize(ULONG aMemorySize)
1451{
1452 /* check RAM limits */
1453 if ( aMemorySize < MM_RAM_MIN_IN_MB
1454 || aMemorySize > MM_RAM_MAX_IN_MB
1455 )
1456 return setError(E_INVALIDARG,
1457 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1458 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1459
1460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 HRESULT rc = i_checkStateDependency(MutableStateDep);
1463 if (FAILED(rc)) return rc;
1464
1465 i_setModified(IsModified_MachineData);
1466 mHWData.backup();
1467 mHWData->mMemorySize = aMemorySize;
1468
1469 return S_OK;
1470}
1471
1472HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1473{
1474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1475
1476 *aCPUCount = mHWData->mCPUCount;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::setCPUCount(ULONG aCPUCount)
1482{
1483 /* check CPU limits */
1484 if ( aCPUCount < SchemaDefs::MinCPUCount
1485 || aCPUCount > SchemaDefs::MaxCPUCount
1486 )
1487 return setError(E_INVALIDARG,
1488 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1489 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1490
1491 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1494 if (mHWData->mCPUHotPlugEnabled)
1495 {
1496 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1497 {
1498 if (mHWData->mCPUAttached[idx])
1499 return setError(E_INVALIDARG,
1500 tr("There is still a CPU attached to socket %lu."
1501 "Detach the CPU before removing the socket"),
1502 aCPUCount, idx+1);
1503 }
1504 }
1505
1506 HRESULT rc = i_checkStateDependency(MutableStateDep);
1507 if (FAILED(rc)) return rc;
1508
1509 i_setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCPUCount = aCPUCount;
1512
1513 return S_OK;
1514}
1515
1516HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1517{
1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1519
1520 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1526{
1527 HRESULT rc = S_OK;
1528
1529 /* check throttle limits */
1530 if ( aCPUExecutionCap < 1
1531 || aCPUExecutionCap > 100
1532 )
1533 return setError(E_INVALIDARG,
1534 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1535 aCPUExecutionCap, 1, 100);
1536
1537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1538
1539 alock.release();
1540 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1541 alock.acquire();
1542 if (FAILED(rc)) return rc;
1543
1544 i_setModified(IsModified_MachineData);
1545 mHWData.backup();
1546 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1547
1548 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1549 if (Global::IsOnline(mData->mMachineState))
1550 i_saveSettings(NULL);
1551
1552 return S_OK;
1553}
1554
1555HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1556{
1557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1558
1559 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1565{
1566 HRESULT rc = S_OK;
1567
1568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1569
1570 rc = i_checkStateDependency(MutableStateDep);
1571 if (FAILED(rc)) return rc;
1572
1573 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1574 {
1575 if (aCPUHotPlugEnabled)
1576 {
1577 i_setModified(IsModified_MachineData);
1578 mHWData.backup();
1579
1580 /* Add the amount of CPUs currently attached */
1581 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1582 mHWData->mCPUAttached[i] = true;
1583 }
1584 else
1585 {
1586 /*
1587 * We can disable hotplug only if the amount of maximum CPUs is equal
1588 * to the amount of attached CPUs
1589 */
1590 unsigned cCpusAttached = 0;
1591 unsigned iHighestId = 0;
1592
1593 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1594 {
1595 if (mHWData->mCPUAttached[i])
1596 {
1597 cCpusAttached++;
1598 iHighestId = i;
1599 }
1600 }
1601
1602 if ( (cCpusAttached != mHWData->mCPUCount)
1603 || (iHighestId >= mHWData->mCPUCount))
1604 return setError(E_INVALIDARG,
1605 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1606
1607 i_setModified(IsModified_MachineData);
1608 mHWData.backup();
1609 }
1610 }
1611
1612 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1613
1614 return rc;
1615}
1616
1617HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1618{
1619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1620
1621 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1622
1623 return S_OK;
1624}
1625
1626HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1627{
1628 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1631 if (SUCCEEDED(hrc))
1632 {
1633 i_setModified(IsModified_MachineData);
1634 mHWData.backup();
1635 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1636 }
1637 return hrc;
1638}
1639
1640HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1641{
1642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1643 aCPUProfile = mHWData->mCpuProfile;
1644 return S_OK;
1645}
1646
1647HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1648{
1649 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1650 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1651 if (SUCCEEDED(hrc))
1652 {
1653 i_setModified(IsModified_MachineData);
1654 mHWData.backup();
1655 /* Empty equals 'host'. */
1656 if (aCPUProfile.isNotEmpty())
1657 mHWData->mCpuProfile = aCPUProfile;
1658 else
1659 mHWData->mCpuProfile = "host";
1660 }
1661 return hrc;
1662}
1663
1664HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1665{
1666#ifdef VBOX_WITH_USB_CARDREADER
1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1668
1669 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1670
1671 return S_OK;
1672#else
1673 NOREF(aEmulatedUSBCardReaderEnabled);
1674 return E_NOTIMPL;
1675#endif
1676}
1677
1678HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1679{
1680#ifdef VBOX_WITH_USB_CARDREADER
1681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1682
1683 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1684 if (FAILED(rc)) return rc;
1685
1686 i_setModified(IsModified_MachineData);
1687 mHWData.backup();
1688 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1689
1690 return S_OK;
1691#else
1692 NOREF(aEmulatedUSBCardReaderEnabled);
1693 return E_NOTIMPL;
1694#endif
1695}
1696
1697HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1698{
1699 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1700
1701 *aHPETEnabled = mHWData->mHPETEnabled;
1702
1703 return S_OK;
1704}
1705
1706HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1707{
1708 HRESULT rc = S_OK;
1709
1710 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1711
1712 rc = i_checkStateDependency(MutableStateDep);
1713 if (FAILED(rc)) return rc;
1714
1715 i_setModified(IsModified_MachineData);
1716 mHWData.backup();
1717
1718 mHWData->mHPETEnabled = aHPETEnabled;
1719
1720 return rc;
1721}
1722
1723HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1724{
1725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1726
1727 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1728 return S_OK;
1729}
1730
1731HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1732{
1733 HRESULT rc = S_OK;
1734
1735 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1736
1737 i_setModified(IsModified_MachineData);
1738 mHWData.backup();
1739 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1740
1741 alock.release();
1742 rc = i_onVideoCaptureChange();
1743 alock.acquire();
1744 if (FAILED(rc))
1745 {
1746 /*
1747 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1748 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1749 * determine if it should start or stop capturing. Therefore we need to manually
1750 * undo change.
1751 */
1752 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1753 return rc;
1754 }
1755
1756 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1757 if (Global::IsOnline(mData->mMachineState))
1758 i_saveSettings(NULL);
1759
1760 return rc;
1761}
1762
1763HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1764{
1765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1766 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1767 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1768 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1769 return S_OK;
1770}
1771
1772HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1773{
1774 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1775 bool fChanged = false;
1776
1777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1778
1779 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1780 {
1781 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1782 {
1783 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1784 fChanged = true;
1785 }
1786 }
1787 if (fChanged)
1788 {
1789 alock.release();
1790 HRESULT rc = i_onVideoCaptureChange();
1791 alock.acquire();
1792 if (FAILED(rc)) return rc;
1793 i_setModified(IsModified_MachineData);
1794
1795 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1796 if (Global::IsOnline(mData->mMachineState))
1797 i_saveSettings(NULL);
1798 }
1799
1800 return S_OK;
1801}
1802
1803HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1804{
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806 if (mHWData->mVideoCaptureFile.isEmpty())
1807 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1808 else
1809 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1810 return S_OK;
1811}
1812
1813HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1814{
1815 Utf8Str strFile(aVideoCaptureFile);
1816 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1817
1818 if ( Global::IsOnline(mData->mMachineState)
1819 && mHWData->mVideoCaptureEnabled)
1820 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1821
1822 if (!RTPathStartsWithRoot(strFile.c_str()))
1823 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1824
1825 if (!strFile.isEmpty())
1826 {
1827 Utf8Str defaultFile;
1828 i_getDefaultVideoCaptureFile(defaultFile);
1829 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1830 strFile.setNull();
1831 }
1832
1833 i_setModified(IsModified_MachineData);
1834 mHWData.backup();
1835 mHWData->mVideoCaptureFile = strFile;
1836
1837 return S_OK;
1838}
1839
1840HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1841{
1842 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1843 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1848{
1849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1850
1851 if ( Global::IsOnline(mData->mMachineState)
1852 && mHWData->mVideoCaptureEnabled)
1853 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1854
1855 i_setModified(IsModified_MachineData);
1856 mHWData.backup();
1857 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1858
1859 return S_OK;
1860}
1861
1862HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1863{
1864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1865 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 if ( Global::IsOnline(mData->mMachineState)
1874 && mHWData->mVideoCaptureEnabled)
1875 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1876
1877 i_setModified(IsModified_MachineData);
1878 mHWData.backup();
1879 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1880
1881 return S_OK;
1882}
1883
1884HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1885{
1886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1887 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1888 return S_OK;
1889}
1890
1891HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1892{
1893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1894
1895 if ( Global::IsOnline(mData->mMachineState)
1896 && mHWData->mVideoCaptureEnabled)
1897 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1898
1899 i_setModified(IsModified_MachineData);
1900 mHWData.backup();
1901 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1902
1903 return S_OK;
1904}
1905
1906HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1907{
1908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1909 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1910 return S_OK;
1911}
1912
1913HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1914{
1915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 if ( Global::IsOnline(mData->mMachineState)
1918 && mHWData->mVideoCaptureEnabled)
1919 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1920
1921 i_setModified(IsModified_MachineData);
1922 mHWData.backup();
1923 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1924
1925 return S_OK;
1926}
1927
1928HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1929{
1930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1931 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1932 return S_OK;
1933}
1934
1935HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1936{
1937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1938
1939 if ( Global::IsOnline(mData->mMachineState)
1940 && mHWData->mVideoCaptureEnabled)
1941 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1942
1943 i_setModified(IsModified_MachineData);
1944 mHWData.backup();
1945 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1954 return S_OK;
1955}
1956
1957HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1958{
1959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 if ( Global::IsOnline(mData->mMachineState)
1962 && mHWData->mVideoCaptureEnabled)
1963 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1964
1965 i_setModified(IsModified_MachineData);
1966 mHWData.backup();
1967 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1968
1969 return S_OK;
1970}
1971
1972HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1973{
1974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1975
1976 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1981{
1982 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1983
1984 if ( Global::IsOnline(mData->mMachineState)
1985 && mHWData->mVideoCaptureEnabled)
1986 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1987
1988 i_setModified(IsModified_MachineData);
1989 mHWData.backup();
1990 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1991
1992 return S_OK;
1993}
1994
1995HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1996{
1997 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1998
1999 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2000
2001 return S_OK;
2002}
2003
2004HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2005{
2006 switch (aGraphicsControllerType)
2007 {
2008 case GraphicsControllerType_Null:
2009 case GraphicsControllerType_VBoxVGA:
2010#ifdef VBOX_WITH_VMSVGA
2011 case GraphicsControllerType_VMSVGA:
2012#endif
2013 break;
2014 default:
2015 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2016 }
2017
2018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2019
2020 HRESULT rc = i_checkStateDependency(MutableStateDep);
2021 if (FAILED(rc)) return rc;
2022
2023 i_setModified(IsModified_MachineData);
2024 mHWData.backup();
2025 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2026
2027 return S_OK;
2028}
2029
2030HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2031{
2032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2033
2034 *aVRAMSize = mHWData->mVRAMSize;
2035
2036 return S_OK;
2037}
2038
2039HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2040{
2041 /* check VRAM limits */
2042 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2043 return setError(E_INVALIDARG,
2044 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2045 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2046
2047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2048
2049 HRESULT rc = i_checkStateDependency(MutableStateDep);
2050 if (FAILED(rc)) return rc;
2051
2052 i_setModified(IsModified_MachineData);
2053 mHWData.backup();
2054 mHWData->mVRAMSize = aVRAMSize;
2055
2056 return S_OK;
2057}
2058
2059/** @todo this method should not be public */
2060HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2061{
2062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2065
2066 return S_OK;
2067}
2068
2069/**
2070 * Set the memory balloon size.
2071 *
2072 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2073 * we have to make sure that we never call IGuest from here.
2074 */
2075HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2076{
2077 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2078#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2079 /* check limits */
2080 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2081 return setError(E_INVALIDARG,
2082 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2083 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2084
2085 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2086
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2090
2091 return S_OK;
2092#else
2093 NOREF(aMemoryBalloonSize);
2094 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2095#endif
2096}
2097
2098HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2099{
2100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2101
2102 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2103 return S_OK;
2104}
2105
2106HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2107{
2108#ifdef VBOX_WITH_PAGE_SHARING
2109 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2112 i_setModified(IsModified_MachineData);
2113 mHWData.backup();
2114 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2115 return S_OK;
2116#else
2117 NOREF(aPageFusionEnabled);
2118 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2119#endif
2120}
2121
2122HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2123{
2124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2125
2126 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2127
2128 return S_OK;
2129}
2130
2131HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /** @todo check validity! */
2139
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2143
2144 return S_OK;
2145}
2146
2147
2148HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2149{
2150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2151
2152 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2153
2154 return S_OK;
2155}
2156
2157HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2158{
2159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 HRESULT rc = i_checkStateDependency(MutableStateDep);
2162 if (FAILED(rc)) return rc;
2163
2164 /** @todo check validity! */
2165 i_setModified(IsModified_MachineData);
2166 mHWData.backup();
2167 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2168
2169 return S_OK;
2170}
2171
2172HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2173{
2174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2175
2176 *aMonitorCount = mHWData->mMonitorCount;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2182{
2183 /* make sure monitor count is a sensible number */
2184 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2185 return setError(E_INVALIDARG,
2186 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2187 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2188
2189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 HRESULT rc = i_checkStateDependency(MutableStateDep);
2192 if (FAILED(rc)) return rc;
2193
2194 i_setModified(IsModified_MachineData);
2195 mHWData.backup();
2196 mHWData->mMonitorCount = aMonitorCount;
2197
2198 return S_OK;
2199}
2200
2201HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2202{
2203 /* mBIOSSettings is constant during life time, no need to lock */
2204 aBIOSSettings = mBIOSSettings;
2205
2206 return S_OK;
2207}
2208
2209HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2210{
2211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2212
2213 switch (aProperty)
2214 {
2215 case CPUPropertyType_PAE:
2216 *aValue = mHWData->mPAEEnabled;
2217 break;
2218
2219 case CPUPropertyType_LongMode:
2220 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2221 *aValue = TRUE;
2222 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2223 *aValue = FALSE;
2224#if HC_ARCH_BITS == 64
2225 else
2226 *aValue = TRUE;
2227#else
2228 else
2229 {
2230 *aValue = FALSE;
2231
2232 ComObjPtr<GuestOSType> pGuestOSType;
2233 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2234 pGuestOSType);
2235 if (SUCCEEDED(hrc2))
2236 {
2237 if (pGuestOSType->i_is64Bit())
2238 {
2239 ComObjPtr<Host> pHost = mParent->i_host();
2240 alock.release();
2241
2242 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2243 if (FAILED(hrc2))
2244 *aValue = FALSE;
2245 }
2246 }
2247 }
2248#endif
2249 break;
2250
2251 case CPUPropertyType_TripleFaultReset:
2252 *aValue = mHWData->mTripleFaultReset;
2253 break;
2254
2255 case CPUPropertyType_APIC:
2256 *aValue = mHWData->mAPIC;
2257 break;
2258
2259 case CPUPropertyType_X2APIC:
2260 *aValue = mHWData->mX2APIC;
2261 break;
2262
2263 case CPUPropertyType_IBPBOnVMExit:
2264 *aValue = mHWData->mIBPBOnVMExit;
2265 break;
2266
2267 case CPUPropertyType_IBPBOnVMEntry:
2268 *aValue = mHWData->mIBPBOnVMEntry;
2269 break;
2270
2271 case CPUPropertyType_SpecCtrl:
2272 *aValue = mHWData->mSpecCtrl;
2273 break;
2274
2275 case CPUPropertyType_SpecCtrlByHost:
2276 *aValue = mHWData->mSpecCtrlByHost;
2277 break;
2278
2279 case CPUPropertyType_HWVirt:
2280 *aValue = mHWData->mNestedHWVirt;
2281 break;
2282
2283 default:
2284 return E_INVALIDARG;
2285 }
2286 return S_OK;
2287}
2288
2289HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2290{
2291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2292
2293 HRESULT rc = i_checkStateDependency(MutableStateDep);
2294 if (FAILED(rc)) return rc;
2295
2296 switch (aProperty)
2297 {
2298 case CPUPropertyType_PAE:
2299 i_setModified(IsModified_MachineData);
2300 mHWData.backup();
2301 mHWData->mPAEEnabled = !!aValue;
2302 break;
2303
2304 case CPUPropertyType_LongMode:
2305 i_setModified(IsModified_MachineData);
2306 mHWData.backup();
2307 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2308 break;
2309
2310 case CPUPropertyType_TripleFaultReset:
2311 i_setModified(IsModified_MachineData);
2312 mHWData.backup();
2313 mHWData->mTripleFaultReset = !!aValue;
2314 break;
2315
2316 case CPUPropertyType_APIC:
2317 if (mHWData->mX2APIC)
2318 aValue = TRUE;
2319 i_setModified(IsModified_MachineData);
2320 mHWData.backup();
2321 mHWData->mAPIC = !!aValue;
2322 break;
2323
2324 case CPUPropertyType_X2APIC:
2325 i_setModified(IsModified_MachineData);
2326 mHWData.backup();
2327 mHWData->mX2APIC = !!aValue;
2328 if (aValue)
2329 mHWData->mAPIC = !!aValue;
2330 break;
2331
2332 case CPUPropertyType_IBPBOnVMExit:
2333 i_setModified(IsModified_MachineData);
2334 mHWData.backup();
2335 mHWData->mIBPBOnVMExit = !!aValue;
2336 break;
2337
2338 case CPUPropertyType_IBPBOnVMEntry:
2339 i_setModified(IsModified_MachineData);
2340 mHWData.backup();
2341 mHWData->mIBPBOnVMEntry = !!aValue;
2342 break;
2343
2344 case CPUPropertyType_SpecCtrl:
2345 i_setModified(IsModified_MachineData);
2346 mHWData.backup();
2347 mHWData->mSpecCtrl = !!aValue;
2348 break;
2349
2350 case CPUPropertyType_SpecCtrlByHost:
2351 i_setModified(IsModified_MachineData);
2352 mHWData.backup();
2353 mHWData->mSpecCtrlByHost = !!aValue;
2354 break;
2355
2356 case CPUPropertyType_HWVirt:
2357 i_setModified(IsModified_MachineData);
2358 mHWData.backup();
2359 mHWData->mNestedHWVirt = !!aValue;
2360 break;
2361
2362 default:
2363 return E_INVALIDARG;
2364 }
2365 return S_OK;
2366}
2367
2368HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2369 ULONG *aValEcx, ULONG *aValEdx)
2370{
2371 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2372 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2373 {
2374 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2375 it != mHWData->mCpuIdLeafList.end();
2376 ++it)
2377 {
2378 if (aOrdinal == 0)
2379 {
2380 const settings::CpuIdLeaf &rLeaf= *it;
2381 *aIdx = rLeaf.idx;
2382 *aSubIdx = rLeaf.idxSub;
2383 *aValEax = rLeaf.uEax;
2384 *aValEbx = rLeaf.uEbx;
2385 *aValEcx = rLeaf.uEcx;
2386 *aValEdx = rLeaf.uEdx;
2387 return S_OK;
2388 }
2389 aOrdinal--;
2390 }
2391 }
2392 return E_INVALIDARG;
2393}
2394
2395HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2396{
2397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2398
2399 /*
2400 * Search the list.
2401 */
2402 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2403 {
2404 const settings::CpuIdLeaf &rLeaf= *it;
2405 if ( rLeaf.idx == aIdx
2406 && ( aSubIdx == UINT32_MAX
2407 || rLeaf.idxSub == aSubIdx) )
2408 {
2409 *aValEax = rLeaf.uEax;
2410 *aValEbx = rLeaf.uEbx;
2411 *aValEcx = rLeaf.uEcx;
2412 *aValEdx = rLeaf.uEdx;
2413 return S_OK;
2414 }
2415 }
2416
2417 return E_INVALIDARG;
2418}
2419
2420
2421HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2422{
2423 /*
2424 * Validate input before taking locks and checking state.
2425 */
2426 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2427 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2428 if ( aIdx >= UINT32_C(0x20)
2429 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2430 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2431 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2432
2433 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2434 HRESULT rc = i_checkStateDependency(MutableStateDep);
2435 if (FAILED(rc)) return rc;
2436
2437 /*
2438 * Impose a maximum number of leaves.
2439 */
2440 if (mHWData->mCpuIdLeafList.size() > 256)
2441 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2442
2443 /*
2444 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2445 */
2446 i_setModified(IsModified_MachineData);
2447 mHWData.backup();
2448
2449 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2450 {
2451 settings::CpuIdLeaf &rLeaf= *it;
2452 if ( rLeaf.idx == aIdx
2453 && ( aSubIdx == UINT32_MAX
2454 || rLeaf.idxSub == aSubIdx) )
2455 it = mHWData->mCpuIdLeafList.erase(it);
2456 else
2457 ++it;
2458 }
2459
2460 settings::CpuIdLeaf NewLeaf;
2461 NewLeaf.idx = aIdx;
2462 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2463 NewLeaf.uEax = aValEax;
2464 NewLeaf.uEbx = aValEbx;
2465 NewLeaf.uEcx = aValEcx;
2466 NewLeaf.uEdx = aValEdx;
2467 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2468 return S_OK;
2469}
2470
2471HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2472{
2473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 HRESULT rc = i_checkStateDependency(MutableStateDep);
2476 if (FAILED(rc)) return rc;
2477
2478 /*
2479 * Do the removal.
2480 */
2481 bool fModified = false;
2482 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2483 {
2484 settings::CpuIdLeaf &rLeaf= *it;
2485 if ( rLeaf.idx == aIdx
2486 && ( aSubIdx == UINT32_MAX
2487 || rLeaf.idxSub == aSubIdx) )
2488 {
2489 if (!fModified)
2490 {
2491 fModified = true;
2492 i_setModified(IsModified_MachineData);
2493 mHWData.backup();
2494 }
2495 it = mHWData->mCpuIdLeafList.erase(it);
2496 }
2497 else
2498 ++it;
2499 }
2500
2501 return S_OK;
2502}
2503
2504HRESULT Machine::removeAllCPUIDLeaves()
2505{
2506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2507
2508 HRESULT rc = i_checkStateDependency(MutableStateDep);
2509 if (FAILED(rc)) return rc;
2510
2511 if (mHWData->mCpuIdLeafList.size() > 0)
2512 {
2513 i_setModified(IsModified_MachineData);
2514 mHWData.backup();
2515
2516 mHWData->mCpuIdLeafList.clear();
2517 }
2518
2519 return S_OK;
2520}
2521HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2522{
2523 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2524
2525 switch(aProperty)
2526 {
2527 case HWVirtExPropertyType_Enabled:
2528 *aValue = mHWData->mHWVirtExEnabled;
2529 break;
2530
2531 case HWVirtExPropertyType_VPID:
2532 *aValue = mHWData->mHWVirtExVPIDEnabled;
2533 break;
2534
2535 case HWVirtExPropertyType_NestedPaging:
2536 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2537 break;
2538
2539 case HWVirtExPropertyType_UnrestrictedExecution:
2540 *aValue = mHWData->mHWVirtExUXEnabled;
2541 break;
2542
2543 case HWVirtExPropertyType_LargePages:
2544 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2545#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2546 *aValue = FALSE;
2547#endif
2548 break;
2549
2550 case HWVirtExPropertyType_Force:
2551 *aValue = mHWData->mHWVirtExForceEnabled;
2552 break;
2553
2554 default:
2555 return E_INVALIDARG;
2556 }
2557 return S_OK;
2558}
2559
2560HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2561{
2562 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 HRESULT rc = i_checkStateDependency(MutableStateDep);
2565 if (FAILED(rc)) return rc;
2566
2567 switch(aProperty)
2568 {
2569 case HWVirtExPropertyType_Enabled:
2570 i_setModified(IsModified_MachineData);
2571 mHWData.backup();
2572 mHWData->mHWVirtExEnabled = !!aValue;
2573 break;
2574
2575 case HWVirtExPropertyType_VPID:
2576 i_setModified(IsModified_MachineData);
2577 mHWData.backup();
2578 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2579 break;
2580
2581 case HWVirtExPropertyType_NestedPaging:
2582 i_setModified(IsModified_MachineData);
2583 mHWData.backup();
2584 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2585 break;
2586
2587 case HWVirtExPropertyType_UnrestrictedExecution:
2588 i_setModified(IsModified_MachineData);
2589 mHWData.backup();
2590 mHWData->mHWVirtExUXEnabled = !!aValue;
2591 break;
2592
2593 case HWVirtExPropertyType_LargePages:
2594 i_setModified(IsModified_MachineData);
2595 mHWData.backup();
2596 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2597 break;
2598
2599 case HWVirtExPropertyType_Force:
2600 i_setModified(IsModified_MachineData);
2601 mHWData.backup();
2602 mHWData->mHWVirtExForceEnabled = !!aValue;
2603 break;
2604
2605 default:
2606 return E_INVALIDARG;
2607 }
2608
2609 return S_OK;
2610}
2611
2612HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2613{
2614 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2615
2616 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2617
2618 return S_OK;
2619}
2620
2621HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2622{
2623 /** @todo (r=dmik):
2624 * 1. Allow to change the name of the snapshot folder containing snapshots
2625 * 2. Rename the folder on disk instead of just changing the property
2626 * value (to be smart and not to leave garbage). Note that it cannot be
2627 * done here because the change may be rolled back. Thus, the right
2628 * place is #saveSettings().
2629 */
2630
2631 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 HRESULT rc = i_checkStateDependency(MutableStateDep);
2634 if (FAILED(rc)) return rc;
2635
2636 if (!mData->mCurrentSnapshot.isNull())
2637 return setError(E_FAIL,
2638 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2639
2640 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2641
2642 if (strSnapshotFolder.isEmpty())
2643 strSnapshotFolder = "Snapshots";
2644 int vrc = i_calculateFullPath(strSnapshotFolder,
2645 strSnapshotFolder);
2646 if (RT_FAILURE(vrc))
2647 return setError(E_FAIL,
2648 tr("Invalid snapshot folder '%s' (%Rrc)"),
2649 strSnapshotFolder.c_str(), vrc);
2650
2651 i_setModified(IsModified_MachineData);
2652 mUserData.backup();
2653
2654 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 aMediumAttachments.resize(mMediumAttachments->size());
2664 size_t i = 0;
2665 for (MediumAttachmentList::const_iterator
2666 it = mMediumAttachments->begin();
2667 it != mMediumAttachments->end();
2668 ++it, ++i)
2669 aMediumAttachments[i] = *it;
2670
2671 return S_OK;
2672}
2673
2674HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2675{
2676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2677
2678 Assert(!!mVRDEServer);
2679
2680 aVRDEServer = mVRDEServer;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 aAudioAdapter = mAudioAdapter;
2690
2691 return S_OK;
2692}
2693
2694HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2695{
2696#ifdef VBOX_WITH_VUSB
2697 clearError();
2698 MultiResult rc(S_OK);
2699
2700# ifdef VBOX_WITH_USB
2701 rc = mParent->i_host()->i_checkUSBProxyService();
2702 if (FAILED(rc)) return rc;
2703# endif
2704
2705 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2706
2707 aUSBControllers.resize(mUSBControllers->size());
2708 size_t i = 0;
2709 for (USBControllerList::const_iterator
2710 it = mUSBControllers->begin();
2711 it != mUSBControllers->end();
2712 ++it, ++i)
2713 aUSBControllers[i] = *it;
2714
2715 return S_OK;
2716#else
2717 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2718 * extended error info to indicate that USB is simply not available
2719 * (w/o treating it as a failure), for example, as in OSE */
2720 NOREF(aUSBControllers);
2721 ReturnComNotImplemented();
2722#endif /* VBOX_WITH_VUSB */
2723}
2724
2725HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2726{
2727#ifdef VBOX_WITH_VUSB
2728 clearError();
2729 MultiResult rc(S_OK);
2730
2731# ifdef VBOX_WITH_USB
2732 rc = mParent->i_host()->i_checkUSBProxyService();
2733 if (FAILED(rc)) return rc;
2734# endif
2735
2736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2737
2738 aUSBDeviceFilters = mUSBDeviceFilters;
2739 return rc;
2740#else
2741 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2742 * extended error info to indicate that USB is simply not available
2743 * (w/o treating it as a failure), for example, as in OSE */
2744 NOREF(aUSBDeviceFilters);
2745 ReturnComNotImplemented();
2746#endif /* VBOX_WITH_VUSB */
2747}
2748
2749HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2750{
2751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2752
2753 aSettingsFilePath = mData->m_strConfigFileFull;
2754
2755 return S_OK;
2756}
2757
2758HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2759{
2760 RT_NOREF(aSettingsFilePath);
2761 ReturnComNotImplemented();
2762}
2763
2764HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2765{
2766 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2767
2768 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2769 if (FAILED(rc)) return rc;
2770
2771 if (!mData->pMachineConfigFile->fileExists())
2772 // this is a new machine, and no config file exists yet:
2773 *aSettingsModified = TRUE;
2774 else
2775 *aSettingsModified = (mData->flModifications != 0);
2776
2777 return S_OK;
2778}
2779
2780HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2781{
2782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2783
2784 *aSessionState = mData->mSession.mState;
2785
2786 return S_OK;
2787}
2788
2789HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2790{
2791 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2792
2793 aSessionName = mData->mSession.mName;
2794
2795 return S_OK;
2796}
2797
2798HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2799{
2800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2801
2802 *aSessionPID = mData->mSession.mPID;
2803
2804 return S_OK;
2805}
2806
2807HRESULT Machine::getState(MachineState_T *aState)
2808{
2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2810
2811 *aState = mData->mMachineState;
2812 Assert(mData->mMachineState != MachineState_Null);
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 aStateFilePath = mSSData->strStateFilePath;
2831
2832 return S_OK;
2833}
2834
2835HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2836{
2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2838
2839 i_getLogFolder(aLogFolder);
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 aCurrentSnapshot = mData->mCurrentSnapshot;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2854{
2855 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2858 ? 0
2859 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2860
2861 return S_OK;
2862}
2863
2864HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2865{
2866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2867
2868 /* Note: for machines with no snapshots, we always return FALSE
2869 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2870 * reasons :) */
2871
2872 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2873 ? FALSE
2874 : mData->mCurrentStateModified;
2875
2876 return S_OK;
2877}
2878
2879HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2880{
2881 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2882
2883 aSharedFolders.resize(mHWData->mSharedFolders.size());
2884 size_t i = 0;
2885 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2886 it = mHWData->mSharedFolders.begin();
2887 it != mHWData->mSharedFolders.end();
2888 ++it, ++i)
2889 aSharedFolders[i] = *it;
2890
2891 return S_OK;
2892}
2893
2894HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2895{
2896 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2897
2898 *aClipboardMode = mHWData->mClipboardMode;
2899
2900 return S_OK;
2901}
2902
2903HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2904{
2905 HRESULT rc = S_OK;
2906
2907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2908
2909 alock.release();
2910 rc = i_onClipboardModeChange(aClipboardMode);
2911 alock.acquire();
2912 if (FAILED(rc)) return rc;
2913
2914 i_setModified(IsModified_MachineData);
2915 mHWData.backup();
2916 mHWData->mClipboardMode = aClipboardMode;
2917
2918 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2919 if (Global::IsOnline(mData->mMachineState))
2920 i_saveSettings(NULL);
2921
2922 return S_OK;
2923}
2924
2925HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2926{
2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2928
2929 *aDnDMode = mHWData->mDnDMode;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2935{
2936 HRESULT rc = S_OK;
2937
2938 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2939
2940 alock.release();
2941 rc = i_onDnDModeChange(aDnDMode);
2942
2943 alock.acquire();
2944 if (FAILED(rc)) return rc;
2945
2946 i_setModified(IsModified_MachineData);
2947 mHWData.backup();
2948 mHWData->mDnDMode = aDnDMode;
2949
2950 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2951 if (Global::IsOnline(mData->mMachineState))
2952 i_saveSettings(NULL);
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2958{
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 aStorageControllers.resize(mStorageControllers->size());
2962 size_t i = 0;
2963 for (StorageControllerList::const_iterator
2964 it = mStorageControllers->begin();
2965 it != mStorageControllers->end();
2966 ++it, ++i)
2967 aStorageControllers[i] = *it;
2968
2969 return S_OK;
2970}
2971
2972HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2973{
2974 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2975
2976 *aEnabled = mUserData->s.fTeleporterEnabled;
2977
2978 return S_OK;
2979}
2980
2981HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2982{
2983 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 /* Only allow it to be set to true when PoweredOff or Aborted.
2986 (Clearing it is always permitted.) */
2987 if ( aTeleporterEnabled
2988 && mData->mRegistered
2989 && ( !i_isSessionMachine()
2990 || ( mData->mMachineState != MachineState_PoweredOff
2991 && mData->mMachineState != MachineState_Teleported
2992 && mData->mMachineState != MachineState_Aborted
2993 )
2994 )
2995 )
2996 return setError(VBOX_E_INVALID_VM_STATE,
2997 tr("The machine is not powered off (state is %s)"),
2998 Global::stringifyMachineState(mData->mMachineState));
2999
3000 i_setModified(IsModified_MachineData);
3001 mUserData.backup();
3002 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3003
3004 return S_OK;
3005}
3006
3007HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3008{
3009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3017{
3018 if (aTeleporterPort >= _64K)
3019 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3020
3021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3022
3023 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3024 if (FAILED(rc)) return rc;
3025
3026 i_setModified(IsModified_MachineData);
3027 mUserData.backup();
3028 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3029
3030 return S_OK;
3031}
3032
3033HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3034{
3035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3036
3037 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3038
3039 return S_OK;
3040}
3041
3042HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3043{
3044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3045
3046 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3047 if (FAILED(rc)) return rc;
3048
3049 i_setModified(IsModified_MachineData);
3050 mUserData.backup();
3051 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3052
3053 return S_OK;
3054}
3055
3056HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3057{
3058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3059 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3060
3061 return S_OK;
3062}
3063
3064HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3065{
3066 /*
3067 * Hash the password first.
3068 */
3069 com::Utf8Str aT = aTeleporterPassword;
3070
3071 if (!aT.isEmpty())
3072 {
3073 if (VBoxIsPasswordHashed(&aT))
3074 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3075 VBoxHashPassword(&aT);
3076 }
3077
3078 /*
3079 * Do the update.
3080 */
3081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3082 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3083 if (SUCCEEDED(hrc))
3084 {
3085 i_setModified(IsModified_MachineData);
3086 mUserData.backup();
3087 mUserData->s.strTeleporterPassword = aT;
3088 }
3089
3090 return hrc;
3091}
3092
3093HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3094{
3095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3096
3097 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3098 return S_OK;
3099}
3100
3101HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3102{
3103 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3104
3105 /** @todo deal with running state change. */
3106 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3107 if (FAILED(rc)) return rc;
3108
3109 i_setModified(IsModified_MachineData);
3110 mUserData.backup();
3111 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3112 return S_OK;
3113}
3114
3115HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3116{
3117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3118
3119 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3120 return S_OK;
3121}
3122
3123HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3124{
3125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3126
3127 /** @todo deal with running state change. */
3128 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3129 if (FAILED(rc)) return rc;
3130
3131 i_setModified(IsModified_MachineData);
3132 mUserData.backup();
3133 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3134 return S_OK;
3135}
3136
3137HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3138{
3139 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3140
3141 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3142 return S_OK;
3143}
3144
3145HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3146{
3147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3148
3149 /** @todo deal with running state change. */
3150 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3151 if (FAILED(rc)) return rc;
3152
3153 i_setModified(IsModified_MachineData);
3154 mUserData.backup();
3155 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3156 return S_OK;
3157}
3158
3159HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3160{
3161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3162
3163 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3164
3165 return S_OK;
3166}
3167
3168HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3169{
3170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3171
3172 /** @todo deal with running state change. */
3173 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3174 if (FAILED(rc)) return rc;
3175
3176 i_setModified(IsModified_MachineData);
3177 mUserData.backup();
3178 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3179
3180 return S_OK;
3181}
3182
3183HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3184{
3185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3186
3187 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3188 return S_OK;
3189}
3190
3191HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3192{
3193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3194
3195 /** @todo deal with running state change. */
3196 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3197 if (FAILED(rc)) return rc;
3198
3199 i_setModified(IsModified_MachineData);
3200 mUserData.backup();
3201 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3202 return S_OK;
3203}
3204
3205HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3206{
3207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3208
3209 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3210
3211 return S_OK;
3212}
3213
3214HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3215{
3216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3217
3218 /* Only allow it to be set to true when PoweredOff or Aborted.
3219 (Clearing it is always permitted.) */
3220 if ( aRTCUseUTC
3221 && mData->mRegistered
3222 && ( !i_isSessionMachine()
3223 || ( mData->mMachineState != MachineState_PoweredOff
3224 && mData->mMachineState != MachineState_Teleported
3225 && mData->mMachineState != MachineState_Aborted
3226 )
3227 )
3228 )
3229 return setError(VBOX_E_INVALID_VM_STATE,
3230 tr("The machine is not powered off (state is %s)"),
3231 Global::stringifyMachineState(mData->mMachineState));
3232
3233 i_setModified(IsModified_MachineData);
3234 mUserData.backup();
3235 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3236
3237 return S_OK;
3238}
3239
3240HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3241{
3242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3243
3244 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3245
3246 return S_OK;
3247}
3248
3249HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3250{
3251 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3252
3253 HRESULT rc = i_checkStateDependency(MutableStateDep);
3254 if (FAILED(rc)) return rc;
3255
3256 i_setModified(IsModified_MachineData);
3257 mHWData.backup();
3258 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3259
3260 return S_OK;
3261}
3262
3263HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3264{
3265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3266
3267 *aIOCacheSize = mHWData->mIOCacheSize;
3268
3269 return S_OK;
3270}
3271
3272HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3273{
3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3275
3276 HRESULT rc = i_checkStateDependency(MutableStateDep);
3277 if (FAILED(rc)) return rc;
3278
3279 i_setModified(IsModified_MachineData);
3280 mHWData.backup();
3281 mHWData->mIOCacheSize = aIOCacheSize;
3282
3283 return S_OK;
3284}
3285
3286
3287/**
3288 * @note Locks objects!
3289 */
3290HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3291 LockType_T aLockType)
3292{
3293 /* check the session state */
3294 SessionState_T state;
3295 HRESULT rc = aSession->COMGETTER(State)(&state);
3296 if (FAILED(rc)) return rc;
3297
3298 if (state != SessionState_Unlocked)
3299 return setError(VBOX_E_INVALID_OBJECT_STATE,
3300 tr("The given session is busy"));
3301
3302 // get the client's IInternalSessionControl interface
3303 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3304 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3305 E_INVALIDARG);
3306
3307 // session name (only used in some code paths)
3308 Utf8Str strSessionName;
3309
3310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3311
3312 if (!mData->mRegistered)
3313 return setError(E_UNEXPECTED,
3314 tr("The machine '%s' is not registered"),
3315 mUserData->s.strName.c_str());
3316
3317 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3318
3319 SessionState_T oldState = mData->mSession.mState;
3320 /* Hack: in case the session is closing and there is a progress object
3321 * which allows waiting for the session to be closed, take the opportunity
3322 * and do a limited wait (max. 1 second). This helps a lot when the system
3323 * is busy and thus session closing can take a little while. */
3324 if ( mData->mSession.mState == SessionState_Unlocking
3325 && mData->mSession.mProgress)
3326 {
3327 alock.release();
3328 mData->mSession.mProgress->WaitForCompletion(1000);
3329 alock.acquire();
3330 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3331 }
3332
3333 // try again now
3334 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3335 // (i.e. session machine exists)
3336 && (aLockType == LockType_Shared) // caller wants a shared link to the
3337 // existing session that holds the write lock:
3338 )
3339 {
3340 // OK, share the session... we are now dealing with three processes:
3341 // 1) VBoxSVC (where this code runs);
3342 // 2) process C: the caller's client process (who wants a shared session);
3343 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3344
3345 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3346 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3347 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3348 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3349 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3350
3351 /*
3352 * Release the lock before calling the client process. It's safe here
3353 * since the only thing to do after we get the lock again is to add
3354 * the remote control to the list (which doesn't directly influence
3355 * anything).
3356 */
3357 alock.release();
3358
3359 // get the console of the session holding the write lock (this is a remote call)
3360 ComPtr<IConsole> pConsoleW;
3361 if (mData->mSession.mLockType == LockType_VM)
3362 {
3363 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3364 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3365 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3366 if (FAILED(rc))
3367 // the failure may occur w/o any error info (from RPC), so provide one
3368 return setError(VBOX_E_VM_ERROR,
3369 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3370 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3371 }
3372
3373 // share the session machine and W's console with the caller's session
3374 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3375 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3376 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3377
3378 if (FAILED(rc))
3379 // the failure may occur w/o any error info (from RPC), so provide one
3380 return setError(VBOX_E_VM_ERROR,
3381 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3382 alock.acquire();
3383
3384 // need to revalidate the state after acquiring the lock again
3385 if (mData->mSession.mState != SessionState_Locked)
3386 {
3387 pSessionControl->Uninitialize();
3388 return setError(VBOX_E_INVALID_SESSION_STATE,
3389 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3390 mUserData->s.strName.c_str());
3391 }
3392
3393 // add the caller's session to the list
3394 mData->mSession.mRemoteControls.push_back(pSessionControl);
3395 }
3396 else if ( mData->mSession.mState == SessionState_Locked
3397 || mData->mSession.mState == SessionState_Unlocking
3398 )
3399 {
3400 // sharing not permitted, or machine still unlocking:
3401 return setError(VBOX_E_INVALID_OBJECT_STATE,
3402 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3403 mUserData->s.strName.c_str());
3404 }
3405 else
3406 {
3407 // machine is not locked: then write-lock the machine (create the session machine)
3408
3409 // must not be busy
3410 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3411
3412 // get the caller's session PID
3413 RTPROCESS pid = NIL_RTPROCESS;
3414 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3415 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3416 Assert(pid != NIL_RTPROCESS);
3417
3418 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3419
3420 if (fLaunchingVMProcess)
3421 {
3422 if (mData->mSession.mPID == NIL_RTPROCESS)
3423 {
3424 // two or more clients racing for a lock, the one which set the
3425 // session state to Spawning will win, the others will get an
3426 // error as we can't decide here if waiting a little would help
3427 // (only for shared locks this would avoid an error)
3428 return setError(VBOX_E_INVALID_OBJECT_STATE,
3429 tr("The machine '%s' already has a lock request pending"),
3430 mUserData->s.strName.c_str());
3431 }
3432
3433 // this machine is awaiting for a spawning session to be opened:
3434 // then the calling process must be the one that got started by
3435 // LaunchVMProcess()
3436
3437 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3438 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3439
3440#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3441 /* Hardened windows builds spawns three processes when a VM is
3442 launched, the 3rd one is the one that will end up here. */
3443 RTPROCESS ppid;
3444 int rc = RTProcQueryParent(pid, &ppid);
3445 if (RT_SUCCESS(rc))
3446 rc = RTProcQueryParent(ppid, &ppid);
3447 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3448 || rc == VERR_ACCESS_DENIED)
3449 {
3450 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3451 mData->mSession.mPID = pid;
3452 }
3453#endif
3454
3455 if (mData->mSession.mPID != pid)
3456 return setError(E_ACCESSDENIED,
3457 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3458 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3459 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3460 }
3461
3462 // create the mutable SessionMachine from the current machine
3463 ComObjPtr<SessionMachine> sessionMachine;
3464 sessionMachine.createObject();
3465 rc = sessionMachine->init(this);
3466 AssertComRC(rc);
3467
3468 /* NOTE: doing return from this function after this point but
3469 * before the end is forbidden since it may call SessionMachine::uninit()
3470 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3471 * lock while still holding the Machine lock in alock so that a deadlock
3472 * is possible due to the wrong lock order. */
3473
3474 if (SUCCEEDED(rc))
3475 {
3476 /*
3477 * Set the session state to Spawning to protect against subsequent
3478 * attempts to open a session and to unregister the machine after
3479 * we release the lock.
3480 */
3481 SessionState_T origState = mData->mSession.mState;
3482 mData->mSession.mState = SessionState_Spawning;
3483
3484#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3485 /* Get the client token ID to be passed to the client process */
3486 Utf8Str strTokenId;
3487 sessionMachine->i_getTokenId(strTokenId);
3488 Assert(!strTokenId.isEmpty());
3489#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3490 /* Get the client token to be passed to the client process */
3491 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3492 /* The token is now "owned" by pToken, fix refcount */
3493 if (!pToken.isNull())
3494 pToken->Release();
3495#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3496
3497 /*
3498 * Release the lock before calling the client process -- it will call
3499 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3500 * because the state is Spawning, so that LaunchVMProcess() and
3501 * LockMachine() calls will fail. This method, called before we
3502 * acquire the lock again, will fail because of the wrong PID.
3503 *
3504 * Note that mData->mSession.mRemoteControls accessed outside
3505 * the lock may not be modified when state is Spawning, so it's safe.
3506 */
3507 alock.release();
3508
3509 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3510#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3511 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3512#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3513 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3514 /* Now the token is owned by the client process. */
3515 pToken.setNull();
3516#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3517 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3518
3519 /* The failure may occur w/o any error info (from RPC), so provide one */
3520 if (FAILED(rc))
3521 setError(VBOX_E_VM_ERROR,
3522 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3523
3524 // get session name, either to remember or to compare against
3525 // the already known session name.
3526 {
3527 Bstr bstrSessionName;
3528 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3529 if (SUCCEEDED(rc2))
3530 strSessionName = bstrSessionName;
3531 }
3532
3533 if ( SUCCEEDED(rc)
3534 && fLaunchingVMProcess
3535 )
3536 {
3537 /* complete the remote session initialization */
3538
3539 /* get the console from the direct session */
3540 ComPtr<IConsole> console;
3541 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3542 ComAssertComRC(rc);
3543
3544 if (SUCCEEDED(rc) && !console)
3545 {
3546 ComAssert(!!console);
3547 rc = E_FAIL;
3548 }
3549
3550 /* assign machine & console to the remote session */
3551 if (SUCCEEDED(rc))
3552 {
3553 /*
3554 * after LaunchVMProcess(), the first and the only
3555 * entry in remoteControls is that remote session
3556 */
3557 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3558 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3559 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3560
3561 /* The failure may occur w/o any error info (from RPC), so provide one */
3562 if (FAILED(rc))
3563 setError(VBOX_E_VM_ERROR,
3564 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3565 }
3566
3567 if (FAILED(rc))
3568 pSessionControl->Uninitialize();
3569 }
3570
3571 /* acquire the lock again */
3572 alock.acquire();
3573
3574 /* Restore the session state */
3575 mData->mSession.mState = origState;
3576 }
3577
3578 // finalize spawning anyway (this is why we don't return on errors above)
3579 if (fLaunchingVMProcess)
3580 {
3581 Assert(mData->mSession.mName == strSessionName);
3582 /* Note that the progress object is finalized later */
3583 /** @todo Consider checking mData->mSession.mProgress for cancellation
3584 * around here. */
3585
3586 /* We don't reset mSession.mPID here because it is necessary for
3587 * SessionMachine::uninit() to reap the child process later. */
3588
3589 if (FAILED(rc))
3590 {
3591 /* Close the remote session, remove the remote control from the list
3592 * and reset session state to Closed (@note keep the code in sync
3593 * with the relevant part in checkForSpawnFailure()). */
3594
3595 Assert(mData->mSession.mRemoteControls.size() == 1);
3596 if (mData->mSession.mRemoteControls.size() == 1)
3597 {
3598 ErrorInfoKeeper eik;
3599 mData->mSession.mRemoteControls.front()->Uninitialize();
3600 }
3601
3602 mData->mSession.mRemoteControls.clear();
3603 mData->mSession.mState = SessionState_Unlocked;
3604 }
3605 }
3606 else
3607 {
3608 /* memorize PID of the directly opened session */
3609 if (SUCCEEDED(rc))
3610 mData->mSession.mPID = pid;
3611 }
3612
3613 if (SUCCEEDED(rc))
3614 {
3615 mData->mSession.mLockType = aLockType;
3616 /* memorize the direct session control and cache IUnknown for it */
3617 mData->mSession.mDirectControl = pSessionControl;
3618 mData->mSession.mState = SessionState_Locked;
3619 if (!fLaunchingVMProcess)
3620 mData->mSession.mName = strSessionName;
3621 /* associate the SessionMachine with this Machine */
3622 mData->mSession.mMachine = sessionMachine;
3623
3624 /* request an IUnknown pointer early from the remote party for later
3625 * identity checks (it will be internally cached within mDirectControl
3626 * at least on XPCOM) */
3627 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3628 NOREF(unk);
3629 }
3630
3631 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3632 * would break the lock order */
3633 alock.release();
3634
3635 /* uninitialize the created session machine on failure */
3636 if (FAILED(rc))
3637 sessionMachine->uninit();
3638 }
3639
3640 if (SUCCEEDED(rc))
3641 {
3642 /*
3643 * tell the client watcher thread to update the set of
3644 * machines that have open sessions
3645 */
3646 mParent->i_updateClientWatcher();
3647
3648 if (oldState != SessionState_Locked)
3649 /* fire an event */
3650 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3651 }
3652
3653 return rc;
3654}
3655
3656/**
3657 * @note Locks objects!
3658 */
3659HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3660 const com::Utf8Str &aName,
3661 const com::Utf8Str &aEnvironment,
3662 ComPtr<IProgress> &aProgress)
3663{
3664 Utf8Str strFrontend(aName);
3665 /* "emergencystop" doesn't need the session, so skip the checks/interface
3666 * retrieval. This code doesn't quite fit in here, but introducing a
3667 * special API method would be even more effort, and would require explicit
3668 * support by every API client. It's better to hide the feature a bit. */
3669 if (strFrontend != "emergencystop")
3670 CheckComArgNotNull(aSession);
3671
3672 HRESULT rc = S_OK;
3673 if (strFrontend.isEmpty())
3674 {
3675 Bstr bstrFrontend;
3676 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3677 if (FAILED(rc))
3678 return rc;
3679 strFrontend = bstrFrontend;
3680 if (strFrontend.isEmpty())
3681 {
3682 ComPtr<ISystemProperties> systemProperties;
3683 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3684 if (FAILED(rc))
3685 return rc;
3686 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3687 if (FAILED(rc))
3688 return rc;
3689 strFrontend = bstrFrontend;
3690 }
3691 /* paranoia - emergencystop is not a valid default */
3692 if (strFrontend == "emergencystop")
3693 strFrontend = Utf8Str::Empty;
3694 }
3695 /* default frontend: Qt GUI */
3696 if (strFrontend.isEmpty())
3697 strFrontend = "GUI/Qt";
3698
3699 if (strFrontend != "emergencystop")
3700 {
3701 /* check the session state */
3702 SessionState_T state;
3703 rc = aSession->COMGETTER(State)(&state);
3704 if (FAILED(rc))
3705 return rc;
3706
3707 if (state != SessionState_Unlocked)
3708 return setError(VBOX_E_INVALID_OBJECT_STATE,
3709 tr("The given session is busy"));
3710
3711 /* get the IInternalSessionControl interface */
3712 ComPtr<IInternalSessionControl> control(aSession);
3713 ComAssertMsgRet(!control.isNull(),
3714 ("No IInternalSessionControl interface"),
3715 E_INVALIDARG);
3716
3717 /* get the teleporter enable state for the progress object init. */
3718 BOOL fTeleporterEnabled;
3719 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3720 if (FAILED(rc))
3721 return rc;
3722
3723 /* create a progress object */
3724 ComObjPtr<ProgressProxy> progress;
3725 progress.createObject();
3726 rc = progress->init(mParent,
3727 static_cast<IMachine*>(this),
3728 Bstr(tr("Starting VM")).raw(),
3729 TRUE /* aCancelable */,
3730 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3731 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3732 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3733 2 /* uFirstOperationWeight */,
3734 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3735
3736 if (SUCCEEDED(rc))
3737 {
3738 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3739 if (SUCCEEDED(rc))
3740 {
3741 aProgress = progress;
3742
3743 /* signal the client watcher thread */
3744 mParent->i_updateClientWatcher();
3745
3746 /* fire an event */
3747 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3748 }
3749 }
3750 }
3751 else
3752 {
3753 /* no progress object - either instant success or failure */
3754 aProgress = NULL;
3755
3756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3757
3758 if (mData->mSession.mState != SessionState_Locked)
3759 return setError(VBOX_E_INVALID_OBJECT_STATE,
3760 tr("The machine '%s' is not locked by a session"),
3761 mUserData->s.strName.c_str());
3762
3763 /* must have a VM process associated - do not kill normal API clients
3764 * with an open session */
3765 if (!Global::IsOnline(mData->mMachineState))
3766 return setError(VBOX_E_INVALID_OBJECT_STATE,
3767 tr("The machine '%s' does not have a VM process"),
3768 mUserData->s.strName.c_str());
3769
3770 /* forcibly terminate the VM process */
3771 if (mData->mSession.mPID != NIL_RTPROCESS)
3772 RTProcTerminate(mData->mSession.mPID);
3773
3774 /* signal the client watcher thread, as most likely the client has
3775 * been terminated */
3776 mParent->i_updateClientWatcher();
3777 }
3778
3779 return rc;
3780}
3781
3782HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3783{
3784 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3785 return setError(E_INVALIDARG,
3786 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3787 aPosition, SchemaDefs::MaxBootPosition);
3788
3789 if (aDevice == DeviceType_USB)
3790 return setError(E_NOTIMPL,
3791 tr("Booting from USB device is currently not supported"));
3792
3793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3794
3795 HRESULT rc = i_checkStateDependency(MutableStateDep);
3796 if (FAILED(rc)) return rc;
3797
3798 i_setModified(IsModified_MachineData);
3799 mHWData.backup();
3800 mHWData->mBootOrder[aPosition - 1] = aDevice;
3801
3802 return S_OK;
3803}
3804
3805HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3806{
3807 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3808 return setError(E_INVALIDARG,
3809 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3810 aPosition, SchemaDefs::MaxBootPosition);
3811
3812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 *aDevice = mHWData->mBootOrder[aPosition - 1];
3815
3816 return S_OK;
3817}
3818
3819HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3820 LONG aControllerPort,
3821 LONG aDevice,
3822 DeviceType_T aType,
3823 const ComPtr<IMedium> &aMedium)
3824{
3825 IMedium *aM = aMedium;
3826 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3827 aName.c_str(), aControllerPort, aDevice, aType, aM));
3828
3829 // request the host lock first, since might be calling Host methods for getting host drives;
3830 // next, protect the media tree all the while we're in here, as well as our member variables
3831 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3832 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3833
3834 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3835 if (FAILED(rc)) return rc;
3836
3837 /// @todo NEWMEDIA implicit machine registration
3838 if (!mData->mRegistered)
3839 return setError(VBOX_E_INVALID_OBJECT_STATE,
3840 tr("Cannot attach storage devices to an unregistered machine"));
3841
3842 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3843
3844 /* Check for an existing controller. */
3845 ComObjPtr<StorageController> ctl;
3846 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3847 if (FAILED(rc)) return rc;
3848
3849 StorageControllerType_T ctrlType;
3850 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3851 if (FAILED(rc))
3852 return setError(E_FAIL,
3853 tr("Could not get type of controller '%s'"),
3854 aName.c_str());
3855
3856 bool fSilent = false;
3857 Utf8Str strReconfig;
3858
3859 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3860 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3861 if ( mData->mMachineState == MachineState_Paused
3862 && strReconfig == "1")
3863 fSilent = true;
3864
3865 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3866 bool fHotplug = false;
3867 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3868 fHotplug = true;
3869
3870 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3871 return setError(VBOX_E_INVALID_VM_STATE,
3872 tr("Controller '%s' does not support hotplugging"),
3873 aName.c_str());
3874
3875 // check that the port and device are not out of range
3876 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3877 if (FAILED(rc)) return rc;
3878
3879 /* check if the device slot is already busy */
3880 MediumAttachment *pAttachTemp;
3881 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3882 aName,
3883 aControllerPort,
3884 aDevice)))
3885 {
3886 Medium *pMedium = pAttachTemp->i_getMedium();
3887 if (pMedium)
3888 {
3889 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3890 return setError(VBOX_E_OBJECT_IN_USE,
3891 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3892 pMedium->i_getLocationFull().c_str(),
3893 aControllerPort,
3894 aDevice,
3895 aName.c_str());
3896 }
3897 else
3898 return setError(VBOX_E_OBJECT_IN_USE,
3899 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3900 aControllerPort, aDevice, aName.c_str());
3901 }
3902
3903 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3904 if (aMedium && medium.isNull())
3905 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3906
3907 AutoCaller mediumCaller(medium);
3908 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3909
3910 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3911
3912 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3913 && !medium.isNull()
3914 )
3915 return setError(VBOX_E_OBJECT_IN_USE,
3916 tr("Medium '%s' is already attached to this virtual machine"),
3917 medium->i_getLocationFull().c_str());
3918
3919 if (!medium.isNull())
3920 {
3921 MediumType_T mtype = medium->i_getType();
3922 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3923 // For DVDs it's not written to the config file, so needs no global config
3924 // version bump. For floppies it's a new attribute "type", which is ignored
3925 // by older VirtualBox version, so needs no global config version bump either.
3926 // For hard disks this type is not accepted.
3927 if (mtype == MediumType_MultiAttach)
3928 {
3929 // This type is new with VirtualBox 4.0 and therefore requires settings
3930 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3931 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3932 // two reasons: The medium type is a property of the media registry tree, which
3933 // can reside in the global config file (for pre-4.0 media); we would therefore
3934 // possibly need to bump the global config version. We don't want to do that though
3935 // because that might make downgrading to pre-4.0 impossible.
3936 // As a result, we can only use these two new types if the medium is NOT in the
3937 // global registry:
3938 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3939 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3940 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3941 )
3942 return setError(VBOX_E_INVALID_OBJECT_STATE,
3943 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3944 "to machines that were created with VirtualBox 4.0 or later"),
3945 medium->i_getLocationFull().c_str());
3946 }
3947 }
3948
3949 bool fIndirect = false;
3950 if (!medium.isNull())
3951 fIndirect = medium->i_isReadOnly();
3952 bool associate = true;
3953
3954 do
3955 {
3956 if ( aType == DeviceType_HardDisk
3957 && mMediumAttachments.isBackedUp())
3958 {
3959 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3960
3961 /* check if the medium was attached to the VM before we started
3962 * changing attachments in which case the attachment just needs to
3963 * be restored */
3964 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3965 {
3966 AssertReturn(!fIndirect, E_FAIL);
3967
3968 /* see if it's the same bus/channel/device */
3969 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3970 {
3971 /* the simplest case: restore the whole attachment
3972 * and return, nothing else to do */
3973 mMediumAttachments->push_back(pAttachTemp);
3974
3975 /* Reattach the medium to the VM. */
3976 if (fHotplug || fSilent)
3977 {
3978 mediumLock.release();
3979 treeLock.release();
3980 alock.release();
3981
3982 MediumLockList *pMediumLockList(new MediumLockList());
3983
3984 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3985 medium /* pToLockWrite */,
3986 false /* fMediumLockWriteAll */,
3987 NULL,
3988 *pMediumLockList);
3989 alock.acquire();
3990 if (FAILED(rc))
3991 delete pMediumLockList;
3992 else
3993 {
3994 mData->mSession.mLockedMedia.Unlock();
3995 alock.release();
3996 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3997 mData->mSession.mLockedMedia.Lock();
3998 alock.acquire();
3999 }
4000 alock.release();
4001
4002 if (SUCCEEDED(rc))
4003 {
4004 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4005 /* Remove lock list in case of error. */
4006 if (FAILED(rc))
4007 {
4008 mData->mSession.mLockedMedia.Unlock();
4009 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4010 mData->mSession.mLockedMedia.Lock();
4011 }
4012 }
4013 }
4014
4015 return S_OK;
4016 }
4017
4018 /* bus/channel/device differ; we need a new attachment object,
4019 * but don't try to associate it again */
4020 associate = false;
4021 break;
4022 }
4023 }
4024
4025 /* go further only if the attachment is to be indirect */
4026 if (!fIndirect)
4027 break;
4028
4029 /* perform the so called smart attachment logic for indirect
4030 * attachments. Note that smart attachment is only applicable to base
4031 * hard disks. */
4032
4033 if (medium->i_getParent().isNull())
4034 {
4035 /* first, investigate the backup copy of the current hard disk
4036 * attachments to make it possible to re-attach existing diffs to
4037 * another device slot w/o losing their contents */
4038 if (mMediumAttachments.isBackedUp())
4039 {
4040 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4041
4042 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4043 uint32_t foundLevel = 0;
4044
4045 for (MediumAttachmentList::const_iterator
4046 it = oldAtts.begin();
4047 it != oldAtts.end();
4048 ++it)
4049 {
4050 uint32_t level = 0;
4051 MediumAttachment *pAttach = *it;
4052 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4053 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4054 if (pMedium.isNull())
4055 continue;
4056
4057 if (pMedium->i_getBase(&level) == medium)
4058 {
4059 /* skip the hard disk if its currently attached (we
4060 * cannot attach the same hard disk twice) */
4061 if (i_findAttachment(*mMediumAttachments.data(),
4062 pMedium))
4063 continue;
4064
4065 /* matched device, channel and bus (i.e. attached to the
4066 * same place) will win and immediately stop the search;
4067 * otherwise the attachment that has the youngest
4068 * descendant of medium will be used
4069 */
4070 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4071 {
4072 /* the simplest case: restore the whole attachment
4073 * and return, nothing else to do */
4074 mMediumAttachments->push_back(*it);
4075
4076 /* Reattach the medium to the VM. */
4077 if (fHotplug || fSilent)
4078 {
4079 mediumLock.release();
4080 treeLock.release();
4081 alock.release();
4082
4083 MediumLockList *pMediumLockList(new MediumLockList());
4084
4085 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4086 medium /* pToLockWrite */,
4087 false /* fMediumLockWriteAll */,
4088 NULL,
4089 *pMediumLockList);
4090 alock.acquire();
4091 if (FAILED(rc))
4092 delete pMediumLockList;
4093 else
4094 {
4095 mData->mSession.mLockedMedia.Unlock();
4096 alock.release();
4097 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4098 mData->mSession.mLockedMedia.Lock();
4099 alock.acquire();
4100 }
4101 alock.release();
4102
4103 if (SUCCEEDED(rc))
4104 {
4105 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4106 /* Remove lock list in case of error. */
4107 if (FAILED(rc))
4108 {
4109 mData->mSession.mLockedMedia.Unlock();
4110 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4111 mData->mSession.mLockedMedia.Lock();
4112 }
4113 }
4114 }
4115
4116 return S_OK;
4117 }
4118 else if ( foundIt == oldAtts.end()
4119 || level > foundLevel /* prefer younger */
4120 )
4121 {
4122 foundIt = it;
4123 foundLevel = level;
4124 }
4125 }
4126 }
4127
4128 if (foundIt != oldAtts.end())
4129 {
4130 /* use the previously attached hard disk */
4131 medium = (*foundIt)->i_getMedium();
4132 mediumCaller.attach(medium);
4133 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4134 mediumLock.attach(medium);
4135 /* not implicit, doesn't require association with this VM */
4136 fIndirect = false;
4137 associate = false;
4138 /* go right to the MediumAttachment creation */
4139 break;
4140 }
4141 }
4142
4143 /* must give up the medium lock and medium tree lock as below we
4144 * go over snapshots, which needs a lock with higher lock order. */
4145 mediumLock.release();
4146 treeLock.release();
4147
4148 /* then, search through snapshots for the best diff in the given
4149 * hard disk's chain to base the new diff on */
4150
4151 ComObjPtr<Medium> base;
4152 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4153 while (snap)
4154 {
4155 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4156
4157 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4158
4159 MediumAttachment *pAttachFound = NULL;
4160 uint32_t foundLevel = 0;
4161
4162 for (MediumAttachmentList::const_iterator
4163 it = snapAtts.begin();
4164 it != snapAtts.end();
4165 ++it)
4166 {
4167 MediumAttachment *pAttach = *it;
4168 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4169 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4170 if (pMedium.isNull())
4171 continue;
4172
4173 uint32_t level = 0;
4174 if (pMedium->i_getBase(&level) == medium)
4175 {
4176 /* matched device, channel and bus (i.e. attached to the
4177 * same place) will win and immediately stop the search;
4178 * otherwise the attachment that has the youngest
4179 * descendant of medium will be used
4180 */
4181 if ( pAttach->i_getDevice() == aDevice
4182 && pAttach->i_getPort() == aControllerPort
4183 && pAttach->i_getControllerName() == aName
4184 )
4185 {
4186 pAttachFound = pAttach;
4187 break;
4188 }
4189 else if ( !pAttachFound
4190 || level > foundLevel /* prefer younger */
4191 )
4192 {
4193 pAttachFound = pAttach;
4194 foundLevel = level;
4195 }
4196 }
4197 }
4198
4199 if (pAttachFound)
4200 {
4201 base = pAttachFound->i_getMedium();
4202 break;
4203 }
4204
4205 snap = snap->i_getParent();
4206 }
4207
4208 /* re-lock medium tree and the medium, as we need it below */
4209 treeLock.acquire();
4210 mediumLock.acquire();
4211
4212 /* found a suitable diff, use it as a base */
4213 if (!base.isNull())
4214 {
4215 medium = base;
4216 mediumCaller.attach(medium);
4217 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4218 mediumLock.attach(medium);
4219 }
4220 }
4221
4222 Utf8Str strFullSnapshotFolder;
4223 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4224
4225 ComObjPtr<Medium> diff;
4226 diff.createObject();
4227 // store this diff in the same registry as the parent
4228 Guid uuidRegistryParent;
4229 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4230 {
4231 // parent image has no registry: this can happen if we're attaching a new immutable
4232 // image that has not yet been attached (medium then points to the base and we're
4233 // creating the diff image for the immutable, and the parent is not yet registered);
4234 // put the parent in the machine registry then
4235 mediumLock.release();
4236 treeLock.release();
4237 alock.release();
4238 i_addMediumToRegistry(medium);
4239 alock.acquire();
4240 treeLock.acquire();
4241 mediumLock.acquire();
4242 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4243 }
4244 rc = diff->init(mParent,
4245 medium->i_getPreferredDiffFormat(),
4246 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4247 uuidRegistryParent,
4248 DeviceType_HardDisk);
4249 if (FAILED(rc)) return rc;
4250
4251 /* Apply the normal locking logic to the entire chain. */
4252 MediumLockList *pMediumLockList(new MediumLockList());
4253 mediumLock.release();
4254 treeLock.release();
4255 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4256 diff /* pToLockWrite */,
4257 false /* fMediumLockWriteAll */,
4258 medium,
4259 *pMediumLockList);
4260 treeLock.acquire();
4261 mediumLock.acquire();
4262 if (SUCCEEDED(rc))
4263 {
4264 mediumLock.release();
4265 treeLock.release();
4266 rc = pMediumLockList->Lock();
4267 treeLock.acquire();
4268 mediumLock.acquire();
4269 if (FAILED(rc))
4270 setError(rc,
4271 tr("Could not lock medium when creating diff '%s'"),
4272 diff->i_getLocationFull().c_str());
4273 else
4274 {
4275 /* will release the lock before the potentially lengthy
4276 * operation, so protect with the special state */
4277 MachineState_T oldState = mData->mMachineState;
4278 i_setMachineState(MachineState_SettingUp);
4279
4280 mediumLock.release();
4281 treeLock.release();
4282 alock.release();
4283
4284 rc = medium->i_createDiffStorage(diff,
4285 medium->i_getPreferredDiffVariant(),
4286 pMediumLockList,
4287 NULL /* aProgress */,
4288 true /* aWait */);
4289
4290 alock.acquire();
4291 treeLock.acquire();
4292 mediumLock.acquire();
4293
4294 i_setMachineState(oldState);
4295 }
4296 }
4297
4298 /* Unlock the media and free the associated memory. */
4299 delete pMediumLockList;
4300
4301 if (FAILED(rc)) return rc;
4302
4303 /* use the created diff for the actual attachment */
4304 medium = diff;
4305 mediumCaller.attach(medium);
4306 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4307 mediumLock.attach(medium);
4308 }
4309 while (0);
4310
4311 ComObjPtr<MediumAttachment> attachment;
4312 attachment.createObject();
4313 rc = attachment->init(this,
4314 medium,
4315 aName,
4316 aControllerPort,
4317 aDevice,
4318 aType,
4319 fIndirect,
4320 false /* fPassthrough */,
4321 false /* fTempEject */,
4322 false /* fNonRotational */,
4323 false /* fDiscard */,
4324 fHotplug /* fHotPluggable */,
4325 Utf8Str::Empty);
4326 if (FAILED(rc)) return rc;
4327
4328 if (associate && !medium.isNull())
4329 {
4330 // as the last step, associate the medium to the VM
4331 rc = medium->i_addBackReference(mData->mUuid);
4332 // here we can fail because of Deleting, or being in process of creating a Diff
4333 if (FAILED(rc)) return rc;
4334
4335 mediumLock.release();
4336 treeLock.release();
4337 alock.release();
4338 i_addMediumToRegistry(medium);
4339 alock.acquire();
4340 treeLock.acquire();
4341 mediumLock.acquire();
4342 }
4343
4344 /* success: finally remember the attachment */
4345 i_setModified(IsModified_Storage);
4346 mMediumAttachments.backup();
4347 mMediumAttachments->push_back(attachment);
4348
4349 mediumLock.release();
4350 treeLock.release();
4351 alock.release();
4352
4353 if (fHotplug || fSilent)
4354 {
4355 if (!medium.isNull())
4356 {
4357 MediumLockList *pMediumLockList(new MediumLockList());
4358
4359 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4360 medium /* pToLockWrite */,
4361 false /* fMediumLockWriteAll */,
4362 NULL,
4363 *pMediumLockList);
4364 alock.acquire();
4365 if (FAILED(rc))
4366 delete pMediumLockList;
4367 else
4368 {
4369 mData->mSession.mLockedMedia.Unlock();
4370 alock.release();
4371 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4372 mData->mSession.mLockedMedia.Lock();
4373 alock.acquire();
4374 }
4375 alock.release();
4376 }
4377
4378 if (SUCCEEDED(rc))
4379 {
4380 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4381 /* Remove lock list in case of error. */
4382 if (FAILED(rc))
4383 {
4384 mData->mSession.mLockedMedia.Unlock();
4385 mData->mSession.mLockedMedia.Remove(attachment);
4386 mData->mSession.mLockedMedia.Lock();
4387 }
4388 }
4389 }
4390
4391 /* Save modified registries, but skip this machine as it's the caller's
4392 * job to save its settings like all other settings changes. */
4393 mParent->i_unmarkRegistryModified(i_getId());
4394 mParent->i_saveModifiedRegistries();
4395
4396 return rc;
4397}
4398
4399HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4400 LONG aDevice)
4401{
4402 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4403 aName.c_str(), aControllerPort, aDevice));
4404
4405 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4406
4407 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4408 if (FAILED(rc)) return rc;
4409
4410 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4411
4412 /* Check for an existing controller. */
4413 ComObjPtr<StorageController> ctl;
4414 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4415 if (FAILED(rc)) return rc;
4416
4417 StorageControllerType_T ctrlType;
4418 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4419 if (FAILED(rc))
4420 return setError(E_FAIL,
4421 tr("Could not get type of controller '%s'"),
4422 aName.c_str());
4423
4424 bool fSilent = false;
4425 Utf8Str strReconfig;
4426
4427 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4428 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4429 if ( mData->mMachineState == MachineState_Paused
4430 && strReconfig == "1")
4431 fSilent = true;
4432
4433 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4434 bool fHotplug = false;
4435 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4436 fHotplug = true;
4437
4438 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4439 return setError(VBOX_E_INVALID_VM_STATE,
4440 tr("Controller '%s' does not support hotplugging"),
4441 aName.c_str());
4442
4443 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4444 aName,
4445 aControllerPort,
4446 aDevice);
4447 if (!pAttach)
4448 return setError(VBOX_E_OBJECT_NOT_FOUND,
4449 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4450 aDevice, aControllerPort, aName.c_str());
4451
4452 if (fHotplug && !pAttach->i_getHotPluggable())
4453 return setError(VBOX_E_NOT_SUPPORTED,
4454 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 /*
4458 * The VM has to detach the device before we delete any implicit diffs.
4459 * If this fails we can roll back without loosing data.
4460 */
4461 if (fHotplug || fSilent)
4462 {
4463 alock.release();
4464 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4465 alock.acquire();
4466 }
4467 if (FAILED(rc)) return rc;
4468
4469 /* If we are here everything went well and we can delete the implicit now. */
4470 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4471
4472 alock.release();
4473
4474 /* Save modified registries, but skip this machine as it's the caller's
4475 * job to save its settings like all other settings changes. */
4476 mParent->i_unmarkRegistryModified(i_getId());
4477 mParent->i_saveModifiedRegistries();
4478
4479 return rc;
4480}
4481
4482HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4483 LONG aDevice, BOOL aPassthrough)
4484{
4485 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4486 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4487
4488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4489
4490 HRESULT rc = i_checkStateDependency(MutableStateDep);
4491 if (FAILED(rc)) return rc;
4492
4493 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4494
4495 if (Global::IsOnlineOrTransient(mData->mMachineState))
4496 return setError(VBOX_E_INVALID_VM_STATE,
4497 tr("Invalid machine state: %s"),
4498 Global::stringifyMachineState(mData->mMachineState));
4499
4500 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4501 aName,
4502 aControllerPort,
4503 aDevice);
4504 if (!pAttach)
4505 return setError(VBOX_E_OBJECT_NOT_FOUND,
4506 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4507 aDevice, aControllerPort, aName.c_str());
4508
4509
4510 i_setModified(IsModified_Storage);
4511 mMediumAttachments.backup();
4512
4513 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4514
4515 if (pAttach->i_getType() != DeviceType_DVD)
4516 return setError(E_INVALIDARG,
4517 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4518 aDevice, aControllerPort, aName.c_str());
4519 pAttach->i_updatePassthrough(!!aPassthrough);
4520
4521 return S_OK;
4522}
4523
4524HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4525 LONG aDevice, BOOL aTemporaryEject)
4526{
4527
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4529 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4530
4531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4532
4533 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4534 if (FAILED(rc)) return rc;
4535
4536 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4537 aName,
4538 aControllerPort,
4539 aDevice);
4540 if (!pAttach)
4541 return setError(VBOX_E_OBJECT_NOT_FOUND,
4542 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4543 aDevice, aControllerPort, aName.c_str());
4544
4545
4546 i_setModified(IsModified_Storage);
4547 mMediumAttachments.backup();
4548
4549 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4550
4551 if (pAttach->i_getType() != DeviceType_DVD)
4552 return setError(E_INVALIDARG,
4553 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4554 aDevice, aControllerPort, aName.c_str());
4555 pAttach->i_updateTempEject(!!aTemporaryEject);
4556
4557 return S_OK;
4558}
4559
4560HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4561 LONG aDevice, BOOL aNonRotational)
4562{
4563
4564 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4565 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4566
4567 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4568
4569 HRESULT rc = i_checkStateDependency(MutableStateDep);
4570 if (FAILED(rc)) return rc;
4571
4572 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4573
4574 if (Global::IsOnlineOrTransient(mData->mMachineState))
4575 return setError(VBOX_E_INVALID_VM_STATE,
4576 tr("Invalid machine state: %s"),
4577 Global::stringifyMachineState(mData->mMachineState));
4578
4579 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4580 aName,
4581 aControllerPort,
4582 aDevice);
4583 if (!pAttach)
4584 return setError(VBOX_E_OBJECT_NOT_FOUND,
4585 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4586 aDevice, aControllerPort, aName.c_str());
4587
4588
4589 i_setModified(IsModified_Storage);
4590 mMediumAttachments.backup();
4591
4592 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4593
4594 if (pAttach->i_getType() != DeviceType_HardDisk)
4595 return setError(E_INVALIDARG,
4596 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"),
4597 aDevice, aControllerPort, aName.c_str());
4598 pAttach->i_updateNonRotational(!!aNonRotational);
4599
4600 return S_OK;
4601}
4602
4603HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4604 LONG aDevice, BOOL aDiscard)
4605{
4606
4607 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4608 aName.c_str(), aControllerPort, aDevice, aDiscard));
4609
4610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4611
4612 HRESULT rc = i_checkStateDependency(MutableStateDep);
4613 if (FAILED(rc)) return rc;
4614
4615 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4616
4617 if (Global::IsOnlineOrTransient(mData->mMachineState))
4618 return setError(VBOX_E_INVALID_VM_STATE,
4619 tr("Invalid machine state: %s"),
4620 Global::stringifyMachineState(mData->mMachineState));
4621
4622 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4623 aName,
4624 aControllerPort,
4625 aDevice);
4626 if (!pAttach)
4627 return setError(VBOX_E_OBJECT_NOT_FOUND,
4628 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4629 aDevice, aControllerPort, aName.c_str());
4630
4631
4632 i_setModified(IsModified_Storage);
4633 mMediumAttachments.backup();
4634
4635 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4636
4637 if (pAttach->i_getType() != DeviceType_HardDisk)
4638 return setError(E_INVALIDARG,
4639 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"),
4640 aDevice, aControllerPort, aName.c_str());
4641 pAttach->i_updateDiscard(!!aDiscard);
4642
4643 return S_OK;
4644}
4645
4646HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4647 LONG aDevice, BOOL aHotPluggable)
4648{
4649 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4650 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4651
4652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4653
4654 HRESULT rc = i_checkStateDependency(MutableStateDep);
4655 if (FAILED(rc)) return rc;
4656
4657 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4658
4659 if (Global::IsOnlineOrTransient(mData->mMachineState))
4660 return setError(VBOX_E_INVALID_VM_STATE,
4661 tr("Invalid machine state: %s"),
4662 Global::stringifyMachineState(mData->mMachineState));
4663
4664 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4665 aName,
4666 aControllerPort,
4667 aDevice);
4668 if (!pAttach)
4669 return setError(VBOX_E_OBJECT_NOT_FOUND,
4670 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4671 aDevice, aControllerPort, aName.c_str());
4672
4673 /* Check for an existing controller. */
4674 ComObjPtr<StorageController> ctl;
4675 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4676 if (FAILED(rc)) return rc;
4677
4678 StorageControllerType_T ctrlType;
4679 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4680 if (FAILED(rc))
4681 return setError(E_FAIL,
4682 tr("Could not get type of controller '%s'"),
4683 aName.c_str());
4684
4685 if (!i_isControllerHotplugCapable(ctrlType))
4686 return setError(VBOX_E_NOT_SUPPORTED,
4687 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4688 aName.c_str());
4689
4690 i_setModified(IsModified_Storage);
4691 mMediumAttachments.backup();
4692
4693 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4694
4695 if (pAttach->i_getType() == DeviceType_Floppy)
4696 return setError(E_INVALIDARG,
4697 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"),
4698 aDevice, aControllerPort, aName.c_str());
4699 pAttach->i_updateHotPluggable(!!aHotPluggable);
4700
4701 return S_OK;
4702}
4703
4704HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4705 LONG aDevice)
4706{
4707 int rc = S_OK;
4708 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4709 aName.c_str(), aControllerPort, aDevice));
4710
4711 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4712
4713 return rc;
4714}
4715
4716HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4717 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4718{
4719 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4720 aName.c_str(), aControllerPort, aDevice));
4721
4722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4725 if (FAILED(rc)) return rc;
4726
4727 if (Global::IsOnlineOrTransient(mData->mMachineState))
4728 return setError(VBOX_E_INVALID_VM_STATE,
4729 tr("Invalid machine state: %s"),
4730 Global::stringifyMachineState(mData->mMachineState));
4731
4732 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4733 aName,
4734 aControllerPort,
4735 aDevice);
4736 if (!pAttach)
4737 return setError(VBOX_E_OBJECT_NOT_FOUND,
4738 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4739 aDevice, aControllerPort, aName.c_str());
4740
4741
4742 i_setModified(IsModified_Storage);
4743 mMediumAttachments.backup();
4744
4745 IBandwidthGroup *iB = aBandwidthGroup;
4746 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4747 if (aBandwidthGroup && group.isNull())
4748 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4749
4750 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4751
4752 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4753 if (strBandwidthGroupOld.isNotEmpty())
4754 {
4755 /* Get the bandwidth group object and release it - this must not fail. */
4756 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4757 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4758 Assert(SUCCEEDED(rc));
4759
4760 pBandwidthGroupOld->i_release();
4761 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4762 }
4763
4764 if (!group.isNull())
4765 {
4766 group->i_reference();
4767 pAttach->i_updateBandwidthGroup(group->i_getName());
4768 }
4769
4770 return S_OK;
4771}
4772
4773HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4774 LONG aControllerPort,
4775 LONG aDevice,
4776 DeviceType_T aType)
4777{
4778 HRESULT rc = S_OK;
4779
4780 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4781 aName.c_str(), aControllerPort, aDevice, aType));
4782
4783 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4784
4785 return rc;
4786}
4787
4788
4789HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4790 LONG aControllerPort,
4791 LONG aDevice,
4792 BOOL aForce)
4793{
4794 int rc = S_OK;
4795 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4796 aName.c_str(), aControllerPort, aForce));
4797
4798 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4799
4800 return rc;
4801}
4802
4803HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4804 LONG aControllerPort,
4805 LONG aDevice,
4806 const ComPtr<IMedium> &aMedium,
4807 BOOL aForce)
4808{
4809 int rc = S_OK;
4810 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4811 aName.c_str(), aControllerPort, aDevice, aForce));
4812
4813 // request the host lock first, since might be calling Host methods for getting host drives;
4814 // next, protect the media tree all the while we're in here, as well as our member variables
4815 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4816 this->lockHandle(),
4817 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4818
4819 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4820 aName,
4821 aControllerPort,
4822 aDevice);
4823 if (pAttach.isNull())
4824 return setError(VBOX_E_OBJECT_NOT_FOUND,
4825 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4826 aDevice, aControllerPort, aName.c_str());
4827
4828 /* Remember previously mounted medium. The medium before taking the
4829 * backup is not necessarily the same thing. */
4830 ComObjPtr<Medium> oldmedium;
4831 oldmedium = pAttach->i_getMedium();
4832
4833 IMedium *iM = aMedium;
4834 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4835 if (aMedium && pMedium.isNull())
4836 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4837
4838 AutoCaller mediumCaller(pMedium);
4839 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4840
4841 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4842 if (pMedium)
4843 {
4844 DeviceType_T mediumType = pAttach->i_getType();
4845 switch (mediumType)
4846 {
4847 case DeviceType_DVD:
4848 case DeviceType_Floppy:
4849 break;
4850
4851 default:
4852 return setError(VBOX_E_INVALID_OBJECT_STATE,
4853 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4854 aControllerPort,
4855 aDevice,
4856 aName.c_str());
4857 }
4858 }
4859
4860 i_setModified(IsModified_Storage);
4861 mMediumAttachments.backup();
4862
4863 {
4864 // The backup operation makes the pAttach reference point to the
4865 // old settings. Re-get the correct reference.
4866 pAttach = i_findAttachment(*mMediumAttachments.data(),
4867 aName,
4868 aControllerPort,
4869 aDevice);
4870 if (!oldmedium.isNull())
4871 oldmedium->i_removeBackReference(mData->mUuid);
4872 if (!pMedium.isNull())
4873 {
4874 pMedium->i_addBackReference(mData->mUuid);
4875
4876 mediumLock.release();
4877 multiLock.release();
4878 i_addMediumToRegistry(pMedium);
4879 multiLock.acquire();
4880 mediumLock.acquire();
4881 }
4882
4883 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4884 pAttach->i_updateMedium(pMedium);
4885 }
4886
4887 i_setModified(IsModified_Storage);
4888
4889 mediumLock.release();
4890 multiLock.release();
4891 rc = i_onMediumChange(pAttach, aForce);
4892 multiLock.acquire();
4893 mediumLock.acquire();
4894
4895 /* On error roll back this change only. */
4896 if (FAILED(rc))
4897 {
4898 if (!pMedium.isNull())
4899 pMedium->i_removeBackReference(mData->mUuid);
4900 pAttach = i_findAttachment(*mMediumAttachments.data(),
4901 aName,
4902 aControllerPort,
4903 aDevice);
4904 /* If the attachment is gone in the meantime, bail out. */
4905 if (pAttach.isNull())
4906 return rc;
4907 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4908 if (!oldmedium.isNull())
4909 oldmedium->i_addBackReference(mData->mUuid);
4910 pAttach->i_updateMedium(oldmedium);
4911 }
4912
4913 mediumLock.release();
4914 multiLock.release();
4915
4916 /* Save modified registries, but skip this machine as it's the caller's
4917 * job to save its settings like all other settings changes. */
4918 mParent->i_unmarkRegistryModified(i_getId());
4919 mParent->i_saveModifiedRegistries();
4920
4921 return rc;
4922}
4923HRESULT Machine::getMedium(const com::Utf8Str &aName,
4924 LONG aControllerPort,
4925 LONG aDevice,
4926 ComPtr<IMedium> &aMedium)
4927{
4928 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4929 aName.c_str(), aControllerPort, aDevice));
4930
4931 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4932
4933 aMedium = NULL;
4934
4935 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4936 aName,
4937 aControllerPort,
4938 aDevice);
4939 if (pAttach.isNull())
4940 return setError(VBOX_E_OBJECT_NOT_FOUND,
4941 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4942 aDevice, aControllerPort, aName.c_str());
4943
4944 aMedium = pAttach->i_getMedium();
4945
4946 return S_OK;
4947}
4948
4949HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4950{
4951
4952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4953
4954 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4955
4956 return S_OK;
4957}
4958
4959HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4960{
4961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4962
4963 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4964
4965 return S_OK;
4966}
4967
4968HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4969{
4970 /* Do not assert if slot is out of range, just return the advertised
4971 status. testdriver/vbox.py triggers this in logVmInfo. */
4972 if (aSlot >= mNetworkAdapters.size())
4973 return setError(E_INVALIDARG,
4974 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4975 aSlot, mNetworkAdapters.size());
4976
4977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4978
4979 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4980
4981 return S_OK;
4982}
4983
4984HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4985{
4986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4987
4988 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4989 size_t i = 0;
4990 for (settings::StringsMap::const_iterator
4991 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4992 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4993 ++it, ++i)
4994 aKeys[i] = it->first;
4995
4996 return S_OK;
4997}
4998
4999 /**
5000 * @note Locks this object for reading.
5001 */
5002HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5003 com::Utf8Str &aValue)
5004{
5005 /* start with nothing found */
5006 aValue = "";
5007
5008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5009
5010 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5011 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5012 // found:
5013 aValue = it->second; // source is a Utf8Str
5014
5015 /* return the result to caller (may be empty) */
5016 return S_OK;
5017}
5018
5019 /**
5020 * @note Locks mParent for writing + this object for writing.
5021 */
5022HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5023{
5024 Utf8Str strOldValue; // empty
5025
5026 // locking note: we only hold the read lock briefly to look up the old value,
5027 // then release it and call the onExtraCanChange callbacks. There is a small
5028 // chance of a race insofar as the callback might be called twice if two callers
5029 // change the same key at the same time, but that's a much better solution
5030 // than the deadlock we had here before. The actual changing of the extradata
5031 // is then performed under the write lock and race-free.
5032
5033 // look up the old value first; if nothing has changed then we need not do anything
5034 {
5035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5036
5037 // For snapshots don't even think about allowing changes, extradata
5038 // is global for a machine, so there is nothing snapshot specific.
5039 if (i_isSnapshotMachine())
5040 return setError(VBOX_E_INVALID_VM_STATE,
5041 tr("Cannot set extradata for a snapshot"));
5042
5043 // check if the right IMachine instance is used
5044 if (mData->mRegistered && !i_isSessionMachine())
5045 return setError(VBOX_E_INVALID_VM_STATE,
5046 tr("Cannot set extradata for an immutable machine"));
5047
5048 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5049 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5050 strOldValue = it->second;
5051 }
5052
5053 bool fChanged;
5054 if ((fChanged = (strOldValue != aValue)))
5055 {
5056 // ask for permission from all listeners outside the locks;
5057 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5058 // lock to copy the list of callbacks to invoke
5059 Bstr error;
5060 Bstr bstrValue(aValue);
5061
5062 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5063 {
5064 const char *sep = error.isEmpty() ? "" : ": ";
5065 CBSTR err = error.raw();
5066 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5067 return setError(E_ACCESSDENIED,
5068 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5069 aKey.c_str(),
5070 aValue.c_str(),
5071 sep,
5072 err);
5073 }
5074
5075 // data is changing and change not vetoed: then write it out under the lock
5076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5077
5078 if (aValue.isEmpty())
5079 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5080 else
5081 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5082 // creates a new key if needed
5083
5084 bool fNeedsGlobalSaveSettings = false;
5085 // This saving of settings is tricky: there is no "old state" for the
5086 // extradata items at all (unlike all other settings), so the old/new
5087 // settings comparison would give a wrong result!
5088 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5089
5090 if (fNeedsGlobalSaveSettings)
5091 {
5092 // save the global settings; for that we should hold only the VirtualBox lock
5093 alock.release();
5094 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5095 mParent->i_saveSettings();
5096 }
5097 }
5098
5099 // fire notification outside the lock
5100 if (fChanged)
5101 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5102
5103 return S_OK;
5104}
5105
5106HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5107{
5108 aProgress = NULL;
5109 NOREF(aSettingsFilePath);
5110 ReturnComNotImplemented();
5111}
5112
5113HRESULT Machine::saveSettings()
5114{
5115 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5116
5117 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5118 if (FAILED(rc)) return rc;
5119
5120 /* the settings file path may never be null */
5121 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5122
5123 /* save all VM data excluding snapshots */
5124 bool fNeedsGlobalSaveSettings = false;
5125 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5126 mlock.release();
5127
5128 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5129 {
5130 // save the global settings; for that we should hold only the VirtualBox lock
5131 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5132 rc = mParent->i_saveSettings();
5133 }
5134
5135 return rc;
5136}
5137
5138
5139HRESULT Machine::discardSettings()
5140{
5141 /*
5142 * We need to take the machine list lock here as well as the machine one
5143 * or we'll get into trouble should any media stuff require rolling back.
5144 *
5145 * Details:
5146 *
5147 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5148 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5149 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
5150 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5151 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5152 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5153 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5154 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
5155 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5156 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5157 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5158 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5159 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5160 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
5161 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
5162 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5163 * 0:005> k
5164 * # Child-SP RetAddr Call Site
5165 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5166 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5167 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5168 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5169 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5170 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5171 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5172 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5173 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5174 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5175 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5176 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5177 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5178 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5179 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5180 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5181 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5182 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5183 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5184 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5185 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5186 *
5187 */
5188 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5190
5191 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5192 if (FAILED(rc)) return rc;
5193
5194 /*
5195 * during this rollback, the session will be notified if data has
5196 * been actually changed
5197 */
5198 i_rollback(true /* aNotify */);
5199
5200 return S_OK;
5201}
5202
5203/** @note Locks objects! */
5204HRESULT Machine::unregister(AutoCaller &autoCaller,
5205 CleanupMode_T aCleanupMode,
5206 std::vector<ComPtr<IMedium> > &aMedia)
5207{
5208 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5209
5210 Guid id(i_getId());
5211
5212 if (mData->mSession.mState != SessionState_Unlocked)
5213 return setError(VBOX_E_INVALID_OBJECT_STATE,
5214 tr("Cannot unregister the machine '%s' while it is locked"),
5215 mUserData->s.strName.c_str());
5216
5217 // wait for state dependents to drop to zero
5218 i_ensureNoStateDependencies();
5219
5220 if (!mData->mAccessible)
5221 {
5222 // inaccessible maschines can only be unregistered; uninitialize ourselves
5223 // here because currently there may be no unregistered that are inaccessible
5224 // (this state combination is not supported). Note releasing the caller and
5225 // leaving the lock before calling uninit()
5226 alock.release();
5227 autoCaller.release();
5228
5229 uninit();
5230
5231 mParent->i_unregisterMachine(this, id);
5232 // calls VirtualBox::i_saveSettings()
5233
5234 return S_OK;
5235 }
5236
5237 HRESULT rc = S_OK;
5238
5239 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5240 // discard saved state
5241 if (mData->mMachineState == MachineState_Saved)
5242 {
5243 // add the saved state file to the list of files the caller should delete
5244 Assert(!mSSData->strStateFilePath.isEmpty());
5245 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5246
5247 mSSData->strStateFilePath.setNull();
5248
5249 // unconditionally set the machine state to powered off, we now
5250 // know no session has locked the machine
5251 mData->mMachineState = MachineState_PoweredOff;
5252 }
5253
5254 size_t cSnapshots = 0;
5255 if (mData->mFirstSnapshot)
5256 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5257 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5258 // fail now before we start detaching media
5259 return setError(VBOX_E_INVALID_OBJECT_STATE,
5260 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5261 mUserData->s.strName.c_str(), cSnapshots);
5262
5263 // This list collects the medium objects from all medium attachments
5264 // which we will detach from the machine and its snapshots, in a specific
5265 // order which allows for closing all media without getting "media in use"
5266 // errors, simply by going through the list from the front to the back:
5267 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5268 // and must be closed before the parent media from the snapshots, or closing the parents
5269 // will fail because they still have children);
5270 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5271 // the root ("first") snapshot of the machine.
5272 MediaList llMedia;
5273
5274 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5275 && mMediumAttachments->size()
5276 )
5277 {
5278 // we have media attachments: detach them all and add the Medium objects to our list
5279 if (aCleanupMode != CleanupMode_UnregisterOnly)
5280 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5281 else
5282 return setError(VBOX_E_INVALID_OBJECT_STATE,
5283 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5284 mUserData->s.strName.c_str(), mMediumAttachments->size());
5285 }
5286
5287 if (cSnapshots)
5288 {
5289 // add the media from the medium attachments of the snapshots to llMedia
5290 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5291 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5292 // into the children first
5293
5294 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5295 MachineState_T oldState = mData->mMachineState;
5296 mData->mMachineState = MachineState_DeletingSnapshot;
5297
5298 // make a copy of the first snapshot so the refcount does not drop to 0
5299 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5300 // because of the AutoCaller voodoo)
5301 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5302
5303 // GO!
5304 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5305
5306 mData->mMachineState = oldState;
5307 }
5308
5309 if (FAILED(rc))
5310 {
5311 i_rollbackMedia();
5312 return rc;
5313 }
5314
5315 // commit all the media changes made above
5316 i_commitMedia();
5317
5318 mData->mRegistered = false;
5319
5320 // machine lock no longer needed
5321 alock.release();
5322
5323 // return media to caller
5324 aMedia.resize(llMedia.size());
5325 size_t i = 0;
5326 for (MediaList::const_iterator
5327 it = llMedia.begin();
5328 it != llMedia.end();
5329 ++it, ++i)
5330 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5331
5332 mParent->i_unregisterMachine(this, id);
5333 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5334
5335 return S_OK;
5336}
5337
5338/**
5339 * Task record for deleting a machine config.
5340 */
5341class Machine::DeleteConfigTask
5342 : public Machine::Task
5343{
5344public:
5345 DeleteConfigTask(Machine *m,
5346 Progress *p,
5347 const Utf8Str &t,
5348 const RTCList<ComPtr<IMedium> > &llMediums,
5349 const StringsList &llFilesToDelete)
5350 : Task(m, p, t),
5351 m_llMediums(llMediums),
5352 m_llFilesToDelete(llFilesToDelete)
5353 {}
5354
5355private:
5356 void handler()
5357 {
5358 try
5359 {
5360 m_pMachine->i_deleteConfigHandler(*this);
5361 }
5362 catch (...)
5363 {
5364 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5365 }
5366 }
5367
5368 RTCList<ComPtr<IMedium> > m_llMediums;
5369 StringsList m_llFilesToDelete;
5370
5371 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5372};
5373
5374/**
5375 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5376 * SessionMachine::taskHandler().
5377 *
5378 * @note Locks this object for writing.
5379 *
5380 * @param task
5381 * @return
5382 */
5383void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5384{
5385 LogFlowThisFuncEnter();
5386
5387 AutoCaller autoCaller(this);
5388 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5389 if (FAILED(autoCaller.rc()))
5390 {
5391 /* we might have been uninitialized because the session was accidentally
5392 * closed by the client, so don't assert */
5393 HRESULT rc = setError(E_FAIL,
5394 tr("The session has been accidentally closed"));
5395 task.m_pProgress->i_notifyComplete(rc);
5396 LogFlowThisFuncLeave();
5397 return;
5398 }
5399
5400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5401
5402 HRESULT rc = S_OK;
5403
5404 try
5405 {
5406 ULONG uLogHistoryCount = 3;
5407 ComPtr<ISystemProperties> systemProperties;
5408 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5409 if (FAILED(rc)) throw rc;
5410
5411 if (!systemProperties.isNull())
5412 {
5413 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5414 if (FAILED(rc)) throw rc;
5415 }
5416
5417 MachineState_T oldState = mData->mMachineState;
5418 i_setMachineState(MachineState_SettingUp);
5419 alock.release();
5420 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5421 {
5422 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5423 {
5424 AutoCaller mac(pMedium);
5425 if (FAILED(mac.rc())) throw mac.rc();
5426 Utf8Str strLocation = pMedium->i_getLocationFull();
5427 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5428 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5429 if (FAILED(rc)) throw rc;
5430 }
5431 if (pMedium->i_isMediumFormatFile())
5432 {
5433 ComPtr<IProgress> pProgress2;
5434 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5435 if (FAILED(rc)) throw rc;
5436 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5437 if (FAILED(rc)) throw rc;
5438 /* Check the result of the asynchronous process. */
5439 LONG iRc;
5440 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5441 if (FAILED(rc)) throw rc;
5442 /* If the thread of the progress object has an error, then
5443 * retrieve the error info from there, or it'll be lost. */
5444 if (FAILED(iRc))
5445 throw setError(ProgressErrorInfo(pProgress2));
5446 }
5447
5448 /* Close the medium, deliberately without checking the return
5449 * code, and without leaving any trace in the error info, as
5450 * a failure here is a very minor issue, which shouldn't happen
5451 * as above we even managed to delete the medium. */
5452 {
5453 ErrorInfoKeeper eik;
5454 pMedium->Close();
5455 }
5456 }
5457 i_setMachineState(oldState);
5458 alock.acquire();
5459
5460 // delete the files pushed on the task list by Machine::Delete()
5461 // (this includes saved states of the machine and snapshots and
5462 // medium storage files from the IMedium list passed in, and the
5463 // machine XML file)
5464 for (StringsList::const_iterator
5465 it = task.m_llFilesToDelete.begin();
5466 it != task.m_llFilesToDelete.end();
5467 ++it)
5468 {
5469 const Utf8Str &strFile = *it;
5470 LogFunc(("Deleting file %s\n", strFile.c_str()));
5471 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5472 if (FAILED(rc)) throw rc;
5473
5474 int vrc = RTFileDelete(strFile.c_str());
5475 if (RT_FAILURE(vrc))
5476 throw setError(VBOX_E_IPRT_ERROR,
5477 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5478 }
5479
5480 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5481 if (FAILED(rc)) throw rc;
5482
5483 /* delete the settings only when the file actually exists */
5484 if (mData->pMachineConfigFile->fileExists())
5485 {
5486 /* Delete any backup or uncommitted XML files. Ignore failures.
5487 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5488 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5489 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5490 RTFileDelete(otherXml.c_str());
5491 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5492 RTFileDelete(otherXml.c_str());
5493
5494 /* delete the Logs folder, nothing important should be left
5495 * there (we don't check for errors because the user might have
5496 * some private files there that we don't want to delete) */
5497 Utf8Str logFolder;
5498 getLogFolder(logFolder);
5499 Assert(logFolder.length());
5500 if (RTDirExists(logFolder.c_str()))
5501 {
5502 /* Delete all VBox.log[.N] files from the Logs folder
5503 * (this must be in sync with the rotation logic in
5504 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5505 * files that may have been created by the GUI. */
5506 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5507 logFolder.c_str(), RTPATH_DELIMITER);
5508 RTFileDelete(log.c_str());
5509 log = Utf8StrFmt("%s%cVBox.png",
5510 logFolder.c_str(), RTPATH_DELIMITER);
5511 RTFileDelete(log.c_str());
5512 for (int i = uLogHistoryCount; i > 0; i--)
5513 {
5514 log = Utf8StrFmt("%s%cVBox.log.%d",
5515 logFolder.c_str(), RTPATH_DELIMITER, i);
5516 RTFileDelete(log.c_str());
5517 log = Utf8StrFmt("%s%cVBox.png.%d",
5518 logFolder.c_str(), RTPATH_DELIMITER, i);
5519 RTFileDelete(log.c_str());
5520 }
5521#if defined(RT_OS_WINDOWS)
5522 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5523 RTFileDelete(log.c_str());
5524 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5525 RTFileDelete(log.c_str());
5526#endif
5527
5528 RTDirRemove(logFolder.c_str());
5529 }
5530
5531 /* delete the Snapshots folder, nothing important should be left
5532 * there (we don't check for errors because the user might have
5533 * some private files there that we don't want to delete) */
5534 Utf8Str strFullSnapshotFolder;
5535 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5536 Assert(!strFullSnapshotFolder.isEmpty());
5537 if (RTDirExists(strFullSnapshotFolder.c_str()))
5538 RTDirRemove(strFullSnapshotFolder.c_str());
5539
5540 // delete the directory that contains the settings file, but only
5541 // if it matches the VM name
5542 Utf8Str settingsDir;
5543 if (i_isInOwnDir(&settingsDir))
5544 RTDirRemove(settingsDir.c_str());
5545 }
5546
5547 alock.release();
5548
5549 mParent->i_saveModifiedRegistries();
5550 }
5551 catch (HRESULT aRC) { rc = aRC; }
5552
5553 task.m_pProgress->i_notifyComplete(rc);
5554
5555 LogFlowThisFuncLeave();
5556}
5557
5558HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5559{
5560 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5561
5562 HRESULT rc = i_checkStateDependency(MutableStateDep);
5563 if (FAILED(rc)) return rc;
5564
5565 if (mData->mRegistered)
5566 return setError(VBOX_E_INVALID_VM_STATE,
5567 tr("Cannot delete settings of a registered machine"));
5568
5569 // collect files to delete
5570 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5571 if (mData->pMachineConfigFile->fileExists())
5572 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5573
5574 RTCList<ComPtr<IMedium> > llMediums;
5575 for (size_t i = 0; i < aMedia.size(); ++i)
5576 {
5577 IMedium *pIMedium(aMedia[i]);
5578 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5579 if (pMedium.isNull())
5580 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5581 SafeArray<BSTR> ids;
5582 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5583 if (FAILED(rc)) return rc;
5584 /* At this point the medium should not have any back references
5585 * anymore. If it has it is attached to another VM and *must* not
5586 * deleted. */
5587 if (ids.size() < 1)
5588 llMediums.append(pMedium);
5589 }
5590
5591 ComObjPtr<Progress> pProgress;
5592 pProgress.createObject();
5593 rc = pProgress->init(i_getVirtualBox(),
5594 static_cast<IMachine*>(this) /* aInitiator */,
5595 tr("Deleting files"),
5596 true /* fCancellable */,
5597 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5598 tr("Collecting file inventory"));
5599 if (FAILED(rc))
5600 return rc;
5601
5602 /* create and start the task on a separate thread (note that it will not
5603 * start working until we release alock) */
5604 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5605 rc = pTask->createThread();
5606 if (FAILED(rc))
5607 return rc;
5608
5609 pProgress.queryInterfaceTo(aProgress.asOutParam());
5610
5611 LogFlowFuncLeave();
5612
5613 return S_OK;
5614}
5615
5616HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5617{
5618 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5619
5620 ComObjPtr<Snapshot> pSnapshot;
5621 HRESULT rc;
5622
5623 if (aNameOrId.isEmpty())
5624 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5625 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5626 else
5627 {
5628 Guid uuid(aNameOrId);
5629 if (uuid.isValid())
5630 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5631 else
5632 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5633 }
5634 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5635
5636 return rc;
5637}
5638
5639HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5640{
5641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5642
5643 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5644 if (FAILED(rc)) return rc;
5645
5646 ComObjPtr<SharedFolder> sharedFolder;
5647 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5648 if (SUCCEEDED(rc))
5649 return setError(VBOX_E_OBJECT_IN_USE,
5650 tr("Shared folder named '%s' already exists"),
5651 aName.c_str());
5652
5653 sharedFolder.createObject();
5654 rc = sharedFolder->init(i_getMachine(),
5655 aName,
5656 aHostPath,
5657 !!aWritable,
5658 !!aAutomount,
5659 true /* fFailOnError */);
5660 if (FAILED(rc)) return rc;
5661
5662 i_setModified(IsModified_SharedFolders);
5663 mHWData.backup();
5664 mHWData->mSharedFolders.push_back(sharedFolder);
5665
5666 /* inform the direct session if any */
5667 alock.release();
5668 i_onSharedFolderChange();
5669
5670 return S_OK;
5671}
5672
5673HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5674{
5675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5676
5677 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5678 if (FAILED(rc)) return rc;
5679
5680 ComObjPtr<SharedFolder> sharedFolder;
5681 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5682 if (FAILED(rc)) return rc;
5683
5684 i_setModified(IsModified_SharedFolders);
5685 mHWData.backup();
5686 mHWData->mSharedFolders.remove(sharedFolder);
5687
5688 /* inform the direct session if any */
5689 alock.release();
5690 i_onSharedFolderChange();
5691
5692 return S_OK;
5693}
5694
5695HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5696{
5697 /* start with No */
5698 *aCanShow = FALSE;
5699
5700 ComPtr<IInternalSessionControl> directControl;
5701 {
5702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5703
5704 if (mData->mSession.mState != SessionState_Locked)
5705 return setError(VBOX_E_INVALID_VM_STATE,
5706 tr("Machine is not locked for session (session state: %s)"),
5707 Global::stringifySessionState(mData->mSession.mState));
5708
5709 if (mData->mSession.mLockType == LockType_VM)
5710 directControl = mData->mSession.mDirectControl;
5711 }
5712
5713 /* ignore calls made after #OnSessionEnd() is called */
5714 if (!directControl)
5715 return S_OK;
5716
5717 LONG64 dummy;
5718 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5719}
5720
5721HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5722{
5723 ComPtr<IInternalSessionControl> directControl;
5724 {
5725 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5726
5727 if (mData->mSession.mState != SessionState_Locked)
5728 return setError(E_FAIL,
5729 tr("Machine is not locked for session (session state: %s)"),
5730 Global::stringifySessionState(mData->mSession.mState));
5731
5732 if (mData->mSession.mLockType == LockType_VM)
5733 directControl = mData->mSession.mDirectControl;
5734 }
5735
5736 /* ignore calls made after #OnSessionEnd() is called */
5737 if (!directControl)
5738 return S_OK;
5739
5740 BOOL dummy;
5741 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5742}
5743
5744#ifdef VBOX_WITH_GUEST_PROPS
5745/**
5746 * Look up a guest property in VBoxSVC's internal structures.
5747 */
5748HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5749 com::Utf8Str &aValue,
5750 LONG64 *aTimestamp,
5751 com::Utf8Str &aFlags) const
5752{
5753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5754
5755 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5756 if (it != mHWData->mGuestProperties.end())
5757 {
5758 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5759 aValue = it->second.strValue;
5760 *aTimestamp = it->second.mTimestamp;
5761 GuestPropWriteFlags(it->second.mFlags, szFlags);
5762 aFlags = Utf8Str(szFlags);
5763 }
5764
5765 return S_OK;
5766}
5767
5768/**
5769 * Query the VM that a guest property belongs to for the property.
5770 * @returns E_ACCESSDENIED if the VM process is not available or not
5771 * currently handling queries and the lookup should then be done in
5772 * VBoxSVC.
5773 */
5774HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5775 com::Utf8Str &aValue,
5776 LONG64 *aTimestamp,
5777 com::Utf8Str &aFlags) const
5778{
5779 HRESULT rc = S_OK;
5780 BSTR bValue = NULL;
5781 BSTR bFlags = NULL;
5782
5783 ComPtr<IInternalSessionControl> directControl;
5784 {
5785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5786 if (mData->mSession.mLockType == LockType_VM)
5787 directControl = mData->mSession.mDirectControl;
5788 }
5789
5790 /* ignore calls made after #OnSessionEnd() is called */
5791 if (!directControl)
5792 rc = E_ACCESSDENIED;
5793 else
5794 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5795 0 /* accessMode */,
5796 &bValue, aTimestamp, &bFlags);
5797
5798 aValue = bValue;
5799 aFlags = bFlags;
5800
5801 return rc;
5802}
5803#endif // VBOX_WITH_GUEST_PROPS
5804
5805HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5806 com::Utf8Str &aValue,
5807 LONG64 *aTimestamp,
5808 com::Utf8Str &aFlags)
5809{
5810#ifndef VBOX_WITH_GUEST_PROPS
5811 ReturnComNotImplemented();
5812#else // VBOX_WITH_GUEST_PROPS
5813
5814 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5815
5816 if (rc == E_ACCESSDENIED)
5817 /* The VM is not running or the service is not (yet) accessible */
5818 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5819 return rc;
5820#endif // VBOX_WITH_GUEST_PROPS
5821}
5822
5823HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5824{
5825 LONG64 dummyTimestamp;
5826 com::Utf8Str dummyFlags;
5827 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5828 return rc;
5829
5830}
5831HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5832{
5833 com::Utf8Str dummyFlags;
5834 com::Utf8Str dummyValue;
5835 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5836 return rc;
5837}
5838
5839#ifdef VBOX_WITH_GUEST_PROPS
5840/**
5841 * Set a guest property in VBoxSVC's internal structures.
5842 */
5843HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5844 const com::Utf8Str &aFlags, bool fDelete)
5845{
5846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5847 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5848 if (FAILED(rc)) return rc;
5849
5850 try
5851 {
5852 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5853 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5854 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5855
5856 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5857 if (it == mHWData->mGuestProperties.end())
5858 {
5859 if (!fDelete)
5860 {
5861 i_setModified(IsModified_MachineData);
5862 mHWData.backupEx();
5863
5864 RTTIMESPEC time;
5865 HWData::GuestProperty prop;
5866 prop.strValue = Bstr(aValue).raw();
5867 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5868 prop.mFlags = fFlags;
5869 mHWData->mGuestProperties[aName] = prop;
5870 }
5871 }
5872 else
5873 {
5874 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5875 {
5876 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5877 }
5878 else
5879 {
5880 i_setModified(IsModified_MachineData);
5881 mHWData.backupEx();
5882
5883 /* The backupEx() operation invalidates our iterator,
5884 * so get a new one. */
5885 it = mHWData->mGuestProperties.find(aName);
5886 Assert(it != mHWData->mGuestProperties.end());
5887
5888 if (!fDelete)
5889 {
5890 RTTIMESPEC time;
5891 it->second.strValue = aValue;
5892 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5893 it->second.mFlags = fFlags;
5894 }
5895 else
5896 mHWData->mGuestProperties.erase(it);
5897 }
5898 }
5899
5900 if (SUCCEEDED(rc))
5901 {
5902 alock.release();
5903
5904 mParent->i_onGuestPropertyChange(mData->mUuid,
5905 Bstr(aName).raw(),
5906 Bstr(aValue).raw(),
5907 Bstr(aFlags).raw());
5908 }
5909 }
5910 catch (std::bad_alloc &)
5911 {
5912 rc = E_OUTOFMEMORY;
5913 }
5914
5915 return rc;
5916}
5917
5918/**
5919 * Set a property on the VM that that property belongs to.
5920 * @returns E_ACCESSDENIED if the VM process is not available or not
5921 * currently handling queries and the setting should then be done in
5922 * VBoxSVC.
5923 */
5924HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5925 const com::Utf8Str &aFlags, bool fDelete)
5926{
5927 HRESULT rc;
5928
5929 try
5930 {
5931 ComPtr<IInternalSessionControl> directControl;
5932 {
5933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5934 if (mData->mSession.mLockType == LockType_VM)
5935 directControl = mData->mSession.mDirectControl;
5936 }
5937
5938 BSTR dummy = NULL; /* will not be changed (setter) */
5939 LONG64 dummy64;
5940 if (!directControl)
5941 rc = E_ACCESSDENIED;
5942 else
5943 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5944 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5945 fDelete? 2: 1 /* accessMode */,
5946 &dummy, &dummy64, &dummy);
5947 }
5948 catch (std::bad_alloc &)
5949 {
5950 rc = E_OUTOFMEMORY;
5951 }
5952
5953 return rc;
5954}
5955#endif // VBOX_WITH_GUEST_PROPS
5956
5957HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5958 const com::Utf8Str &aFlags)
5959{
5960#ifndef VBOX_WITH_GUEST_PROPS
5961 ReturnComNotImplemented();
5962#else // VBOX_WITH_GUEST_PROPS
5963 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5964 if (rc == E_ACCESSDENIED)
5965 /* The VM is not running or the service is not (yet) accessible */
5966 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5967 return rc;
5968#endif // VBOX_WITH_GUEST_PROPS
5969}
5970
5971HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5972{
5973 return setGuestProperty(aProperty, aValue, "");
5974}
5975
5976HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5977{
5978#ifndef VBOX_WITH_GUEST_PROPS
5979 ReturnComNotImplemented();
5980#else // VBOX_WITH_GUEST_PROPS
5981 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5982 if (rc == E_ACCESSDENIED)
5983 /* The VM is not running or the service is not (yet) accessible */
5984 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5985 return rc;
5986#endif // VBOX_WITH_GUEST_PROPS
5987}
5988
5989#ifdef VBOX_WITH_GUEST_PROPS
5990/**
5991 * Enumerate the guest properties in VBoxSVC's internal structures.
5992 */
5993HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5994 std::vector<com::Utf8Str> &aNames,
5995 std::vector<com::Utf8Str> &aValues,
5996 std::vector<LONG64> &aTimestamps,
5997 std::vector<com::Utf8Str> &aFlags)
5998{
5999 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6000 Utf8Str strPatterns(aPatterns);
6001
6002 /*
6003 * Look for matching patterns and build up a list.
6004 */
6005 HWData::GuestPropertyMap propMap;
6006 for (HWData::GuestPropertyMap::const_iterator
6007 it = mHWData->mGuestProperties.begin();
6008 it != mHWData->mGuestProperties.end();
6009 ++it)
6010 {
6011 if ( strPatterns.isEmpty()
6012 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6013 RTSTR_MAX,
6014 it->first.c_str(),
6015 RTSTR_MAX,
6016 NULL)
6017 )
6018 propMap.insert(*it);
6019 }
6020
6021 alock.release();
6022
6023 /*
6024 * And build up the arrays for returning the property information.
6025 */
6026 size_t cEntries = propMap.size();
6027
6028 aNames.resize(cEntries);
6029 aValues.resize(cEntries);
6030 aTimestamps.resize(cEntries);
6031 aFlags.resize(cEntries);
6032
6033 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6034 size_t i = 0;
6035 for (HWData::GuestPropertyMap::const_iterator
6036 it = propMap.begin();
6037 it != propMap.end();
6038 ++it, ++i)
6039 {
6040 aNames[i] = it->first;
6041 aValues[i] = it->second.strValue;
6042 aTimestamps[i] = it->second.mTimestamp;
6043 GuestPropWriteFlags(it->second.mFlags, szFlags);
6044 aFlags[i] = Utf8Str(szFlags);
6045 }
6046
6047 return S_OK;
6048}
6049
6050/**
6051 * Enumerate the properties managed by a VM.
6052 * @returns E_ACCESSDENIED if the VM process is not available or not
6053 * currently handling queries and the setting should then be done in
6054 * VBoxSVC.
6055 */
6056HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6057 std::vector<com::Utf8Str> &aNames,
6058 std::vector<com::Utf8Str> &aValues,
6059 std::vector<LONG64> &aTimestamps,
6060 std::vector<com::Utf8Str> &aFlags)
6061{
6062 HRESULT rc;
6063 ComPtr<IInternalSessionControl> directControl;
6064 {
6065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6066 if (mData->mSession.mLockType == LockType_VM)
6067 directControl = mData->mSession.mDirectControl;
6068 }
6069
6070 com::SafeArray<BSTR> bNames;
6071 com::SafeArray<BSTR> bValues;
6072 com::SafeArray<LONG64> bTimestamps;
6073 com::SafeArray<BSTR> bFlags;
6074
6075 if (!directControl)
6076 rc = E_ACCESSDENIED;
6077 else
6078 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6079 ComSafeArrayAsOutParam(bNames),
6080 ComSafeArrayAsOutParam(bValues),
6081 ComSafeArrayAsOutParam(bTimestamps),
6082 ComSafeArrayAsOutParam(bFlags));
6083 size_t i;
6084 aNames.resize(bNames.size());
6085 for (i = 0; i < bNames.size(); ++i)
6086 aNames[i] = Utf8Str(bNames[i]);
6087 aValues.resize(bValues.size());
6088 for (i = 0; i < bValues.size(); ++i)
6089 aValues[i] = Utf8Str(bValues[i]);
6090 aTimestamps.resize(bTimestamps.size());
6091 for (i = 0; i < bTimestamps.size(); ++i)
6092 aTimestamps[i] = bTimestamps[i];
6093 aFlags.resize(bFlags.size());
6094 for (i = 0; i < bFlags.size(); ++i)
6095 aFlags[i] = Utf8Str(bFlags[i]);
6096
6097 return rc;
6098}
6099#endif // VBOX_WITH_GUEST_PROPS
6100HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6101 std::vector<com::Utf8Str> &aNames,
6102 std::vector<com::Utf8Str> &aValues,
6103 std::vector<LONG64> &aTimestamps,
6104 std::vector<com::Utf8Str> &aFlags)
6105{
6106#ifndef VBOX_WITH_GUEST_PROPS
6107 ReturnComNotImplemented();
6108#else // VBOX_WITH_GUEST_PROPS
6109
6110 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6111
6112 if (rc == E_ACCESSDENIED)
6113 /* The VM is not running or the service is not (yet) accessible */
6114 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6115 return rc;
6116#endif // VBOX_WITH_GUEST_PROPS
6117}
6118
6119HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6120 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6121{
6122 MediumAttachmentList atts;
6123
6124 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6125 if (FAILED(rc)) return rc;
6126
6127 aMediumAttachments.resize(atts.size());
6128 size_t i = 0;
6129 for (MediumAttachmentList::const_iterator
6130 it = atts.begin();
6131 it != atts.end();
6132 ++it, ++i)
6133 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6134
6135 return S_OK;
6136}
6137
6138HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6139 LONG aControllerPort,
6140 LONG aDevice,
6141 ComPtr<IMediumAttachment> &aAttachment)
6142{
6143 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6144 aName.c_str(), aControllerPort, aDevice));
6145
6146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6147
6148 aAttachment = NULL;
6149
6150 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6151 aName,
6152 aControllerPort,
6153 aDevice);
6154 if (pAttach.isNull())
6155 return setError(VBOX_E_OBJECT_NOT_FOUND,
6156 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6157 aDevice, aControllerPort, aName.c_str());
6158
6159 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6160
6161 return S_OK;
6162}
6163
6164
6165HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6166 StorageBus_T aConnectionType,
6167 ComPtr<IStorageController> &aController)
6168{
6169 if ( (aConnectionType <= StorageBus_Null)
6170 || (aConnectionType > StorageBus_PCIe))
6171 return setError(E_INVALIDARG,
6172 tr("Invalid connection type: %d"),
6173 aConnectionType);
6174
6175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6176
6177 HRESULT rc = i_checkStateDependency(MutableStateDep);
6178 if (FAILED(rc)) return rc;
6179
6180 /* try to find one with the name first. */
6181 ComObjPtr<StorageController> ctrl;
6182
6183 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6184 if (SUCCEEDED(rc))
6185 return setError(VBOX_E_OBJECT_IN_USE,
6186 tr("Storage controller named '%s' already exists"),
6187 aName.c_str());
6188
6189 ctrl.createObject();
6190
6191 /* get a new instance number for the storage controller */
6192 ULONG ulInstance = 0;
6193 bool fBootable = true;
6194 for (StorageControllerList::const_iterator
6195 it = mStorageControllers->begin();
6196 it != mStorageControllers->end();
6197 ++it)
6198 {
6199 if ((*it)->i_getStorageBus() == aConnectionType)
6200 {
6201 ULONG ulCurInst = (*it)->i_getInstance();
6202
6203 if (ulCurInst >= ulInstance)
6204 ulInstance = ulCurInst + 1;
6205
6206 /* Only one controller of each type can be marked as bootable. */
6207 if ((*it)->i_getBootable())
6208 fBootable = false;
6209 }
6210 }
6211
6212 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6213 if (FAILED(rc)) return rc;
6214
6215 i_setModified(IsModified_Storage);
6216 mStorageControllers.backup();
6217 mStorageControllers->push_back(ctrl);
6218
6219 ctrl.queryInterfaceTo(aController.asOutParam());
6220
6221 /* inform the direct session if any */
6222 alock.release();
6223 i_onStorageControllerChange();
6224
6225 return S_OK;
6226}
6227
6228HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6229 ComPtr<IStorageController> &aStorageController)
6230{
6231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6232
6233 ComObjPtr<StorageController> ctrl;
6234
6235 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6236 if (SUCCEEDED(rc))
6237 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6238
6239 return rc;
6240}
6241
6242HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6243 ULONG aInstance,
6244 ComPtr<IStorageController> &aStorageController)
6245{
6246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6247
6248 for (StorageControllerList::const_iterator
6249 it = mStorageControllers->begin();
6250 it != mStorageControllers->end();
6251 ++it)
6252 {
6253 if ( (*it)->i_getStorageBus() == aConnectionType
6254 && (*it)->i_getInstance() == aInstance)
6255 {
6256 (*it).queryInterfaceTo(aStorageController.asOutParam());
6257 return S_OK;
6258 }
6259 }
6260
6261 return setError(VBOX_E_OBJECT_NOT_FOUND,
6262 tr("Could not find a storage controller with instance number '%lu'"),
6263 aInstance);
6264}
6265
6266HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6267{
6268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6269
6270 HRESULT rc = i_checkStateDependency(MutableStateDep);
6271 if (FAILED(rc)) return rc;
6272
6273 ComObjPtr<StorageController> ctrl;
6274
6275 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6276 if (SUCCEEDED(rc))
6277 {
6278 /* Ensure that only one controller of each type is marked as bootable. */
6279 if (aBootable == TRUE)
6280 {
6281 for (StorageControllerList::const_iterator
6282 it = mStorageControllers->begin();
6283 it != mStorageControllers->end();
6284 ++it)
6285 {
6286 ComObjPtr<StorageController> aCtrl = (*it);
6287
6288 if ( (aCtrl->i_getName() != aName)
6289 && aCtrl->i_getBootable() == TRUE
6290 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6291 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6292 {
6293 aCtrl->i_setBootable(FALSE);
6294 break;
6295 }
6296 }
6297 }
6298
6299 if (SUCCEEDED(rc))
6300 {
6301 ctrl->i_setBootable(aBootable);
6302 i_setModified(IsModified_Storage);
6303 }
6304 }
6305
6306 if (SUCCEEDED(rc))
6307 {
6308 /* inform the direct session if any */
6309 alock.release();
6310 i_onStorageControllerChange();
6311 }
6312
6313 return rc;
6314}
6315
6316HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6317{
6318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6319
6320 HRESULT rc = i_checkStateDependency(MutableStateDep);
6321 if (FAILED(rc)) return rc;
6322
6323 ComObjPtr<StorageController> ctrl;
6324 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6325 if (FAILED(rc)) return rc;
6326
6327 {
6328 /* find all attached devices to the appropriate storage controller and detach them all */
6329 // make a temporary list because detachDevice invalidates iterators into
6330 // mMediumAttachments
6331 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6332
6333 for (MediumAttachmentList::const_iterator
6334 it = llAttachments2.begin();
6335 it != llAttachments2.end();
6336 ++it)
6337 {
6338 MediumAttachment *pAttachTemp = *it;
6339
6340 AutoCaller localAutoCaller(pAttachTemp);
6341 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6342
6343 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6344
6345 if (pAttachTemp->i_getControllerName() == aName)
6346 {
6347 rc = i_detachDevice(pAttachTemp, alock, NULL);
6348 if (FAILED(rc)) return rc;
6349 }
6350 }
6351 }
6352
6353 /* We can remove it now. */
6354 i_setModified(IsModified_Storage);
6355 mStorageControllers.backup();
6356
6357 ctrl->i_unshare();
6358
6359 mStorageControllers->remove(ctrl);
6360
6361 /* inform the direct session if any */
6362 alock.release();
6363 i_onStorageControllerChange();
6364
6365 return S_OK;
6366}
6367
6368HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6369 ComPtr<IUSBController> &aController)
6370{
6371 if ( (aType <= USBControllerType_Null)
6372 || (aType >= USBControllerType_Last))
6373 return setError(E_INVALIDARG,
6374 tr("Invalid USB controller type: %d"),
6375 aType);
6376
6377 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 HRESULT rc = i_checkStateDependency(MutableStateDep);
6380 if (FAILED(rc)) return rc;
6381
6382 /* try to find one with the same type first. */
6383 ComObjPtr<USBController> ctrl;
6384
6385 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6386 if (SUCCEEDED(rc))
6387 return setError(VBOX_E_OBJECT_IN_USE,
6388 tr("USB controller named '%s' already exists"),
6389 aName.c_str());
6390
6391 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6392 ULONG maxInstances;
6393 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6394 if (FAILED(rc))
6395 return rc;
6396
6397 ULONG cInstances = i_getUSBControllerCountByType(aType);
6398 if (cInstances >= maxInstances)
6399 return setError(E_INVALIDARG,
6400 tr("Too many USB controllers of this type"));
6401
6402 ctrl.createObject();
6403
6404 rc = ctrl->init(this, aName, aType);
6405 if (FAILED(rc)) return rc;
6406
6407 i_setModified(IsModified_USB);
6408 mUSBControllers.backup();
6409 mUSBControllers->push_back(ctrl);
6410
6411 ctrl.queryInterfaceTo(aController.asOutParam());
6412
6413 /* inform the direct session if any */
6414 alock.release();
6415 i_onUSBControllerChange();
6416
6417 return S_OK;
6418}
6419
6420HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6421{
6422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6423
6424 ComObjPtr<USBController> ctrl;
6425
6426 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6427 if (SUCCEEDED(rc))
6428 ctrl.queryInterfaceTo(aController.asOutParam());
6429
6430 return rc;
6431}
6432
6433HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6434 ULONG *aControllers)
6435{
6436 if ( (aType <= USBControllerType_Null)
6437 || (aType >= USBControllerType_Last))
6438 return setError(E_INVALIDARG,
6439 tr("Invalid USB controller type: %d"),
6440 aType);
6441
6442 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6443
6444 ComObjPtr<USBController> ctrl;
6445
6446 *aControllers = i_getUSBControllerCountByType(aType);
6447
6448 return S_OK;
6449}
6450
6451HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6452{
6453
6454 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6455
6456 HRESULT rc = i_checkStateDependency(MutableStateDep);
6457 if (FAILED(rc)) return rc;
6458
6459 ComObjPtr<USBController> ctrl;
6460 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6461 if (FAILED(rc)) return rc;
6462
6463 i_setModified(IsModified_USB);
6464 mUSBControllers.backup();
6465
6466 ctrl->i_unshare();
6467
6468 mUSBControllers->remove(ctrl);
6469
6470 /* inform the direct session if any */
6471 alock.release();
6472 i_onUSBControllerChange();
6473
6474 return S_OK;
6475}
6476
6477HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6478 ULONG *aOriginX,
6479 ULONG *aOriginY,
6480 ULONG *aWidth,
6481 ULONG *aHeight,
6482 BOOL *aEnabled)
6483{
6484 uint32_t u32OriginX= 0;
6485 uint32_t u32OriginY= 0;
6486 uint32_t u32Width = 0;
6487 uint32_t u32Height = 0;
6488 uint16_t u16Flags = 0;
6489
6490 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6491 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6492 if (RT_FAILURE(vrc))
6493 {
6494#ifdef RT_OS_WINDOWS
6495 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6496 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6497 * So just assign fEnable to TRUE again.
6498 * The right fix would be to change GUI API wrappers to make sure that parameters
6499 * are changed only if API succeeds.
6500 */
6501 *aEnabled = TRUE;
6502#endif
6503 return setError(VBOX_E_IPRT_ERROR,
6504 tr("Saved guest size is not available (%Rrc)"),
6505 vrc);
6506 }
6507
6508 *aOriginX = u32OriginX;
6509 *aOriginY = u32OriginY;
6510 *aWidth = u32Width;
6511 *aHeight = u32Height;
6512 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6513
6514 return S_OK;
6515}
6516
6517HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6518 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6519{
6520 if (aScreenId != 0)
6521 return E_NOTIMPL;
6522
6523 if ( aBitmapFormat != BitmapFormat_BGR0
6524 && aBitmapFormat != BitmapFormat_BGRA
6525 && aBitmapFormat != BitmapFormat_RGBA
6526 && aBitmapFormat != BitmapFormat_PNG)
6527 return setError(E_NOTIMPL,
6528 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6529
6530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 uint8_t *pu8Data = NULL;
6533 uint32_t cbData = 0;
6534 uint32_t u32Width = 0;
6535 uint32_t u32Height = 0;
6536
6537 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6538
6539 if (RT_FAILURE(vrc))
6540 return setError(VBOX_E_IPRT_ERROR,
6541 tr("Saved thumbnail data is not available (%Rrc)"),
6542 vrc);
6543
6544 HRESULT hr = S_OK;
6545
6546 *aWidth = u32Width;
6547 *aHeight = u32Height;
6548
6549 if (cbData > 0)
6550 {
6551 /* Convert pixels to the format expected by the API caller. */
6552 if (aBitmapFormat == BitmapFormat_BGR0)
6553 {
6554 /* [0] B, [1] G, [2] R, [3] 0. */
6555 aData.resize(cbData);
6556 memcpy(&aData.front(), pu8Data, cbData);
6557 }
6558 else if (aBitmapFormat == BitmapFormat_BGRA)
6559 {
6560 /* [0] B, [1] G, [2] R, [3] A. */
6561 aData.resize(cbData);
6562 for (uint32_t i = 0; i < cbData; i += 4)
6563 {
6564 aData[i] = pu8Data[i];
6565 aData[i + 1] = pu8Data[i + 1];
6566 aData[i + 2] = pu8Data[i + 2];
6567 aData[i + 3] = 0xff;
6568 }
6569 }
6570 else if (aBitmapFormat == BitmapFormat_RGBA)
6571 {
6572 /* [0] R, [1] G, [2] B, [3] A. */
6573 aData.resize(cbData);
6574 for (uint32_t i = 0; i < cbData; i += 4)
6575 {
6576 aData[i] = pu8Data[i + 2];
6577 aData[i + 1] = pu8Data[i + 1];
6578 aData[i + 2] = pu8Data[i];
6579 aData[i + 3] = 0xff;
6580 }
6581 }
6582 else if (aBitmapFormat == BitmapFormat_PNG)
6583 {
6584 uint8_t *pu8PNG = NULL;
6585 uint32_t cbPNG = 0;
6586 uint32_t cxPNG = 0;
6587 uint32_t cyPNG = 0;
6588
6589 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6590
6591 if (RT_SUCCESS(vrc))
6592 {
6593 aData.resize(cbPNG);
6594 if (cbPNG)
6595 memcpy(&aData.front(), pu8PNG, cbPNG);
6596 }
6597 else
6598 hr = setError(VBOX_E_IPRT_ERROR,
6599 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6600 vrc);
6601
6602 RTMemFree(pu8PNG);
6603 }
6604 }
6605
6606 freeSavedDisplayScreenshot(pu8Data);
6607
6608 return hr;
6609}
6610
6611HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6612 ULONG *aWidth,
6613 ULONG *aHeight,
6614 std::vector<BitmapFormat_T> &aBitmapFormats)
6615{
6616 if (aScreenId != 0)
6617 return E_NOTIMPL;
6618
6619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6620
6621 uint8_t *pu8Data = NULL;
6622 uint32_t cbData = 0;
6623 uint32_t u32Width = 0;
6624 uint32_t u32Height = 0;
6625
6626 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6627
6628 if (RT_FAILURE(vrc))
6629 return setError(VBOX_E_IPRT_ERROR,
6630 tr("Saved screenshot data is not available (%Rrc)"),
6631 vrc);
6632
6633 *aWidth = u32Width;
6634 *aHeight = u32Height;
6635 aBitmapFormats.resize(1);
6636 aBitmapFormats[0] = BitmapFormat_PNG;
6637
6638 freeSavedDisplayScreenshot(pu8Data);
6639
6640 return S_OK;
6641}
6642
6643HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6644 BitmapFormat_T aBitmapFormat,
6645 ULONG *aWidth,
6646 ULONG *aHeight,
6647 std::vector<BYTE> &aData)
6648{
6649 if (aScreenId != 0)
6650 return E_NOTIMPL;
6651
6652 if (aBitmapFormat != BitmapFormat_PNG)
6653 return E_NOTIMPL;
6654
6655 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 uint8_t *pu8Data = NULL;
6658 uint32_t cbData = 0;
6659 uint32_t u32Width = 0;
6660 uint32_t u32Height = 0;
6661
6662 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6663
6664 if (RT_FAILURE(vrc))
6665 return setError(VBOX_E_IPRT_ERROR,
6666 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6667 vrc);
6668
6669 *aWidth = u32Width;
6670 *aHeight = u32Height;
6671
6672 aData.resize(cbData);
6673 if (cbData)
6674 memcpy(&aData.front(), pu8Data, cbData);
6675
6676 freeSavedDisplayScreenshot(pu8Data);
6677
6678 return S_OK;
6679}
6680
6681HRESULT Machine::hotPlugCPU(ULONG aCpu)
6682{
6683 HRESULT rc = S_OK;
6684 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6685
6686 if (!mHWData->mCPUHotPlugEnabled)
6687 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6688
6689 if (aCpu >= mHWData->mCPUCount)
6690 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6691
6692 if (mHWData->mCPUAttached[aCpu])
6693 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6694
6695 alock.release();
6696 rc = i_onCPUChange(aCpu, false);
6697 alock.acquire();
6698 if (FAILED(rc)) return rc;
6699
6700 i_setModified(IsModified_MachineData);
6701 mHWData.backup();
6702 mHWData->mCPUAttached[aCpu] = true;
6703
6704 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6705 if (Global::IsOnline(mData->mMachineState))
6706 i_saveSettings(NULL);
6707
6708 return S_OK;
6709}
6710
6711HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6712{
6713 HRESULT rc = S_OK;
6714
6715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 if (!mHWData->mCPUHotPlugEnabled)
6718 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6719
6720 if (aCpu >= SchemaDefs::MaxCPUCount)
6721 return setError(E_INVALIDARG,
6722 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6723 SchemaDefs::MaxCPUCount);
6724
6725 if (!mHWData->mCPUAttached[aCpu])
6726 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6727
6728 /* CPU 0 can't be detached */
6729 if (aCpu == 0)
6730 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6731
6732 alock.release();
6733 rc = i_onCPUChange(aCpu, true);
6734 alock.acquire();
6735 if (FAILED(rc)) return rc;
6736
6737 i_setModified(IsModified_MachineData);
6738 mHWData.backup();
6739 mHWData->mCPUAttached[aCpu] = false;
6740
6741 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6742 if (Global::IsOnline(mData->mMachineState))
6743 i_saveSettings(NULL);
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6749{
6750 *aAttached = false;
6751
6752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6753
6754 /* If hotplug is enabled the CPU is always enabled. */
6755 if (!mHWData->mCPUHotPlugEnabled)
6756 {
6757 if (aCpu < mHWData->mCPUCount)
6758 *aAttached = true;
6759 }
6760 else
6761 {
6762 if (aCpu < SchemaDefs::MaxCPUCount)
6763 *aAttached = mHWData->mCPUAttached[aCpu];
6764 }
6765
6766 return S_OK;
6767}
6768
6769HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6770{
6771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6772
6773 Utf8Str log = i_getLogFilename(aIdx);
6774 if (!RTFileExists(log.c_str()))
6775 log.setNull();
6776 aFilename = log;
6777
6778 return S_OK;
6779}
6780
6781HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6782{
6783 if (aSize < 0)
6784 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6785
6786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6787
6788 HRESULT rc = S_OK;
6789 Utf8Str log = i_getLogFilename(aIdx);
6790
6791 /* do not unnecessarily hold the lock while doing something which does
6792 * not need the lock and potentially takes a long time. */
6793 alock.release();
6794
6795 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6796 * keeps the SOAP reply size under 1M for the webservice (we're using
6797 * base64 encoded strings for binary data for years now, avoiding the
6798 * expansion of each byte array element to approx. 25 bytes of XML. */
6799 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6800 aData.resize(cbData);
6801
6802 RTFILE LogFile;
6803 int vrc = RTFileOpen(&LogFile, log.c_str(),
6804 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6805 if (RT_SUCCESS(vrc))
6806 {
6807 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6808 if (RT_SUCCESS(vrc))
6809 aData.resize(cbData);
6810 else
6811 rc = setError(VBOX_E_IPRT_ERROR,
6812 tr("Could not read log file '%s' (%Rrc)"),
6813 log.c_str(), vrc);
6814 RTFileClose(LogFile);
6815 }
6816 else
6817 rc = setError(VBOX_E_IPRT_ERROR,
6818 tr("Could not open log file '%s' (%Rrc)"),
6819 log.c_str(), vrc);
6820
6821 if (FAILED(rc))
6822 aData.resize(0);
6823
6824 return rc;
6825}
6826
6827
6828/**
6829 * Currently this method doesn't attach device to the running VM,
6830 * just makes sure it's plugged on next VM start.
6831 */
6832HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6833{
6834 // lock scope
6835 {
6836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6837
6838 HRESULT rc = i_checkStateDependency(MutableStateDep);
6839 if (FAILED(rc)) return rc;
6840
6841 ChipsetType_T aChipset = ChipsetType_PIIX3;
6842 COMGETTER(ChipsetType)(&aChipset);
6843
6844 if (aChipset != ChipsetType_ICH9)
6845 {
6846 return setError(E_INVALIDARG,
6847 tr("Host PCI attachment only supported with ICH9 chipset"));
6848 }
6849
6850 // check if device with this host PCI address already attached
6851 for (HWData::PCIDeviceAssignmentList::const_iterator
6852 it = mHWData->mPCIDeviceAssignments.begin();
6853 it != mHWData->mPCIDeviceAssignments.end();
6854 ++it)
6855 {
6856 LONG iHostAddress = -1;
6857 ComPtr<PCIDeviceAttachment> pAttach;
6858 pAttach = *it;
6859 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6860 if (iHostAddress == aHostAddress)
6861 return setError(E_INVALIDARG,
6862 tr("Device with host PCI address already attached to this VM"));
6863 }
6864
6865 ComObjPtr<PCIDeviceAttachment> pda;
6866 char name[32];
6867
6868 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6869 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6870 pda.createObject();
6871 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6872 i_setModified(IsModified_MachineData);
6873 mHWData.backup();
6874 mHWData->mPCIDeviceAssignments.push_back(pda);
6875 }
6876
6877 return S_OK;
6878}
6879
6880/**
6881 * Currently this method doesn't detach device from the running VM,
6882 * just makes sure it's not plugged on next VM start.
6883 */
6884HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6885{
6886 ComObjPtr<PCIDeviceAttachment> pAttach;
6887 bool fRemoved = false;
6888 HRESULT rc;
6889
6890 // lock scope
6891 {
6892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6893
6894 rc = i_checkStateDependency(MutableStateDep);
6895 if (FAILED(rc)) return rc;
6896
6897 for (HWData::PCIDeviceAssignmentList::const_iterator
6898 it = mHWData->mPCIDeviceAssignments.begin();
6899 it != mHWData->mPCIDeviceAssignments.end();
6900 ++it)
6901 {
6902 LONG iHostAddress = -1;
6903 pAttach = *it;
6904 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6905 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6906 {
6907 i_setModified(IsModified_MachineData);
6908 mHWData.backup();
6909 mHWData->mPCIDeviceAssignments.remove(pAttach);
6910 fRemoved = true;
6911 break;
6912 }
6913 }
6914 }
6915
6916
6917 /* Fire event outside of the lock */
6918 if (fRemoved)
6919 {
6920 Assert(!pAttach.isNull());
6921 ComPtr<IEventSource> es;
6922 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6923 Assert(SUCCEEDED(rc));
6924 Bstr mid;
6925 rc = this->COMGETTER(Id)(mid.asOutParam());
6926 Assert(SUCCEEDED(rc));
6927 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6928 }
6929
6930 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6931 tr("No host PCI device %08x attached"),
6932 aHostAddress
6933 );
6934}
6935
6936HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6937{
6938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6939
6940 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6941 size_t i = 0;
6942 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6943 it = mHWData->mPCIDeviceAssignments.begin();
6944 it != mHWData->mPCIDeviceAssignments.end();
6945 ++it, ++i)
6946 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6947
6948 return S_OK;
6949}
6950
6951HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6952{
6953 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6954
6955 return S_OK;
6956}
6957
6958HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6959{
6960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6961
6962 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6963
6964 return S_OK;
6965}
6966
6967HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6968{
6969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6970 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6971 if (SUCCEEDED(hrc))
6972 {
6973 hrc = mHWData.backupEx();
6974 if (SUCCEEDED(hrc))
6975 {
6976 i_setModified(IsModified_MachineData);
6977 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6978 }
6979 }
6980 return hrc;
6981}
6982
6983HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6984{
6985 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6987 return S_OK;
6988}
6989
6990HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6991{
6992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6993 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6994 if (SUCCEEDED(hrc))
6995 {
6996 hrc = mHWData.backupEx();
6997 if (SUCCEEDED(hrc))
6998 {
6999 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7000 if (SUCCEEDED(hrc))
7001 i_setModified(IsModified_MachineData);
7002 }
7003 }
7004 return hrc;
7005}
7006
7007HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7012
7013 return S_OK;
7014}
7015
7016HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 i_setModified(IsModified_MachineData);
7026 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7027 }
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7033{
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035
7036 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7037
7038 return S_OK;
7039}
7040
7041HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7042{
7043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7044
7045 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7046 if ( SUCCEEDED(hrc)
7047 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7048 {
7049 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7050 int vrc;
7051
7052 if (aAutostartEnabled)
7053 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7054 else
7055 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7056
7057 if (RT_SUCCESS(vrc))
7058 {
7059 hrc = mHWData.backupEx();
7060 if (SUCCEEDED(hrc))
7061 {
7062 i_setModified(IsModified_MachineData);
7063 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7064 }
7065 }
7066 else if (vrc == VERR_NOT_SUPPORTED)
7067 hrc = setError(VBOX_E_NOT_SUPPORTED,
7068 tr("The VM autostart feature is not supported on this platform"));
7069 else if (vrc == VERR_PATH_NOT_FOUND)
7070 hrc = setError(E_FAIL,
7071 tr("The path to the autostart database is not set"));
7072 else
7073 hrc = setError(E_UNEXPECTED,
7074 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7075 aAutostartEnabled ? "Adding" : "Removing",
7076 mUserData->s.strName.c_str(), vrc);
7077 }
7078 return hrc;
7079}
7080
7081HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7082{
7083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7086
7087 return S_OK;
7088}
7089
7090HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7091{
7092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7093 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7094 if (SUCCEEDED(hrc))
7095 {
7096 hrc = mHWData.backupEx();
7097 if (SUCCEEDED(hrc))
7098 {
7099 i_setModified(IsModified_MachineData);
7100 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7101 }
7102 }
7103 return hrc;
7104}
7105
7106HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7107{
7108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7109
7110 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7111
7112 return S_OK;
7113}
7114
7115HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7116{
7117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7118 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7119 if ( SUCCEEDED(hrc)
7120 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7121 {
7122 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7123 int vrc;
7124
7125 if (aAutostopType != AutostopType_Disabled)
7126 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7127 else
7128 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7129
7130 if (RT_SUCCESS(vrc))
7131 {
7132 hrc = mHWData.backupEx();
7133 if (SUCCEEDED(hrc))
7134 {
7135 i_setModified(IsModified_MachineData);
7136 mHWData->mAutostart.enmAutostopType = aAutostopType;
7137 }
7138 }
7139 else if (vrc == VERR_NOT_SUPPORTED)
7140 hrc = setError(VBOX_E_NOT_SUPPORTED,
7141 tr("The VM autostop feature is not supported on this platform"));
7142 else if (vrc == VERR_PATH_NOT_FOUND)
7143 hrc = setError(E_FAIL,
7144 tr("The path to the autostart database is not set"));
7145 else
7146 hrc = setError(E_UNEXPECTED,
7147 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7148 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7149 mUserData->s.strName.c_str(), vrc);
7150 }
7151 return hrc;
7152}
7153
7154HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7155{
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 aDefaultFrontend = mHWData->mDefaultFrontend;
7159
7160 return S_OK;
7161}
7162
7163HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7164{
7165 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7166 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7167 if (SUCCEEDED(hrc))
7168 {
7169 hrc = mHWData.backupEx();
7170 if (SUCCEEDED(hrc))
7171 {
7172 i_setModified(IsModified_MachineData);
7173 mHWData->mDefaultFrontend = aDefaultFrontend;
7174 }
7175 }
7176 return hrc;
7177}
7178
7179HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7180{
7181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7182 size_t cbIcon = mUserData->s.ovIcon.size();
7183 aIcon.resize(cbIcon);
7184 if (cbIcon)
7185 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7186 return S_OK;
7187}
7188
7189HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7190{
7191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7192 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7193 if (SUCCEEDED(hrc))
7194 {
7195 i_setModified(IsModified_MachineData);
7196 mUserData.backup();
7197 size_t cbIcon = aIcon.size();
7198 mUserData->s.ovIcon.resize(cbIcon);
7199 if (cbIcon)
7200 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7201 }
7202 return hrc;
7203}
7204
7205HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7206{
7207#ifdef VBOX_WITH_USB
7208 *aUSBProxyAvailable = true;
7209#else
7210 *aUSBProxyAvailable = false;
7211#endif
7212 return S_OK;
7213}
7214
7215HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7216{
7217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7218
7219 aVMProcessPriority = mUserData->s.strVMPriority;
7220
7221 return S_OK;
7222}
7223
7224HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7225{
7226 RT_NOREF(aVMProcessPriority);
7227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7228 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7229 if (SUCCEEDED(hrc))
7230 {
7231 /** @todo r=klaus: currently this is marked as not implemented, as
7232 * the code for setting the priority of the process is not there
7233 * (neither when starting the VM nor at runtime). */
7234 ReturnComNotImplemented();
7235#if 0
7236 hrc = mUserData.backupEx();
7237 if (SUCCEEDED(hrc))
7238 {
7239 i_setModified(IsModified_MachineData);
7240 mUserData->s.strVMPriority = aVMProcessPriority;
7241 }
7242#endif
7243 }
7244 return hrc;
7245}
7246
7247HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7248 ComPtr<IProgress> &aProgress)
7249{
7250 ComObjPtr<Progress> pP;
7251 Progress *ppP = pP;
7252 IProgress *iP = static_cast<IProgress *>(ppP);
7253 IProgress **pProgress = &iP;
7254
7255 IMachine *pTarget = aTarget;
7256
7257 /* Convert the options. */
7258 RTCList<CloneOptions_T> optList;
7259 if (aOptions.size())
7260 for (size_t i = 0; i < aOptions.size(); ++i)
7261 optList.append(aOptions[i]);
7262
7263 if (optList.contains(CloneOptions_Link))
7264 {
7265 if (!i_isSnapshotMachine())
7266 return setError(E_INVALIDARG,
7267 tr("Linked clone can only be created from a snapshot"));
7268 if (aMode != CloneMode_MachineState)
7269 return setError(E_INVALIDARG,
7270 tr("Linked clone can only be created for a single machine state"));
7271 }
7272 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7273
7274 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7275
7276 HRESULT rc = pWorker->start(pProgress);
7277
7278 pP = static_cast<Progress *>(*pProgress);
7279 pP.queryInterfaceTo(aProgress.asOutParam());
7280
7281 return rc;
7282
7283}
7284
7285HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7286 const com::Utf8Str &aType,
7287 ComPtr<IProgress> &aProgress)
7288{
7289 LogFlowThisFuncEnter();
7290
7291 ComObjPtr<Progress> progress;
7292
7293 progress.createObject();
7294
7295 HRESULT rc = S_OK;
7296 Utf8Str targetPath = aTargetPath;
7297 Utf8Str type = aType;
7298
7299 /* Initialize our worker task */
7300 MachineMoveVM* task = NULL;
7301 try
7302 {
7303 task = new MachineMoveVM(this, targetPath, type, progress);
7304 }
7305 catch(...)
7306 {
7307 delete task;
7308 return rc;
7309 }
7310
7311 /*
7312 * task pointer will be owned by the ThreadTask class.
7313 * There is no need to call operator "delete" in the end.
7314 */
7315 rc = task->init();
7316 if (SUCCEEDED(rc))
7317 {
7318 rc = task->createThread();
7319 if (FAILED(rc))
7320 {
7321 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7322 }
7323
7324 /* Return progress to the caller */
7325 progress.queryInterfaceTo(aProgress.asOutParam());
7326 }
7327
7328 LogFlowThisFuncLeave();
7329 return rc;
7330
7331}
7332
7333HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7334{
7335 NOREF(aProgress);
7336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7337
7338 // This check should always fail.
7339 HRESULT rc = i_checkStateDependency(MutableStateDep);
7340 if (FAILED(rc)) return rc;
7341
7342 AssertFailedReturn(E_NOTIMPL);
7343}
7344
7345HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7346{
7347 NOREF(aSavedStateFile);
7348 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7349
7350 // This check should always fail.
7351 HRESULT rc = i_checkStateDependency(MutableStateDep);
7352 if (FAILED(rc)) return rc;
7353
7354 AssertFailedReturn(E_NOTIMPL);
7355}
7356
7357HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7358{
7359 NOREF(aFRemoveFile);
7360 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7361
7362 // This check should always fail.
7363 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7364 if (FAILED(rc)) return rc;
7365
7366 AssertFailedReturn(E_NOTIMPL);
7367}
7368
7369// public methods for internal purposes
7370/////////////////////////////////////////////////////////////////////////////
7371
7372/**
7373 * Adds the given IsModified_* flag to the dirty flags of the machine.
7374 * This must be called either during i_loadSettings or under the machine write lock.
7375 * @param fl Flag
7376 * @param fAllowStateModification If state modifications are allowed.
7377 */
7378void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7379{
7380 mData->flModifications |= fl;
7381 if (fAllowStateModification && i_isStateModificationAllowed())
7382 mData->mCurrentStateModified = true;
7383}
7384
7385/**
7386 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7387 * care of the write locking.
7388 *
7389 * @param fModification The flag to add.
7390 * @param fAllowStateModification If state modifications are allowed.
7391 */
7392void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7393{
7394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7395 i_setModified(fModification, fAllowStateModification);
7396}
7397
7398/**
7399 * Saves the registry entry of this machine to the given configuration node.
7400 *
7401 * @param data Machine registry data.
7402 *
7403 * @note locks this object for reading.
7404 */
7405HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7406{
7407 AutoLimitedCaller autoCaller(this);
7408 AssertComRCReturnRC(autoCaller.rc());
7409
7410 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7411
7412 data.uuid = mData->mUuid;
7413 data.strSettingsFile = mData->m_strConfigFile;
7414
7415 return S_OK;
7416}
7417
7418/**
7419 * Calculates the absolute path of the given path taking the directory of the
7420 * machine settings file as the current directory.
7421 *
7422 * @param strPath Path to calculate the absolute path for.
7423 * @param aResult Where to put the result (used only on success, can be the
7424 * same Utf8Str instance as passed in @a aPath).
7425 * @return IPRT result.
7426 *
7427 * @note Locks this object for reading.
7428 */
7429int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7430{
7431 AutoCaller autoCaller(this);
7432 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7433
7434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7435
7436 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7437
7438 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7439
7440 strSettingsDir.stripFilename();
7441 char folder[RTPATH_MAX];
7442 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7443 if (RT_SUCCESS(vrc))
7444 aResult = folder;
7445
7446 return vrc;
7447}
7448
7449/**
7450 * Copies strSource to strTarget, making it relative to the machine folder
7451 * if it is a subdirectory thereof, or simply copying it otherwise.
7452 *
7453 * @param strSource Path to evaluate and copy.
7454 * @param strTarget Buffer to receive target path.
7455 *
7456 * @note Locks this object for reading.
7457 */
7458void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7459 Utf8Str &strTarget)
7460{
7461 AutoCaller autoCaller(this);
7462 AssertComRCReturn(autoCaller.rc(), (void)0);
7463
7464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7465
7466 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7467 // use strTarget as a temporary buffer to hold the machine settings dir
7468 strTarget = mData->m_strConfigFileFull;
7469 strTarget.stripFilename();
7470 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7471 {
7472 // is relative: then append what's left
7473 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7474 // for empty paths (only possible for subdirs) use "." to avoid
7475 // triggering default settings for not present config attributes.
7476 if (strTarget.isEmpty())
7477 strTarget = ".";
7478 }
7479 else
7480 // is not relative: then overwrite
7481 strTarget = strSource;
7482}
7483
7484/**
7485 * Returns the full path to the machine's log folder in the
7486 * \a aLogFolder argument.
7487 */
7488void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7489{
7490 AutoCaller autoCaller(this);
7491 AssertComRCReturnVoid(autoCaller.rc());
7492
7493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7494
7495 char szTmp[RTPATH_MAX];
7496 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7497 if (RT_SUCCESS(vrc))
7498 {
7499 if (szTmp[0] && !mUserData.isNull())
7500 {
7501 char szTmp2[RTPATH_MAX];
7502 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7503 if (RT_SUCCESS(vrc))
7504 aLogFolder = Utf8StrFmt("%s%c%s",
7505 szTmp2,
7506 RTPATH_DELIMITER,
7507 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7508 }
7509 else
7510 vrc = VERR_PATH_IS_RELATIVE;
7511 }
7512
7513 if (RT_FAILURE(vrc))
7514 {
7515 // fallback if VBOX_USER_LOGHOME is not set or invalid
7516 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7517 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7518 aLogFolder.append(RTPATH_DELIMITER);
7519 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7520 }
7521}
7522
7523/**
7524 * Returns the full path to the machine's log file for an given index.
7525 */
7526Utf8Str Machine::i_getLogFilename(ULONG idx)
7527{
7528 Utf8Str logFolder;
7529 getLogFolder(logFolder);
7530 Assert(logFolder.length());
7531
7532 Utf8Str log;
7533 if (idx == 0)
7534 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7535#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7536 else if (idx == 1)
7537 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7538 else
7539 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7540#else
7541 else
7542 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7543#endif
7544 return log;
7545}
7546
7547/**
7548 * Returns the full path to the machine's hardened log file.
7549 */
7550Utf8Str Machine::i_getHardeningLogFilename(void)
7551{
7552 Utf8Str strFilename;
7553 getLogFolder(strFilename);
7554 Assert(strFilename.length());
7555 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7556 return strFilename;
7557}
7558
7559
7560/**
7561 * Composes a unique saved state filename based on the current system time. The filename is
7562 * granular to the second so this will work so long as no more than one snapshot is taken on
7563 * a machine per second.
7564 *
7565 * Before version 4.1, we used this formula for saved state files:
7566 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7567 * which no longer works because saved state files can now be shared between the saved state of the
7568 * "saved" machine and an online snapshot, and the following would cause problems:
7569 * 1) save machine
7570 * 2) create online snapshot from that machine state --> reusing saved state file
7571 * 3) save machine again --> filename would be reused, breaking the online snapshot
7572 *
7573 * So instead we now use a timestamp.
7574 *
7575 * @param strStateFilePath
7576 */
7577
7578void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7579{
7580 AutoCaller autoCaller(this);
7581 AssertComRCReturnVoid(autoCaller.rc());
7582
7583 {
7584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7585 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7586 }
7587
7588 RTTIMESPEC ts;
7589 RTTimeNow(&ts);
7590 RTTIME time;
7591 RTTimeExplode(&time, &ts);
7592
7593 strStateFilePath += RTPATH_DELIMITER;
7594 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7595 time.i32Year, time.u8Month, time.u8MonthDay,
7596 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7597}
7598
7599/**
7600 * Returns the full path to the default video capture file.
7601 */
7602void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7603{
7604 AutoCaller autoCaller(this);
7605 AssertComRCReturnVoid(autoCaller.rc());
7606
7607 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7608
7609 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7610 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7611 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7612}
7613
7614/**
7615 * Returns whether at least one USB controller is present for the VM.
7616 */
7617bool Machine::i_isUSBControllerPresent()
7618{
7619 AutoCaller autoCaller(this);
7620 AssertComRCReturn(autoCaller.rc(), false);
7621
7622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7623
7624 return (mUSBControllers->size() > 0);
7625}
7626
7627/**
7628 * @note Locks this object for writing, calls the client process
7629 * (inside the lock).
7630 */
7631HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7632 const Utf8Str &strFrontend,
7633 const Utf8Str &strEnvironment,
7634 ProgressProxy *aProgress)
7635{
7636 LogFlowThisFuncEnter();
7637
7638 AssertReturn(aControl, E_FAIL);
7639 AssertReturn(aProgress, E_FAIL);
7640 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7641
7642 AutoCaller autoCaller(this);
7643 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7644
7645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7646
7647 if (!mData->mRegistered)
7648 return setError(E_UNEXPECTED,
7649 tr("The machine '%s' is not registered"),
7650 mUserData->s.strName.c_str());
7651
7652 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7653
7654 /* The process started when launching a VM with separate UI/VM processes is always
7655 * the UI process, i.e. needs special handling as it won't claim the session. */
7656 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7657
7658 if (fSeparate)
7659 {
7660 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7661 return setError(VBOX_E_INVALID_OBJECT_STATE,
7662 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7663 mUserData->s.strName.c_str());
7664 }
7665 else
7666 {
7667 if ( mData->mSession.mState == SessionState_Locked
7668 || mData->mSession.mState == SessionState_Spawning
7669 || mData->mSession.mState == SessionState_Unlocking)
7670 return setError(VBOX_E_INVALID_OBJECT_STATE,
7671 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7672 mUserData->s.strName.c_str());
7673
7674 /* may not be busy */
7675 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7676 }
7677
7678 /* get the path to the executable */
7679 char szPath[RTPATH_MAX];
7680 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7681 size_t cchBufLeft = strlen(szPath);
7682 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7683 szPath[cchBufLeft] = 0;
7684 char *pszNamePart = szPath + cchBufLeft;
7685 cchBufLeft = sizeof(szPath) - cchBufLeft;
7686
7687 int vrc = VINF_SUCCESS;
7688 RTPROCESS pid = NIL_RTPROCESS;
7689
7690 RTENV env = RTENV_DEFAULT;
7691
7692 if (!strEnvironment.isEmpty())
7693 {
7694 char *newEnvStr = NULL;
7695
7696 do
7697 {
7698 /* clone the current environment */
7699 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7700 AssertRCBreakStmt(vrc2, vrc = vrc2);
7701
7702 newEnvStr = RTStrDup(strEnvironment.c_str());
7703 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7704
7705 /* put new variables to the environment
7706 * (ignore empty variable names here since RTEnv API
7707 * intentionally doesn't do that) */
7708 char *var = newEnvStr;
7709 for (char *p = newEnvStr; *p; ++p)
7710 {
7711 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7712 {
7713 *p = '\0';
7714 if (*var)
7715 {
7716 char *val = strchr(var, '=');
7717 if (val)
7718 {
7719 *val++ = '\0';
7720 vrc2 = RTEnvSetEx(env, var, val);
7721 }
7722 else
7723 vrc2 = RTEnvUnsetEx(env, var);
7724 if (RT_FAILURE(vrc2))
7725 break;
7726 }
7727 var = p + 1;
7728 }
7729 }
7730 if (RT_SUCCESS(vrc2) && *var)
7731 vrc2 = RTEnvPutEx(env, var);
7732
7733 AssertRCBreakStmt(vrc2, vrc = vrc2);
7734 }
7735 while (0);
7736
7737 if (newEnvStr != NULL)
7738 RTStrFree(newEnvStr);
7739 }
7740
7741 /* Hardening logging */
7742#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7743 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7744 {
7745 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7746 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7747 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7748 {
7749 Utf8Str strStartupLogDir = strHardeningLogFile;
7750 strStartupLogDir.stripFilename();
7751 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7752 file without stripping the file. */
7753 }
7754 strSupHardeningLogArg.append(strHardeningLogFile);
7755
7756 /* Remove legacy log filename to avoid confusion. */
7757 Utf8Str strOldStartupLogFile;
7758 getLogFolder(strOldStartupLogFile);
7759 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7760 RTFileDelete(strOldStartupLogFile.c_str());
7761 }
7762 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7763#else
7764 const char *pszSupHardeningLogArg = NULL;
7765#endif
7766
7767 Utf8Str strCanonicalName;
7768
7769#ifdef VBOX_WITH_QTGUI
7770 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7771 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7772 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7773 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7774 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7775 {
7776 strCanonicalName = "GUI/Qt";
7777# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7778 /* Modify the base path so that we don't need to use ".." below. */
7779 RTPathStripTrailingSlash(szPath);
7780 RTPathStripFilename(szPath);
7781 cchBufLeft = strlen(szPath);
7782 pszNamePart = szPath + cchBufLeft;
7783 cchBufLeft = sizeof(szPath) - cchBufLeft;
7784
7785# define OSX_APP_NAME "VirtualBoxVM"
7786# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7787
7788 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7789 if ( strAppOverride.contains(".")
7790 || strAppOverride.contains("/")
7791 || strAppOverride.contains("\\")
7792 || strAppOverride.contains(":"))
7793 strAppOverride.setNull();
7794 Utf8Str strAppPath;
7795 if (!strAppOverride.isEmpty())
7796 {
7797 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7798 Utf8Str strFullPath(szPath);
7799 strFullPath.append(strAppPath);
7800 /* there is a race, but people using this deserve the failure */
7801 if (!RTFileExists(strFullPath.c_str()))
7802 strAppOverride.setNull();
7803 }
7804 if (strAppOverride.isEmpty())
7805 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7806 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7807 strcpy(pszNamePart, strAppPath.c_str());
7808# else
7809 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7810 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7811 strcpy(pszNamePart, s_szVirtualBox_exe);
7812# endif
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 "--no-startvm-errormsgbox",
7821 NULL, /* For "--separate". */
7822 NULL, /* For "--sup-startup-log". */
7823 NULL
7824 };
7825 unsigned iArg = 6;
7826 if (fSeparate)
7827 apszArgs[iArg++] = "--separate";
7828 apszArgs[iArg++] = pszSupHardeningLogArg;
7829
7830 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7831 }
7832#else /* !VBOX_WITH_QTGUI */
7833 if (0)
7834 ;
7835#endif /* VBOX_WITH_QTGUI */
7836
7837 else
7838
7839#ifdef VBOX_WITH_VBOXSDL
7840 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7841 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7842 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7843 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7844 {
7845 strCanonicalName = "GUI/SDL";
7846 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7847 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7848 strcpy(pszNamePart, s_szVBoxSDL_exe);
7849
7850 Utf8Str idStr = mData->mUuid.toString();
7851 const char *apszArgs[] =
7852 {
7853 szPath,
7854 "--comment", mUserData->s.strName.c_str(),
7855 "--startvm", idStr.c_str(),
7856 NULL, /* For "--separate". */
7857 NULL, /* For "--sup-startup-log". */
7858 NULL
7859 };
7860 unsigned iArg = 5;
7861 if (fSeparate)
7862 apszArgs[iArg++] = "--separate";
7863 apszArgs[iArg++] = pszSupHardeningLogArg;
7864
7865 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7866 }
7867#else /* !VBOX_WITH_VBOXSDL */
7868 if (0)
7869 ;
7870#endif /* !VBOX_WITH_VBOXSDL */
7871
7872 else
7873
7874#ifdef VBOX_WITH_HEADLESS
7875 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7876 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7877 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7878 )
7879 {
7880 strCanonicalName = "headless";
7881 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7882 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7883 * and a VM works even if the server has not been installed.
7884 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7885 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7886 * differently in 4.0 and 3.x.
7887 */
7888 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7889 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7890 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7891
7892 Utf8Str idStr = mData->mUuid.toString();
7893 const char *apszArgs[] =
7894 {
7895 szPath,
7896 "--comment", mUserData->s.strName.c_str(),
7897 "--startvm", idStr.c_str(),
7898 "--vrde", "config",
7899 NULL, /* For "--capture". */
7900 NULL, /* For "--sup-startup-log". */
7901 NULL
7902 };
7903 unsigned iArg = 7;
7904 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7905 apszArgs[iArg++] = "--capture";
7906 apszArgs[iArg++] = pszSupHardeningLogArg;
7907
7908# ifdef RT_OS_WINDOWS
7909 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7910# else
7911 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7912# endif
7913 }
7914#else /* !VBOX_WITH_HEADLESS */
7915 if (0)
7916 ;
7917#endif /* !VBOX_WITH_HEADLESS */
7918 else
7919 {
7920 RTEnvDestroy(env);
7921 return setError(E_INVALIDARG,
7922 tr("Invalid frontend name: '%s'"),
7923 strFrontend.c_str());
7924 }
7925
7926 RTEnvDestroy(env);
7927
7928 if (RT_FAILURE(vrc))
7929 return setError(VBOX_E_IPRT_ERROR,
7930 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7931 mUserData->s.strName.c_str(), vrc);
7932
7933 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7934
7935 if (!fSeparate)
7936 {
7937 /*
7938 * Note that we don't release the lock here before calling the client,
7939 * because it doesn't need to call us back if called with a NULL argument.
7940 * Releasing the lock here is dangerous because we didn't prepare the
7941 * launch data yet, but the client we've just started may happen to be
7942 * too fast and call LockMachine() that will fail (because of PID, etc.),
7943 * so that the Machine will never get out of the Spawning session state.
7944 */
7945
7946 /* inform the session that it will be a remote one */
7947 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7948#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7949 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7950#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7951 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7952#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7953 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7954
7955 if (FAILED(rc))
7956 {
7957 /* restore the session state */
7958 mData->mSession.mState = SessionState_Unlocked;
7959 alock.release();
7960 mParent->i_addProcessToReap(pid);
7961 /* The failure may occur w/o any error info (from RPC), so provide one */
7962 return setError(VBOX_E_VM_ERROR,
7963 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7964 }
7965
7966 /* attach launch data to the machine */
7967 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7968 mData->mSession.mRemoteControls.push_back(aControl);
7969 mData->mSession.mProgress = aProgress;
7970 mData->mSession.mPID = pid;
7971 mData->mSession.mState = SessionState_Spawning;
7972 Assert(strCanonicalName.isNotEmpty());
7973 mData->mSession.mName = strCanonicalName;
7974 }
7975 else
7976 {
7977 /* For separate UI process we declare the launch as completed instantly, as the
7978 * actual headless VM start may or may not come. No point in remembering anything
7979 * yet, as what matters for us is when the headless VM gets started. */
7980 aProgress->i_notifyComplete(S_OK);
7981 }
7982
7983 alock.release();
7984 mParent->i_addProcessToReap(pid);
7985
7986 LogFlowThisFuncLeave();
7987 return S_OK;
7988}
7989
7990/**
7991 * Returns @c true if the given session machine instance has an open direct
7992 * session (and optionally also for direct sessions which are closing) and
7993 * returns the session control machine instance if so.
7994 *
7995 * Note that when the method returns @c false, the arguments remain unchanged.
7996 *
7997 * @param aMachine Session machine object.
7998 * @param aControl Direct session control object (optional).
7999 * @param aRequireVM If true then only allow VM sessions.
8000 * @param aAllowClosing If true then additionally a session which is currently
8001 * being closed will also be allowed.
8002 *
8003 * @note locks this object for reading.
8004 */
8005bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8006 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8007 bool aRequireVM /*= false*/,
8008 bool aAllowClosing /*= false*/)
8009{
8010 AutoLimitedCaller autoCaller(this);
8011 AssertComRCReturn(autoCaller.rc(), false);
8012
8013 /* just return false for inaccessible machines */
8014 if (getObjectState().getState() != ObjectState::Ready)
8015 return false;
8016
8017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8018
8019 if ( ( mData->mSession.mState == SessionState_Locked
8020 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8021 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8022 )
8023 {
8024 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8025
8026 aMachine = mData->mSession.mMachine;
8027
8028 if (aControl != NULL)
8029 *aControl = mData->mSession.mDirectControl;
8030
8031 return true;
8032 }
8033
8034 return false;
8035}
8036
8037/**
8038 * Returns @c true if the given machine has an spawning direct session.
8039 *
8040 * @note locks this object for reading.
8041 */
8042bool Machine::i_isSessionSpawning()
8043{
8044 AutoLimitedCaller autoCaller(this);
8045 AssertComRCReturn(autoCaller.rc(), false);
8046
8047 /* just return false for inaccessible machines */
8048 if (getObjectState().getState() != ObjectState::Ready)
8049 return false;
8050
8051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8052
8053 if (mData->mSession.mState == SessionState_Spawning)
8054 return true;
8055
8056 return false;
8057}
8058
8059/**
8060 * Called from the client watcher thread to check for unexpected client process
8061 * death during Session_Spawning state (e.g. before it successfully opened a
8062 * direct session).
8063 *
8064 * On Win32 and on OS/2, this method is called only when we've got the
8065 * direct client's process termination notification, so it always returns @c
8066 * true.
8067 *
8068 * On other platforms, this method returns @c true if the client process is
8069 * terminated and @c false if it's still alive.
8070 *
8071 * @note Locks this object for writing.
8072 */
8073bool Machine::i_checkForSpawnFailure()
8074{
8075 AutoCaller autoCaller(this);
8076 if (!autoCaller.isOk())
8077 {
8078 /* nothing to do */
8079 LogFlowThisFunc(("Already uninitialized!\n"));
8080 return true;
8081 }
8082
8083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8084
8085 if (mData->mSession.mState != SessionState_Spawning)
8086 {
8087 /* nothing to do */
8088 LogFlowThisFunc(("Not spawning any more!\n"));
8089 return true;
8090 }
8091
8092 HRESULT rc = S_OK;
8093
8094 /* PID not yet initialized, skip check. */
8095 if (mData->mSession.mPID == NIL_RTPROCESS)
8096 return false;
8097
8098 RTPROCSTATUS status;
8099 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8100
8101 if (vrc != VERR_PROCESS_RUNNING)
8102 {
8103 Utf8Str strExtraInfo;
8104
8105#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8106 /* If the startup logfile exists and is of non-zero length, tell the
8107 user to look there for more details to encourage them to attach it
8108 when reporting startup issues. */
8109 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8110 uint64_t cbStartupLogFile = 0;
8111 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8112 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8113 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8114#endif
8115
8116 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8117 rc = setError(E_FAIL,
8118 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8119 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8120 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8121 rc = setError(E_FAIL,
8122 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8123 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8124 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8125 rc = setError(E_FAIL,
8126 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8127 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8128 else
8129 rc = setError(E_FAIL,
8130 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8131 i_getName().c_str(), vrc, strExtraInfo.c_str());
8132 }
8133
8134 if (FAILED(rc))
8135 {
8136 /* Close the remote session, remove the remote control from the list
8137 * and reset session state to Closed (@note keep the code in sync with
8138 * the relevant part in LockMachine()). */
8139
8140 Assert(mData->mSession.mRemoteControls.size() == 1);
8141 if (mData->mSession.mRemoteControls.size() == 1)
8142 {
8143 ErrorInfoKeeper eik;
8144 mData->mSession.mRemoteControls.front()->Uninitialize();
8145 }
8146
8147 mData->mSession.mRemoteControls.clear();
8148 mData->mSession.mState = SessionState_Unlocked;
8149
8150 /* finalize the progress after setting the state */
8151 if (!mData->mSession.mProgress.isNull())
8152 {
8153 mData->mSession.mProgress->notifyComplete(rc);
8154 mData->mSession.mProgress.setNull();
8155 }
8156
8157 mData->mSession.mPID = NIL_RTPROCESS;
8158
8159 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8160 return true;
8161 }
8162
8163 return false;
8164}
8165
8166/**
8167 * Checks whether the machine can be registered. If so, commits and saves
8168 * all settings.
8169 *
8170 * @note Must be called from mParent's write lock. Locks this object and
8171 * children for writing.
8172 */
8173HRESULT Machine::i_prepareRegister()
8174{
8175 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8176
8177 AutoLimitedCaller autoCaller(this);
8178 AssertComRCReturnRC(autoCaller.rc());
8179
8180 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8181
8182 /* wait for state dependents to drop to zero */
8183 i_ensureNoStateDependencies();
8184
8185 if (!mData->mAccessible)
8186 return setError(VBOX_E_INVALID_OBJECT_STATE,
8187 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8188 mUserData->s.strName.c_str(),
8189 mData->mUuid.toString().c_str());
8190
8191 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8192
8193 if (mData->mRegistered)
8194 return setError(VBOX_E_INVALID_OBJECT_STATE,
8195 tr("The machine '%s' with UUID {%s} is already registered"),
8196 mUserData->s.strName.c_str(),
8197 mData->mUuid.toString().c_str());
8198
8199 HRESULT rc = S_OK;
8200
8201 // Ensure the settings are saved. If we are going to be registered and
8202 // no config file exists yet, create it by calling i_saveSettings() too.
8203 if ( (mData->flModifications)
8204 || (!mData->pMachineConfigFile->fileExists())
8205 )
8206 {
8207 rc = i_saveSettings(NULL);
8208 // no need to check whether VirtualBox.xml needs saving too since
8209 // we can't have a machine XML file rename pending
8210 if (FAILED(rc)) return rc;
8211 }
8212
8213 /* more config checking goes here */
8214
8215 if (SUCCEEDED(rc))
8216 {
8217 /* we may have had implicit modifications we want to fix on success */
8218 i_commit();
8219
8220 mData->mRegistered = true;
8221 }
8222 else
8223 {
8224 /* we may have had implicit modifications we want to cancel on failure*/
8225 i_rollback(false /* aNotify */);
8226 }
8227
8228 return rc;
8229}
8230
8231/**
8232 * Increases the number of objects dependent on the machine state or on the
8233 * registered state. Guarantees that these two states will not change at least
8234 * until #i_releaseStateDependency() is called.
8235 *
8236 * Depending on the @a aDepType value, additional state checks may be made.
8237 * These checks will set extended error info on failure. See
8238 * #i_checkStateDependency() for more info.
8239 *
8240 * If this method returns a failure, the dependency is not added and the caller
8241 * is not allowed to rely on any particular machine state or registration state
8242 * value and may return the failed result code to the upper level.
8243 *
8244 * @param aDepType Dependency type to add.
8245 * @param aState Current machine state (NULL if not interested).
8246 * @param aRegistered Current registered state (NULL if not interested).
8247 *
8248 * @note Locks this object for writing.
8249 */
8250HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8251 MachineState_T *aState /* = NULL */,
8252 BOOL *aRegistered /* = NULL */)
8253{
8254 AutoCaller autoCaller(this);
8255 AssertComRCReturnRC(autoCaller.rc());
8256
8257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8258
8259 HRESULT rc = i_checkStateDependency(aDepType);
8260 if (FAILED(rc)) return rc;
8261
8262 {
8263 if (mData->mMachineStateChangePending != 0)
8264 {
8265 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8266 * drop to zero so don't add more. It may make sense to wait a bit
8267 * and retry before reporting an error (since the pending state
8268 * transition should be really quick) but let's just assert for
8269 * now to see if it ever happens on practice. */
8270
8271 AssertFailed();
8272
8273 return setError(E_ACCESSDENIED,
8274 tr("Machine state change is in progress. Please retry the operation later."));
8275 }
8276
8277 ++mData->mMachineStateDeps;
8278 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8279 }
8280
8281 if (aState)
8282 *aState = mData->mMachineState;
8283 if (aRegistered)
8284 *aRegistered = mData->mRegistered;
8285
8286 return S_OK;
8287}
8288
8289/**
8290 * Decreases the number of objects dependent on the machine state.
8291 * Must always complete the #i_addStateDependency() call after the state
8292 * dependency is no more necessary.
8293 */
8294void Machine::i_releaseStateDependency()
8295{
8296 AutoCaller autoCaller(this);
8297 AssertComRCReturnVoid(autoCaller.rc());
8298
8299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8300
8301 /* releaseStateDependency() w/o addStateDependency()? */
8302 AssertReturnVoid(mData->mMachineStateDeps != 0);
8303 -- mData->mMachineStateDeps;
8304
8305 if (mData->mMachineStateDeps == 0)
8306 {
8307 /* inform i_ensureNoStateDependencies() that there are no more deps */
8308 if (mData->mMachineStateChangePending != 0)
8309 {
8310 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8311 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8312 }
8313 }
8314}
8315
8316Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8317{
8318 /* start with nothing found */
8319 Utf8Str strResult("");
8320
8321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8322
8323 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8324 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8325 // found:
8326 strResult = it->second; // source is a Utf8Str
8327
8328 return strResult;
8329}
8330
8331// protected methods
8332/////////////////////////////////////////////////////////////////////////////
8333
8334/**
8335 * Performs machine state checks based on the @a aDepType value. If a check
8336 * fails, this method will set extended error info, otherwise it will return
8337 * S_OK. It is supposed, that on failure, the caller will immediately return
8338 * the return value of this method to the upper level.
8339 *
8340 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8341 *
8342 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8343 * current state of this machine object allows to change settings of the
8344 * machine (i.e. the machine is not registered, or registered but not running
8345 * and not saved). It is useful to call this method from Machine setters
8346 * before performing any change.
8347 *
8348 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8349 * as for MutableStateDep except that if the machine is saved, S_OK is also
8350 * returned. This is useful in setters which allow changing machine
8351 * properties when it is in the saved state.
8352 *
8353 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8354 * if the current state of this machine object allows to change runtime
8355 * changeable settings of the machine (i.e. the machine is not registered, or
8356 * registered but either running or not running and not saved). It is useful
8357 * to call this method from Machine setters before performing any changes to
8358 * runtime changeable settings.
8359 *
8360 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8361 * the same as for MutableOrRunningStateDep except that if the machine is
8362 * saved, S_OK is also returned. This is useful in setters which allow
8363 * changing runtime and saved state changeable machine properties.
8364 *
8365 * @param aDepType Dependency type to check.
8366 *
8367 * @note Non Machine based classes should use #i_addStateDependency() and
8368 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8369 * template.
8370 *
8371 * @note This method must be called from under this object's read or write
8372 * lock.
8373 */
8374HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8375{
8376 switch (aDepType)
8377 {
8378 case AnyStateDep:
8379 {
8380 break;
8381 }
8382 case MutableStateDep:
8383 {
8384 if ( mData->mRegistered
8385 && ( !i_isSessionMachine()
8386 || ( mData->mMachineState != MachineState_Aborted
8387 && mData->mMachineState != MachineState_Teleported
8388 && mData->mMachineState != MachineState_PoweredOff
8389 )
8390 )
8391 )
8392 return setError(VBOX_E_INVALID_VM_STATE,
8393 tr("The machine is not mutable (state is %s)"),
8394 Global::stringifyMachineState(mData->mMachineState));
8395 break;
8396 }
8397 case MutableOrSavedStateDep:
8398 {
8399 if ( mData->mRegistered
8400 && ( !i_isSessionMachine()
8401 || ( mData->mMachineState != MachineState_Aborted
8402 && mData->mMachineState != MachineState_Teleported
8403 && mData->mMachineState != MachineState_Saved
8404 && mData->mMachineState != MachineState_PoweredOff
8405 )
8406 )
8407 )
8408 return setError(VBOX_E_INVALID_VM_STATE,
8409 tr("The machine is not mutable or saved (state is %s)"),
8410 Global::stringifyMachineState(mData->mMachineState));
8411 break;
8412 }
8413 case MutableOrRunningStateDep:
8414 {
8415 if ( mData->mRegistered
8416 && ( !i_isSessionMachine()
8417 || ( mData->mMachineState != MachineState_Aborted
8418 && mData->mMachineState != MachineState_Teleported
8419 && mData->mMachineState != MachineState_PoweredOff
8420 && !Global::IsOnline(mData->mMachineState)
8421 )
8422 )
8423 )
8424 return setError(VBOX_E_INVALID_VM_STATE,
8425 tr("The machine is not mutable or running (state is %s)"),
8426 Global::stringifyMachineState(mData->mMachineState));
8427 break;
8428 }
8429 case MutableOrSavedOrRunningStateDep:
8430 {
8431 if ( mData->mRegistered
8432 && ( !i_isSessionMachine()
8433 || ( mData->mMachineState != MachineState_Aborted
8434 && mData->mMachineState != MachineState_Teleported
8435 && mData->mMachineState != MachineState_Saved
8436 && mData->mMachineState != MachineState_PoweredOff
8437 && !Global::IsOnline(mData->mMachineState)
8438 )
8439 )
8440 )
8441 return setError(VBOX_E_INVALID_VM_STATE,
8442 tr("The machine is not mutable, saved or running (state is %s)"),
8443 Global::stringifyMachineState(mData->mMachineState));
8444 break;
8445 }
8446 }
8447
8448 return S_OK;
8449}
8450
8451/**
8452 * Helper to initialize all associated child objects and allocate data
8453 * structures.
8454 *
8455 * This method must be called as a part of the object's initialization procedure
8456 * (usually done in the #init() method).
8457 *
8458 * @note Must be called only from #init() or from #i_registeredInit().
8459 */
8460HRESULT Machine::initDataAndChildObjects()
8461{
8462 AutoCaller autoCaller(this);
8463 AssertComRCReturnRC(autoCaller.rc());
8464 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8465 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8466
8467 AssertReturn(!mData->mAccessible, E_FAIL);
8468
8469 /* allocate data structures */
8470 mSSData.allocate();
8471 mUserData.allocate();
8472 mHWData.allocate();
8473 mMediumAttachments.allocate();
8474 mStorageControllers.allocate();
8475 mUSBControllers.allocate();
8476
8477 /* initialize mOSTypeId */
8478 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8479
8480/** @todo r=bird: init() methods never fails, right? Why don't we make them
8481 * return void then! */
8482
8483 /* create associated BIOS settings object */
8484 unconst(mBIOSSettings).createObject();
8485 mBIOSSettings->init(this);
8486
8487 /* create an associated VRDE object (default is disabled) */
8488 unconst(mVRDEServer).createObject();
8489 mVRDEServer->init(this);
8490
8491 /* create associated serial port objects */
8492 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8493 {
8494 unconst(mSerialPorts[slot]).createObject();
8495 mSerialPorts[slot]->init(this, slot);
8496 }
8497
8498 /* create associated parallel port objects */
8499 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8500 {
8501 unconst(mParallelPorts[slot]).createObject();
8502 mParallelPorts[slot]->init(this, slot);
8503 }
8504
8505 /* create the audio adapter object (always present, default is disabled) */
8506 unconst(mAudioAdapter).createObject();
8507 mAudioAdapter->init(this);
8508
8509 /* create the USB device filters object (always present) */
8510 unconst(mUSBDeviceFilters).createObject();
8511 mUSBDeviceFilters->init(this);
8512
8513 /* create associated network adapter objects */
8514 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8515 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8516 {
8517 unconst(mNetworkAdapters[slot]).createObject();
8518 mNetworkAdapters[slot]->init(this, slot);
8519 }
8520
8521 /* create the bandwidth control */
8522 unconst(mBandwidthControl).createObject();
8523 mBandwidthControl->init(this);
8524
8525 return S_OK;
8526}
8527
8528/**
8529 * Helper to uninitialize all associated child objects and to free all data
8530 * structures.
8531 *
8532 * This method must be called as a part of the object's uninitialization
8533 * procedure (usually done in the #uninit() method).
8534 *
8535 * @note Must be called only from #uninit() or from #i_registeredInit().
8536 */
8537void Machine::uninitDataAndChildObjects()
8538{
8539 AutoCaller autoCaller(this);
8540 AssertComRCReturnVoid(autoCaller.rc());
8541 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8542 || getObjectState().getState() == ObjectState::Limited);
8543
8544 /* tell all our other child objects we've been uninitialized */
8545 if (mBandwidthControl)
8546 {
8547 mBandwidthControl->uninit();
8548 unconst(mBandwidthControl).setNull();
8549 }
8550
8551 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8552 {
8553 if (mNetworkAdapters[slot])
8554 {
8555 mNetworkAdapters[slot]->uninit();
8556 unconst(mNetworkAdapters[slot]).setNull();
8557 }
8558 }
8559
8560 if (mUSBDeviceFilters)
8561 {
8562 mUSBDeviceFilters->uninit();
8563 unconst(mUSBDeviceFilters).setNull();
8564 }
8565
8566 if (mAudioAdapter)
8567 {
8568 mAudioAdapter->uninit();
8569 unconst(mAudioAdapter).setNull();
8570 }
8571
8572 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8573 {
8574 if (mParallelPorts[slot])
8575 {
8576 mParallelPorts[slot]->uninit();
8577 unconst(mParallelPorts[slot]).setNull();
8578 }
8579 }
8580
8581 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8582 {
8583 if (mSerialPorts[slot])
8584 {
8585 mSerialPorts[slot]->uninit();
8586 unconst(mSerialPorts[slot]).setNull();
8587 }
8588 }
8589
8590 if (mVRDEServer)
8591 {
8592 mVRDEServer->uninit();
8593 unconst(mVRDEServer).setNull();
8594 }
8595
8596 if (mBIOSSettings)
8597 {
8598 mBIOSSettings->uninit();
8599 unconst(mBIOSSettings).setNull();
8600 }
8601
8602 /* Deassociate media (only when a real Machine or a SnapshotMachine
8603 * instance is uninitialized; SessionMachine instances refer to real
8604 * Machine media). This is necessary for a clean re-initialization of
8605 * the VM after successfully re-checking the accessibility state. Note
8606 * that in case of normal Machine or SnapshotMachine uninitialization (as
8607 * a result of unregistering or deleting the snapshot), outdated media
8608 * attachments will already be uninitialized and deleted, so this
8609 * code will not affect them. */
8610 if ( !mMediumAttachments.isNull()
8611 && !i_isSessionMachine()
8612 )
8613 {
8614 for (MediumAttachmentList::const_iterator
8615 it = mMediumAttachments->begin();
8616 it != mMediumAttachments->end();
8617 ++it)
8618 {
8619 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8620 if (pMedium.isNull())
8621 continue;
8622 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8623 AssertComRC(rc);
8624 }
8625 }
8626
8627 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8628 {
8629 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8630 if (mData->mFirstSnapshot)
8631 {
8632 // snapshots tree is protected by machine write lock; strictly
8633 // this isn't necessary here since we're deleting the entire
8634 // machine, but otherwise we assert in Snapshot::uninit()
8635 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8636 mData->mFirstSnapshot->uninit();
8637 mData->mFirstSnapshot.setNull();
8638 }
8639
8640 mData->mCurrentSnapshot.setNull();
8641 }
8642
8643 /* free data structures (the essential mData structure is not freed here
8644 * since it may be still in use) */
8645 mMediumAttachments.free();
8646 mStorageControllers.free();
8647 mUSBControllers.free();
8648 mHWData.free();
8649 mUserData.free();
8650 mSSData.free();
8651}
8652
8653/**
8654 * Returns a pointer to the Machine object for this machine that acts like a
8655 * parent for complex machine data objects such as shared folders, etc.
8656 *
8657 * For primary Machine objects and for SnapshotMachine objects, returns this
8658 * object's pointer itself. For SessionMachine objects, returns the peer
8659 * (primary) machine pointer.
8660 */
8661Machine *Machine::i_getMachine()
8662{
8663 if (i_isSessionMachine())
8664 return (Machine*)mPeer;
8665 return this;
8666}
8667
8668/**
8669 * Makes sure that there are no machine state dependents. If necessary, waits
8670 * for the number of dependents to drop to zero.
8671 *
8672 * Make sure this method is called from under this object's write lock to
8673 * guarantee that no new dependents may be added when this method returns
8674 * control to the caller.
8675 *
8676 * @note Locks this object for writing. The lock will be released while waiting
8677 * (if necessary).
8678 *
8679 * @warning To be used only in methods that change the machine state!
8680 */
8681void Machine::i_ensureNoStateDependencies()
8682{
8683 AssertReturnVoid(isWriteLockOnCurrentThread());
8684
8685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8686
8687 /* Wait for all state dependents if necessary */
8688 if (mData->mMachineStateDeps != 0)
8689 {
8690 /* lazy semaphore creation */
8691 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8692 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8693
8694 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8695 mData->mMachineStateDeps));
8696
8697 ++mData->mMachineStateChangePending;
8698
8699 /* reset the semaphore before waiting, the last dependent will signal
8700 * it */
8701 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8702
8703 alock.release();
8704
8705 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8706
8707 alock.acquire();
8708
8709 -- mData->mMachineStateChangePending;
8710 }
8711}
8712
8713/**
8714 * Changes the machine state and informs callbacks.
8715 *
8716 * This method is not intended to fail so it either returns S_OK or asserts (and
8717 * returns a failure).
8718 *
8719 * @note Locks this object for writing.
8720 */
8721HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8722{
8723 LogFlowThisFuncEnter();
8724 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8725 Assert(aMachineState != MachineState_Null);
8726
8727 AutoCaller autoCaller(this);
8728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8729
8730 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8731
8732 /* wait for state dependents to drop to zero */
8733 i_ensureNoStateDependencies();
8734
8735 MachineState_T const enmOldState = mData->mMachineState;
8736 if (enmOldState != aMachineState)
8737 {
8738 mData->mMachineState = aMachineState;
8739 RTTimeNow(&mData->mLastStateChange);
8740
8741#ifdef VBOX_WITH_DTRACE_R3_MAIN
8742 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8743#endif
8744 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8745 }
8746
8747 LogFlowThisFuncLeave();
8748 return S_OK;
8749}
8750
8751/**
8752 * Searches for a shared folder with the given logical name
8753 * in the collection of shared folders.
8754 *
8755 * @param aName logical name of the shared folder
8756 * @param aSharedFolder where to return the found object
8757 * @param aSetError whether to set the error info if the folder is
8758 * not found
8759 * @return
8760 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8761 *
8762 * @note
8763 * must be called from under the object's lock!
8764 */
8765HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8766 ComObjPtr<SharedFolder> &aSharedFolder,
8767 bool aSetError /* = false */)
8768{
8769 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8770 for (HWData::SharedFolderList::const_iterator
8771 it = mHWData->mSharedFolders.begin();
8772 it != mHWData->mSharedFolders.end();
8773 ++it)
8774 {
8775 SharedFolder *pSF = *it;
8776 AutoCaller autoCaller(pSF);
8777 if (pSF->i_getName() == aName)
8778 {
8779 aSharedFolder = pSF;
8780 rc = S_OK;
8781 break;
8782 }
8783 }
8784
8785 if (aSetError && FAILED(rc))
8786 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8787
8788 return rc;
8789}
8790
8791/**
8792 * Initializes all machine instance data from the given settings structures
8793 * from XML. The exception is the machine UUID which needs special handling
8794 * depending on the caller's use case, so the caller needs to set that herself.
8795 *
8796 * This gets called in several contexts during machine initialization:
8797 *
8798 * -- When machine XML exists on disk already and needs to be loaded into memory,
8799 * for example, from #i_registeredInit() to load all registered machines on
8800 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8801 * attached to the machine should be part of some media registry already.
8802 *
8803 * -- During OVF import, when a machine config has been constructed from an
8804 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8805 * ensure that the media listed as attachments in the config (which have
8806 * been imported from the OVF) receive the correct registry ID.
8807 *
8808 * -- During VM cloning.
8809 *
8810 * @param config Machine settings from XML.
8811 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8812 * for each attached medium in the config.
8813 * @return
8814 */
8815HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8816 const Guid *puuidRegistry)
8817{
8818 // copy name, description, OS type, teleporter, UTC etc.
8819 mUserData->s = config.machineUserData;
8820
8821 // look up the object by Id to check it is valid
8822 ComObjPtr<GuestOSType> pGuestOSType;
8823 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8824 pGuestOSType);
8825 if (FAILED(rc)) return rc;
8826 mUserData->s.strOsType = pGuestOSType->i_id();
8827
8828 // stateFile (optional)
8829 if (config.strStateFile.isEmpty())
8830 mSSData->strStateFilePath.setNull();
8831 else
8832 {
8833 Utf8Str stateFilePathFull(config.strStateFile);
8834 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8835 if (RT_FAILURE(vrc))
8836 return setError(E_FAIL,
8837 tr("Invalid saved state file path '%s' (%Rrc)"),
8838 config.strStateFile.c_str(),
8839 vrc);
8840 mSSData->strStateFilePath = stateFilePathFull;
8841 }
8842
8843 // snapshot folder needs special processing so set it again
8844 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8845 if (FAILED(rc)) return rc;
8846
8847 /* Copy the extra data items (config may or may not be the same as
8848 * mData->pMachineConfigFile) if necessary. When loading the XML files
8849 * from disk they are the same, but not for OVF import. */
8850 if (mData->pMachineConfigFile != &config)
8851 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8852
8853 /* currentStateModified (optional, default is true) */
8854 mData->mCurrentStateModified = config.fCurrentStateModified;
8855
8856 mData->mLastStateChange = config.timeLastStateChange;
8857
8858 /*
8859 * note: all mUserData members must be assigned prior this point because
8860 * we need to commit changes in order to let mUserData be shared by all
8861 * snapshot machine instances.
8862 */
8863 mUserData.commitCopy();
8864
8865 // machine registry, if present (must be loaded before snapshots)
8866 if (config.canHaveOwnMediaRegistry())
8867 {
8868 // determine machine folder
8869 Utf8Str strMachineFolder = i_getSettingsFileFull();
8870 strMachineFolder.stripFilename();
8871 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8872 config.mediaRegistry,
8873 strMachineFolder);
8874 if (FAILED(rc)) return rc;
8875 }
8876
8877 /* Snapshot node (optional) */
8878 size_t cRootSnapshots;
8879 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8880 {
8881 // there must be only one root snapshot
8882 Assert(cRootSnapshots == 1);
8883
8884 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8885
8886 rc = i_loadSnapshot(snap,
8887 config.uuidCurrentSnapshot,
8888 NULL); // no parent == first snapshot
8889 if (FAILED(rc)) return rc;
8890 }
8891
8892 // hardware data
8893 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8894 if (FAILED(rc)) return rc;
8895
8896 /*
8897 * NOTE: the assignment below must be the last thing to do,
8898 * otherwise it will be not possible to change the settings
8899 * somewhere in the code above because all setters will be
8900 * blocked by i_checkStateDependency(MutableStateDep).
8901 */
8902
8903 /* set the machine state to Aborted or Saved when appropriate */
8904 if (config.fAborted)
8905 {
8906 mSSData->strStateFilePath.setNull();
8907
8908 /* no need to use i_setMachineState() during init() */
8909 mData->mMachineState = MachineState_Aborted;
8910 }
8911 else if (!mSSData->strStateFilePath.isEmpty())
8912 {
8913 /* no need to use i_setMachineState() during init() */
8914 mData->mMachineState = MachineState_Saved;
8915 }
8916
8917 // after loading settings, we are no longer different from the XML on disk
8918 mData->flModifications = 0;
8919
8920 return S_OK;
8921}
8922
8923/**
8924 * Recursively loads all snapshots starting from the given.
8925 *
8926 * @param data snapshot settings.
8927 * @param aCurSnapshotId Current snapshot ID from the settings file.
8928 * @param aParentSnapshot Parent snapshot.
8929 */
8930HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8931 const Guid &aCurSnapshotId,
8932 Snapshot *aParentSnapshot)
8933{
8934 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8935 AssertReturn(!i_isSessionMachine(), E_FAIL);
8936
8937 HRESULT rc = S_OK;
8938
8939 Utf8Str strStateFile;
8940 if (!data.strStateFile.isEmpty())
8941 {
8942 /* optional */
8943 strStateFile = data.strStateFile;
8944 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8945 if (RT_FAILURE(vrc))
8946 return setError(E_FAIL,
8947 tr("Invalid saved state file path '%s' (%Rrc)"),
8948 strStateFile.c_str(),
8949 vrc);
8950 }
8951
8952 /* create a snapshot machine object */
8953 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8954 pSnapshotMachine.createObject();
8955 rc = pSnapshotMachine->initFromSettings(this,
8956 data.hardware,
8957 &data.debugging,
8958 &data.autostart,
8959 data.uuid.ref(),
8960 strStateFile);
8961 if (FAILED(rc)) return rc;
8962
8963 /* create a snapshot object */
8964 ComObjPtr<Snapshot> pSnapshot;
8965 pSnapshot.createObject();
8966 /* initialize the snapshot */
8967 rc = pSnapshot->init(mParent, // VirtualBox object
8968 data.uuid,
8969 data.strName,
8970 data.strDescription,
8971 data.timestamp,
8972 pSnapshotMachine,
8973 aParentSnapshot);
8974 if (FAILED(rc)) return rc;
8975
8976 /* memorize the first snapshot if necessary */
8977 if (!mData->mFirstSnapshot)
8978 mData->mFirstSnapshot = pSnapshot;
8979
8980 /* memorize the current snapshot when appropriate */
8981 if ( !mData->mCurrentSnapshot
8982 && pSnapshot->i_getId() == aCurSnapshotId
8983 )
8984 mData->mCurrentSnapshot = pSnapshot;
8985
8986 // now create the children
8987 for (settings::SnapshotsList::const_iterator
8988 it = data.llChildSnapshots.begin();
8989 it != data.llChildSnapshots.end();
8990 ++it)
8991 {
8992 const settings::Snapshot &childData = *it;
8993 // recurse
8994 rc = i_loadSnapshot(childData,
8995 aCurSnapshotId,
8996 pSnapshot); // parent = the one we created above
8997 if (FAILED(rc)) return rc;
8998 }
8999
9000 return rc;
9001}
9002
9003/**
9004 * Loads settings into mHWData.
9005 *
9006 * @param puuidRegistry Registry ID.
9007 * @param puuidSnapshot Snapshot ID
9008 * @param data Reference to the hardware settings.
9009 * @param pDbg Pointer to the debugging settings.
9010 * @param pAutostart Pointer to the autostart settings.
9011 */
9012HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9013 const Guid *puuidSnapshot,
9014 const settings::Hardware &data,
9015 const settings::Debugging *pDbg,
9016 const settings::Autostart *pAutostart)
9017{
9018 AssertReturn(!i_isSessionMachine(), E_FAIL);
9019
9020 HRESULT rc = S_OK;
9021
9022 try
9023 {
9024 ComObjPtr<GuestOSType> pGuestOSType;
9025 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
9026 pGuestOSType);
9027 if (FAILED(rc))
9028 return rc;
9029
9030 /* The hardware version attribute (optional). */
9031 mHWData->mHWVersion = data.strVersion;
9032 mHWData->mHardwareUUID = data.uuid;
9033
9034 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9035 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9036 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9037 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9038 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9039 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9040 mHWData->mPAEEnabled = data.fPAE;
9041 mHWData->mLongMode = data.enmLongMode;
9042 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9043 mHWData->mAPIC = data.fAPIC;
9044 mHWData->mX2APIC = data.fX2APIC;
9045 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9046 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9047 mHWData->mSpecCtrl = data.fSpecCtrl;
9048 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9049 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9050 mHWData->mCPUCount = data.cCPUs;
9051 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9052 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9053 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9054 mHWData->mCpuProfile = data.strCpuProfile;
9055
9056 // cpu
9057 if (mHWData->mCPUHotPlugEnabled)
9058 {
9059 for (settings::CpuList::const_iterator
9060 it = data.llCpus.begin();
9061 it != data.llCpus.end();
9062 ++it)
9063 {
9064 const settings::Cpu &cpu = *it;
9065
9066 mHWData->mCPUAttached[cpu.ulId] = true;
9067 }
9068 }
9069
9070 // cpuid leafs
9071 for (settings::CpuIdLeafsList::const_iterator
9072 it = data.llCpuIdLeafs.begin();
9073 it != data.llCpuIdLeafs.end();
9074 ++it)
9075 {
9076 const settings::CpuIdLeaf &rLeaf= *it;
9077 if ( rLeaf.idx < UINT32_C(0x20)
9078 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9079 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9080 mHWData->mCpuIdLeafList.push_back(rLeaf);
9081 /* else: just ignore */
9082 }
9083
9084 mHWData->mMemorySize = data.ulMemorySizeMB;
9085 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9086
9087 // boot order
9088 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9089 {
9090 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9091 if (it == data.mapBootOrder.end())
9092 mHWData->mBootOrder[i] = DeviceType_Null;
9093 else
9094 mHWData->mBootOrder[i] = it->second;
9095 }
9096
9097 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9098 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9099 mHWData->mMonitorCount = data.cMonitors;
9100 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9101 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9102 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9103 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9104 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9105 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9106 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9107 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9108 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9109 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9110 if (!data.strVideoCaptureFile.isEmpty())
9111 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9112 else
9113 mHWData->mVideoCaptureFile.setNull();
9114 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9115 mHWData->mFirmwareType = data.firmwareType;
9116 mHWData->mPointingHIDType = data.pointingHIDType;
9117 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9118 mHWData->mChipsetType = data.chipsetType;
9119 mHWData->mParavirtProvider = data.paravirtProvider;
9120 mHWData->mParavirtDebug = data.strParavirtDebug;
9121 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9122 mHWData->mHPETEnabled = data.fHPETEnabled;
9123
9124 /* VRDEServer */
9125 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9126 if (FAILED(rc)) return rc;
9127
9128 /* BIOS */
9129 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9130 if (FAILED(rc)) return rc;
9131
9132 // Bandwidth control (must come before network adapters)
9133 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9134 if (FAILED(rc)) return rc;
9135
9136 /* Shared folders */
9137 for (settings::USBControllerList::const_iterator
9138 it = data.usbSettings.llUSBControllers.begin();
9139 it != data.usbSettings.llUSBControllers.end();
9140 ++it)
9141 {
9142 const settings::USBController &settingsCtrl = *it;
9143 ComObjPtr<USBController> newCtrl;
9144
9145 newCtrl.createObject();
9146 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9147 mUSBControllers->push_back(newCtrl);
9148 }
9149
9150 /* USB device filters */
9151 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9152 if (FAILED(rc)) return rc;
9153
9154 // network adapters (establish array size first and apply defaults, to
9155 // ensure reading the same settings as we saved, since the list skips
9156 // adapters having defaults)
9157 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9158 size_t oldCount = mNetworkAdapters.size();
9159 if (newCount > oldCount)
9160 {
9161 mNetworkAdapters.resize(newCount);
9162 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9163 {
9164 unconst(mNetworkAdapters[slot]).createObject();
9165 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9166 }
9167 }
9168 else if (newCount < oldCount)
9169 mNetworkAdapters.resize(newCount);
9170 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9171 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9172 for (settings::NetworkAdaptersList::const_iterator
9173 it = data.llNetworkAdapters.begin();
9174 it != data.llNetworkAdapters.end();
9175 ++it)
9176 {
9177 const settings::NetworkAdapter &nic = *it;
9178
9179 /* slot uniqueness is guaranteed by XML Schema */
9180 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9181 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9182 if (FAILED(rc)) return rc;
9183 }
9184
9185 // serial ports (establish defaults first, to ensure reading the same
9186 // settings as we saved, since the list skips ports having defaults)
9187 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9188 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9189 for (settings::SerialPortsList::const_iterator
9190 it = data.llSerialPorts.begin();
9191 it != data.llSerialPorts.end();
9192 ++it)
9193 {
9194 const settings::SerialPort &s = *it;
9195
9196 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9197 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9198 if (FAILED(rc)) return rc;
9199 }
9200
9201 // parallel ports (establish defaults first, to ensure reading the same
9202 // settings as we saved, since the list skips ports having defaults)
9203 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9204 mParallelPorts[i]->i_applyDefaults();
9205 for (settings::ParallelPortsList::const_iterator
9206 it = data.llParallelPorts.begin();
9207 it != data.llParallelPorts.end();
9208 ++it)
9209 {
9210 const settings::ParallelPort &p = *it;
9211
9212 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9213 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9214 if (FAILED(rc)) return rc;
9215 }
9216
9217 /* AudioAdapter */
9218 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9219 if (FAILED(rc)) return rc;
9220
9221 /* storage controllers */
9222 rc = i_loadStorageControllers(data.storage,
9223 puuidRegistry,
9224 puuidSnapshot);
9225 if (FAILED(rc)) return rc;
9226
9227 /* Shared folders */
9228 for (settings::SharedFoldersList::const_iterator
9229 it = data.llSharedFolders.begin();
9230 it != data.llSharedFolders.end();
9231 ++it)
9232 {
9233 const settings::SharedFolder &sf = *it;
9234
9235 ComObjPtr<SharedFolder> sharedFolder;
9236 /* Check for double entries. Not allowed! */
9237 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9238 if (SUCCEEDED(rc))
9239 return setError(VBOX_E_OBJECT_IN_USE,
9240 tr("Shared folder named '%s' already exists"),
9241 sf.strName.c_str());
9242
9243 /* Create the new shared folder. Don't break on error. This will be
9244 * reported when the machine starts. */
9245 sharedFolder.createObject();
9246 rc = sharedFolder->init(i_getMachine(),
9247 sf.strName,
9248 sf.strHostPath,
9249 RT_BOOL(sf.fWritable),
9250 RT_BOOL(sf.fAutoMount),
9251 false /* fFailOnError */);
9252 if (FAILED(rc)) return rc;
9253 mHWData->mSharedFolders.push_back(sharedFolder);
9254 }
9255
9256 // Clipboard
9257 mHWData->mClipboardMode = data.clipboardMode;
9258
9259 // drag'n'drop
9260 mHWData->mDnDMode = data.dndMode;
9261
9262 // guest settings
9263 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9264
9265 // IO settings
9266 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9267 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9268
9269 // Host PCI devices
9270 for (settings::HostPCIDeviceAttachmentList::const_iterator
9271 it = data.pciAttachments.begin();
9272 it != data.pciAttachments.end();
9273 ++it)
9274 {
9275 const settings::HostPCIDeviceAttachment &hpda = *it;
9276 ComObjPtr<PCIDeviceAttachment> pda;
9277
9278 pda.createObject();
9279 pda->i_loadSettings(this, hpda);
9280 mHWData->mPCIDeviceAssignments.push_back(pda);
9281 }
9282
9283 /*
9284 * (The following isn't really real hardware, but it lives in HWData
9285 * for reasons of convenience.)
9286 */
9287
9288#ifdef VBOX_WITH_GUEST_PROPS
9289 /* Guest properties (optional) */
9290
9291 /* Only load transient guest properties for configs which have saved
9292 * state, because there shouldn't be any for powered off VMs. The same
9293 * logic applies for snapshots, as offline snapshots shouldn't have
9294 * any such properties. They confuse the code in various places.
9295 * Note: can't rely on the machine state, as it isn't set yet. */
9296 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9297 /* apologies for the hacky unconst() usage, but this needs hacking
9298 * actually inconsistent settings into consistency, otherwise there
9299 * will be some corner cases where the inconsistency survives
9300 * surprisingly long without getting fixed, especially for snapshots
9301 * as there are no config changes. */
9302 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9303 for (settings::GuestPropertiesList::iterator
9304 it = llGuestProperties.begin();
9305 it != llGuestProperties.end();
9306 /*nothing*/)
9307 {
9308 const settings::GuestProperty &prop = *it;
9309 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9310 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9311 if ( fSkipTransientGuestProperties
9312 && ( fFlags & GUEST_PROP_F_TRANSIENT
9313 || fFlags & GUEST_PROP_F_TRANSRESET))
9314 {
9315 it = llGuestProperties.erase(it);
9316 continue;
9317 }
9318 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9319 mHWData->mGuestProperties[prop.strName] = property;
9320 ++it;
9321 }
9322#endif /* VBOX_WITH_GUEST_PROPS defined */
9323
9324 rc = i_loadDebugging(pDbg);
9325 if (FAILED(rc))
9326 return rc;
9327
9328 mHWData->mAutostart = *pAutostart;
9329
9330 /* default frontend */
9331 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9332 }
9333 catch (std::bad_alloc &)
9334 {
9335 return E_OUTOFMEMORY;
9336 }
9337
9338 AssertComRC(rc);
9339 return rc;
9340}
9341
9342/**
9343 * Called from i_loadHardware() to load the debugging settings of the
9344 * machine.
9345 *
9346 * @param pDbg Pointer to the settings.
9347 */
9348HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9349{
9350 mHWData->mDebugging = *pDbg;
9351 /* no more processing currently required, this will probably change. */
9352 return S_OK;
9353}
9354
9355/**
9356 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9357 *
9358 * @param data storage settings.
9359 * @param puuidRegistry media registry ID to set media to or NULL;
9360 * see Machine::i_loadMachineDataFromSettings()
9361 * @param puuidSnapshot snapshot ID
9362 * @return
9363 */
9364HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9365 const Guid *puuidRegistry,
9366 const Guid *puuidSnapshot)
9367{
9368 AssertReturn(!i_isSessionMachine(), E_FAIL);
9369
9370 HRESULT rc = S_OK;
9371
9372 for (settings::StorageControllersList::const_iterator
9373 it = data.llStorageControllers.begin();
9374 it != data.llStorageControllers.end();
9375 ++it)
9376 {
9377 const settings::StorageController &ctlData = *it;
9378
9379 ComObjPtr<StorageController> pCtl;
9380 /* Try to find one with the name first. */
9381 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9382 if (SUCCEEDED(rc))
9383 return setError(VBOX_E_OBJECT_IN_USE,
9384 tr("Storage controller named '%s' already exists"),
9385 ctlData.strName.c_str());
9386
9387 pCtl.createObject();
9388 rc = pCtl->init(this,
9389 ctlData.strName,
9390 ctlData.storageBus,
9391 ctlData.ulInstance,
9392 ctlData.fBootable);
9393 if (FAILED(rc)) return rc;
9394
9395 mStorageControllers->push_back(pCtl);
9396
9397 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9398 if (FAILED(rc)) return rc;
9399
9400 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9401 if (FAILED(rc)) return rc;
9402
9403 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9404 if (FAILED(rc)) return rc;
9405
9406 /* Load the attached devices now. */
9407 rc = i_loadStorageDevices(pCtl,
9408 ctlData,
9409 puuidRegistry,
9410 puuidSnapshot);
9411 if (FAILED(rc)) return rc;
9412 }
9413
9414 return S_OK;
9415}
9416
9417/**
9418 * Called from i_loadStorageControllers for a controller's devices.
9419 *
9420 * @param aStorageController
9421 * @param data
9422 * @param puuidRegistry media registry ID to set media to or NULL; see
9423 * Machine::i_loadMachineDataFromSettings()
9424 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9425 * @return
9426 */
9427HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9428 const settings::StorageController &data,
9429 const Guid *puuidRegistry,
9430 const Guid *puuidSnapshot)
9431{
9432 HRESULT rc = S_OK;
9433
9434 /* paranoia: detect duplicate attachments */
9435 for (settings::AttachedDevicesList::const_iterator
9436 it = data.llAttachedDevices.begin();
9437 it != data.llAttachedDevices.end();
9438 ++it)
9439 {
9440 const settings::AttachedDevice &ad = *it;
9441
9442 for (settings::AttachedDevicesList::const_iterator it2 = it;
9443 it2 != data.llAttachedDevices.end();
9444 ++it2)
9445 {
9446 if (it == it2)
9447 continue;
9448
9449 const settings::AttachedDevice &ad2 = *it2;
9450
9451 if ( ad.lPort == ad2.lPort
9452 && ad.lDevice == ad2.lDevice)
9453 {
9454 return setError(E_FAIL,
9455 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9456 aStorageController->i_getName().c_str(),
9457 ad.lPort,
9458 ad.lDevice,
9459 mUserData->s.strName.c_str());
9460 }
9461 }
9462 }
9463
9464 for (settings::AttachedDevicesList::const_iterator
9465 it = data.llAttachedDevices.begin();
9466 it != data.llAttachedDevices.end();
9467 ++it)
9468 {
9469 const settings::AttachedDevice &dev = *it;
9470 ComObjPtr<Medium> medium;
9471
9472 switch (dev.deviceType)
9473 {
9474 case DeviceType_Floppy:
9475 case DeviceType_DVD:
9476 if (dev.strHostDriveSrc.isNotEmpty())
9477 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9478 false /* fRefresh */, medium);
9479 else
9480 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9481 dev.uuid,
9482 false /* fRefresh */,
9483 false /* aSetError */,
9484 medium);
9485 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9486 // This is not an error. The host drive or UUID might have vanished, so just go
9487 // ahead without this removeable medium attachment
9488 rc = S_OK;
9489 break;
9490
9491 case DeviceType_HardDisk:
9492 {
9493 /* find a hard disk by UUID */
9494 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9495 if (FAILED(rc))
9496 {
9497 if (i_isSnapshotMachine())
9498 {
9499 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9500 // so the user knows that the bad disk is in a snapshot somewhere
9501 com::ErrorInfo info;
9502 return setError(E_FAIL,
9503 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9504 puuidSnapshot->raw(),
9505 info.getText().raw());
9506 }
9507 else
9508 return rc;
9509 }
9510
9511 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9512
9513 if (medium->i_getType() == MediumType_Immutable)
9514 {
9515 if (i_isSnapshotMachine())
9516 return setError(E_FAIL,
9517 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9518 "of the virtual machine '%s' ('%s')"),
9519 medium->i_getLocationFull().c_str(),
9520 dev.uuid.raw(),
9521 puuidSnapshot->raw(),
9522 mUserData->s.strName.c_str(),
9523 mData->m_strConfigFileFull.c_str());
9524
9525 return setError(E_FAIL,
9526 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly 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
9533 if (medium->i_getType() == MediumType_MultiAttach)
9534 {
9535 if (i_isSnapshotMachine())
9536 return setError(E_FAIL,
9537 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9538 "of the virtual machine '%s' ('%s')"),
9539 medium->i_getLocationFull().c_str(),
9540 dev.uuid.raw(),
9541 puuidSnapshot->raw(),
9542 mUserData->s.strName.c_str(),
9543 mData->m_strConfigFileFull.c_str());
9544
9545 return setError(E_FAIL,
9546 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9547 medium->i_getLocationFull().c_str(),
9548 dev.uuid.raw(),
9549 mUserData->s.strName.c_str(),
9550 mData->m_strConfigFileFull.c_str());
9551 }
9552
9553 if ( !i_isSnapshotMachine()
9554 && medium->i_getChildren().size() != 0
9555 )
9556 return setError(E_FAIL,
9557 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9558 "because it has %d differencing child hard disks"),
9559 medium->i_getLocationFull().c_str(),
9560 dev.uuid.raw(),
9561 mUserData->s.strName.c_str(),
9562 mData->m_strConfigFileFull.c_str(),
9563 medium->i_getChildren().size());
9564
9565 if (i_findAttachment(*mMediumAttachments.data(),
9566 medium))
9567 return setError(E_FAIL,
9568 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9569 medium->i_getLocationFull().c_str(),
9570 dev.uuid.raw(),
9571 mUserData->s.strName.c_str(),
9572 mData->m_strConfigFileFull.c_str());
9573
9574 break;
9575 }
9576
9577 default:
9578 return setError(E_FAIL,
9579 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9580 medium->i_getLocationFull().c_str(),
9581 mUserData->s.strName.c_str(),
9582 mData->m_strConfigFileFull.c_str());
9583 }
9584
9585 if (FAILED(rc))
9586 break;
9587
9588 /* Bandwidth groups are loaded at this point. */
9589 ComObjPtr<BandwidthGroup> pBwGroup;
9590
9591 if (!dev.strBwGroup.isEmpty())
9592 {
9593 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9594 if (FAILED(rc))
9595 return setError(E_FAIL,
9596 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9597 medium->i_getLocationFull().c_str(),
9598 dev.strBwGroup.c_str(),
9599 mUserData->s.strName.c_str(),
9600 mData->m_strConfigFileFull.c_str());
9601 pBwGroup->i_reference();
9602 }
9603
9604 const Utf8Str controllerName = aStorageController->i_getName();
9605 ComObjPtr<MediumAttachment> pAttachment;
9606 pAttachment.createObject();
9607 rc = pAttachment->init(this,
9608 medium,
9609 controllerName,
9610 dev.lPort,
9611 dev.lDevice,
9612 dev.deviceType,
9613 false,
9614 dev.fPassThrough,
9615 dev.fTempEject,
9616 dev.fNonRotational,
9617 dev.fDiscard,
9618 dev.fHotPluggable,
9619 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9620 if (FAILED(rc)) break;
9621
9622 /* associate the medium with this machine and snapshot */
9623 if (!medium.isNull())
9624 {
9625 AutoCaller medCaller(medium);
9626 if (FAILED(medCaller.rc())) return medCaller.rc();
9627 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9628
9629 if (i_isSnapshotMachine())
9630 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9631 else
9632 rc = medium->i_addBackReference(mData->mUuid);
9633 /* If the medium->addBackReference fails it sets an appropriate
9634 * error message, so no need to do any guesswork here. */
9635
9636 if (puuidRegistry)
9637 // caller wants registry ID to be set on all attached media (OVF import case)
9638 medium->i_addRegistry(*puuidRegistry);
9639 }
9640
9641 if (FAILED(rc))
9642 break;
9643
9644 /* back up mMediumAttachments to let registeredInit() properly rollback
9645 * on failure (= limited accessibility) */
9646 i_setModified(IsModified_Storage);
9647 mMediumAttachments.backup();
9648 mMediumAttachments->push_back(pAttachment);
9649 }
9650
9651 return rc;
9652}
9653
9654/**
9655 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9656 *
9657 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9658 * @param aSnapshot where to return the found snapshot
9659 * @param aSetError true to set extended error info on failure
9660 */
9661HRESULT Machine::i_findSnapshotById(const Guid &aId,
9662 ComObjPtr<Snapshot> &aSnapshot,
9663 bool aSetError /* = false */)
9664{
9665 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9666
9667 if (!mData->mFirstSnapshot)
9668 {
9669 if (aSetError)
9670 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9671 return E_FAIL;
9672 }
9673
9674 if (aId.isZero())
9675 aSnapshot = mData->mFirstSnapshot;
9676 else
9677 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9678
9679 if (!aSnapshot)
9680 {
9681 if (aSetError)
9682 return setError(E_FAIL,
9683 tr("Could not find a snapshot with UUID {%s}"),
9684 aId.toString().c_str());
9685 return E_FAIL;
9686 }
9687
9688 return S_OK;
9689}
9690
9691/**
9692 * Returns the snapshot with the given name or fails of no such snapshot.
9693 *
9694 * @param strName snapshot name to find
9695 * @param aSnapshot where to return the found snapshot
9696 * @param aSetError true to set extended error info on failure
9697 */
9698HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9699 ComObjPtr<Snapshot> &aSnapshot,
9700 bool aSetError /* = false */)
9701{
9702 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9703
9704 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9705
9706 if (!mData->mFirstSnapshot)
9707 {
9708 if (aSetError)
9709 return setError(VBOX_E_OBJECT_NOT_FOUND,
9710 tr("This machine does not have any snapshots"));
9711 return VBOX_E_OBJECT_NOT_FOUND;
9712 }
9713
9714 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9715
9716 if (!aSnapshot)
9717 {
9718 if (aSetError)
9719 return setError(VBOX_E_OBJECT_NOT_FOUND,
9720 tr("Could not find a snapshot named '%s'"), strName.c_str());
9721 return VBOX_E_OBJECT_NOT_FOUND;
9722 }
9723
9724 return S_OK;
9725}
9726
9727/**
9728 * Returns a storage controller object with the given name.
9729 *
9730 * @param aName storage controller name to find
9731 * @param aStorageController where to return the found storage controller
9732 * @param aSetError true to set extended error info on failure
9733 */
9734HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9735 ComObjPtr<StorageController> &aStorageController,
9736 bool aSetError /* = false */)
9737{
9738 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9739
9740 for (StorageControllerList::const_iterator
9741 it = mStorageControllers->begin();
9742 it != mStorageControllers->end();
9743 ++it)
9744 {
9745 if ((*it)->i_getName() == aName)
9746 {
9747 aStorageController = (*it);
9748 return S_OK;
9749 }
9750 }
9751
9752 if (aSetError)
9753 return setError(VBOX_E_OBJECT_NOT_FOUND,
9754 tr("Could not find a storage controller named '%s'"),
9755 aName.c_str());
9756 return VBOX_E_OBJECT_NOT_FOUND;
9757}
9758
9759/**
9760 * Returns a USB controller object with the given name.
9761 *
9762 * @param aName USB controller name to find
9763 * @param aUSBController where to return the found USB controller
9764 * @param aSetError true to set extended error info on failure
9765 */
9766HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9767 ComObjPtr<USBController> &aUSBController,
9768 bool aSetError /* = false */)
9769{
9770 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9771
9772 for (USBControllerList::const_iterator
9773 it = mUSBControllers->begin();
9774 it != mUSBControllers->end();
9775 ++it)
9776 {
9777 if ((*it)->i_getName() == aName)
9778 {
9779 aUSBController = (*it);
9780 return S_OK;
9781 }
9782 }
9783
9784 if (aSetError)
9785 return setError(VBOX_E_OBJECT_NOT_FOUND,
9786 tr("Could not find a storage controller named '%s'"),
9787 aName.c_str());
9788 return VBOX_E_OBJECT_NOT_FOUND;
9789}
9790
9791/**
9792 * Returns the number of USB controller instance of the given type.
9793 *
9794 * @param enmType USB controller type.
9795 */
9796ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9797{
9798 ULONG cCtrls = 0;
9799
9800 for (USBControllerList::const_iterator
9801 it = mUSBControllers->begin();
9802 it != mUSBControllers->end();
9803 ++it)
9804 {
9805 if ((*it)->i_getControllerType() == enmType)
9806 cCtrls++;
9807 }
9808
9809 return cCtrls;
9810}
9811
9812HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9813 MediumAttachmentList &atts)
9814{
9815 AutoCaller autoCaller(this);
9816 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9817
9818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9819
9820 for (MediumAttachmentList::const_iterator
9821 it = mMediumAttachments->begin();
9822 it != mMediumAttachments->end();
9823 ++it)
9824 {
9825 const ComObjPtr<MediumAttachment> &pAtt = *it;
9826 // should never happen, but deal with NULL pointers in the list.
9827 AssertContinue(!pAtt.isNull());
9828
9829 // getControllerName() needs caller+read lock
9830 AutoCaller autoAttCaller(pAtt);
9831 if (FAILED(autoAttCaller.rc()))
9832 {
9833 atts.clear();
9834 return autoAttCaller.rc();
9835 }
9836 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9837
9838 if (pAtt->i_getControllerName() == aName)
9839 atts.push_back(pAtt);
9840 }
9841
9842 return S_OK;
9843}
9844
9845
9846/**
9847 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9848 * file if the machine name was changed and about creating a new settings file
9849 * if this is a new machine.
9850 *
9851 * @note Must be never called directly but only from #saveSettings().
9852 */
9853HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9854{
9855 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9856
9857 HRESULT rc = S_OK;
9858
9859 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9860
9861 /// @todo need to handle primary group change, too
9862
9863 /* attempt to rename the settings file if machine name is changed */
9864 if ( mUserData->s.fNameSync
9865 && mUserData.isBackedUp()
9866 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9867 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9868 )
9869 {
9870 bool dirRenamed = false;
9871 bool fileRenamed = false;
9872
9873 Utf8Str configFile, newConfigFile;
9874 Utf8Str configFilePrev, newConfigFilePrev;
9875 Utf8Str configDir, newConfigDir;
9876
9877 do
9878 {
9879 int vrc = VINF_SUCCESS;
9880
9881 Utf8Str name = mUserData.backedUpData()->s.strName;
9882 Utf8Str newName = mUserData->s.strName;
9883 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9884 if (group == "/")
9885 group.setNull();
9886 Utf8Str newGroup = mUserData->s.llGroups.front();
9887 if (newGroup == "/")
9888 newGroup.setNull();
9889
9890 configFile = mData->m_strConfigFileFull;
9891
9892 /* first, rename the directory if it matches the group and machine name */
9893 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9894 group.c_str(), RTPATH_DELIMITER, name.c_str());
9895 /** @todo hack, make somehow use of ComposeMachineFilename */
9896 if (mUserData->s.fDirectoryIncludesUUID)
9897 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9898 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9899 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9900 /** @todo hack, make somehow use of ComposeMachineFilename */
9901 if (mUserData->s.fDirectoryIncludesUUID)
9902 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9903 configDir = configFile;
9904 configDir.stripFilename();
9905 newConfigDir = configDir;
9906 if ( configDir.length() >= groupPlusName.length()
9907 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9908 groupPlusName.c_str()))
9909 {
9910 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9911 Utf8Str newConfigBaseDir(newConfigDir);
9912 newConfigDir.append(newGroupPlusName);
9913 /* consistency: use \ if appropriate on the platform */
9914 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9915 /* new dir and old dir cannot be equal here because of 'if'
9916 * above and because name != newName */
9917 Assert(configDir != newConfigDir);
9918 if (!fSettingsFileIsNew)
9919 {
9920 /* perform real rename only if the machine is not new */
9921 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9922 if ( vrc == VERR_FILE_NOT_FOUND
9923 || vrc == VERR_PATH_NOT_FOUND)
9924 {
9925 /* create the parent directory, then retry renaming */
9926 Utf8Str parent(newConfigDir);
9927 parent.stripFilename();
9928 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9929 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9930 }
9931 if (RT_FAILURE(vrc))
9932 {
9933 rc = setError(E_FAIL,
9934 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9935 configDir.c_str(),
9936 newConfigDir.c_str(),
9937 vrc);
9938 break;
9939 }
9940 /* delete subdirectories which are no longer needed */
9941 Utf8Str dir(configDir);
9942 dir.stripFilename();
9943 while (dir != newConfigBaseDir && dir != ".")
9944 {
9945 vrc = RTDirRemove(dir.c_str());
9946 if (RT_FAILURE(vrc))
9947 break;
9948 dir.stripFilename();
9949 }
9950 dirRenamed = true;
9951 }
9952 }
9953
9954 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9955 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9956
9957 /* then try to rename the settings file itself */
9958 if (newConfigFile != configFile)
9959 {
9960 /* get the path to old settings file in renamed directory */
9961 configFile = Utf8StrFmt("%s%c%s",
9962 newConfigDir.c_str(),
9963 RTPATH_DELIMITER,
9964 RTPathFilename(configFile.c_str()));
9965 if (!fSettingsFileIsNew)
9966 {
9967 /* perform real rename only if the machine is not new */
9968 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9969 if (RT_FAILURE(vrc))
9970 {
9971 rc = setError(E_FAIL,
9972 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9973 configFile.c_str(),
9974 newConfigFile.c_str(),
9975 vrc);
9976 break;
9977 }
9978 fileRenamed = true;
9979 configFilePrev = configFile;
9980 configFilePrev += "-prev";
9981 newConfigFilePrev = newConfigFile;
9982 newConfigFilePrev += "-prev";
9983 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9984 }
9985 }
9986
9987 // update m_strConfigFileFull amd mConfigFile
9988 mData->m_strConfigFileFull = newConfigFile;
9989 // compute the relative path too
9990 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9991
9992 // store the old and new so that VirtualBox::i_saveSettings() can update
9993 // the media registry
9994 if ( mData->mRegistered
9995 && (configDir != newConfigDir || configFile != newConfigFile))
9996 {
9997 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9998
9999 if (pfNeedsGlobalSaveSettings)
10000 *pfNeedsGlobalSaveSettings = true;
10001 }
10002
10003 // in the saved state file path, replace the old directory with the new directory
10004 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10005 {
10006 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10007 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10008 }
10009
10010 // and do the same thing for the saved state file paths of all the online snapshots
10011 if (mData->mFirstSnapshot)
10012 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10013 newConfigDir.c_str());
10014 }
10015 while (0);
10016
10017 if (FAILED(rc))
10018 {
10019 /* silently try to rename everything back */
10020 if (fileRenamed)
10021 {
10022 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10023 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10024 }
10025 if (dirRenamed)
10026 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10027 }
10028
10029 if (FAILED(rc)) return rc;
10030 }
10031
10032 if (fSettingsFileIsNew)
10033 {
10034 /* create a virgin config file */
10035 int vrc = VINF_SUCCESS;
10036
10037 /* ensure the settings directory exists */
10038 Utf8Str path(mData->m_strConfigFileFull);
10039 path.stripFilename();
10040 if (!RTDirExists(path.c_str()))
10041 {
10042 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10043 if (RT_FAILURE(vrc))
10044 {
10045 return setError(E_FAIL,
10046 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10047 path.c_str(),
10048 vrc);
10049 }
10050 }
10051
10052 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10053 path = Utf8Str(mData->m_strConfigFileFull);
10054 RTFILE f = NIL_RTFILE;
10055 vrc = RTFileOpen(&f, path.c_str(),
10056 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10057 if (RT_FAILURE(vrc))
10058 return setError(E_FAIL,
10059 tr("Could not create the settings file '%s' (%Rrc)"),
10060 path.c_str(),
10061 vrc);
10062 RTFileClose(f);
10063 }
10064
10065 return rc;
10066}
10067
10068/**
10069 * Saves and commits machine data, user data and hardware data.
10070 *
10071 * Note that on failure, the data remains uncommitted.
10072 *
10073 * @a aFlags may combine the following flags:
10074 *
10075 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10076 * Used when saving settings after an operation that makes them 100%
10077 * correspond to the settings from the current snapshot.
10078 * - SaveS_Force: settings will be saved without doing a deep compare of the
10079 * settings structures. This is used when this is called because snapshots
10080 * have changed to avoid the overhead of the deep compare.
10081 *
10082 * @note Must be called from under this object's write lock. Locks children for
10083 * writing.
10084 *
10085 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10086 * initialized to false and that will be set to true by this function if
10087 * the caller must invoke VirtualBox::i_saveSettings() because the global
10088 * settings have changed. This will happen if a machine rename has been
10089 * saved and the global machine and media registries will therefore need
10090 * updating.
10091 * @param aFlags Flags.
10092 */
10093HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10094 int aFlags /*= 0*/)
10095{
10096 LogFlowThisFuncEnter();
10097
10098 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10099
10100 /* make sure child objects are unable to modify the settings while we are
10101 * saving them */
10102 i_ensureNoStateDependencies();
10103
10104 AssertReturn(!i_isSnapshotMachine(),
10105 E_FAIL);
10106
10107 HRESULT rc = S_OK;
10108 bool fNeedsWrite = false;
10109
10110 /* First, prepare to save settings. It will care about renaming the
10111 * settings directory and file if the machine name was changed and about
10112 * creating a new settings file if this is a new machine. */
10113 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10114 if (FAILED(rc)) return rc;
10115
10116 // keep a pointer to the current settings structures
10117 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10118 settings::MachineConfigFile *pNewConfig = NULL;
10119
10120 try
10121 {
10122 // make a fresh one to have everyone write stuff into
10123 pNewConfig = new settings::MachineConfigFile(NULL);
10124 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10125
10126 // now go and copy all the settings data from COM to the settings structures
10127 // (this calls i_saveSettings() on all the COM objects in the machine)
10128 i_copyMachineDataToSettings(*pNewConfig);
10129
10130 if (aFlags & SaveS_ResetCurStateModified)
10131 {
10132 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10133 mData->mCurrentStateModified = FALSE;
10134 fNeedsWrite = true; // always, no need to compare
10135 }
10136 else if (aFlags & SaveS_Force)
10137 {
10138 fNeedsWrite = true; // always, no need to compare
10139 }
10140 else
10141 {
10142 if (!mData->mCurrentStateModified)
10143 {
10144 // do a deep compare of the settings that we just saved with the settings
10145 // previously stored in the config file; this invokes MachineConfigFile::operator==
10146 // which does a deep compare of all the settings, which is expensive but less expensive
10147 // than writing out XML in vain
10148 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10149
10150 // could still be modified if any settings changed
10151 mData->mCurrentStateModified = fAnySettingsChanged;
10152
10153 fNeedsWrite = fAnySettingsChanged;
10154 }
10155 else
10156 fNeedsWrite = true;
10157 }
10158
10159 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10160
10161 if (fNeedsWrite)
10162 // now spit it all out!
10163 pNewConfig->write(mData->m_strConfigFileFull);
10164
10165 mData->pMachineConfigFile = pNewConfig;
10166 delete pOldConfig;
10167 i_commit();
10168
10169 // after saving settings, we are no longer different from the XML on disk
10170 mData->flModifications = 0;
10171 }
10172 catch (HRESULT err)
10173 {
10174 // we assume that error info is set by the thrower
10175 rc = err;
10176
10177 // restore old config
10178 delete pNewConfig;
10179 mData->pMachineConfigFile = pOldConfig;
10180 }
10181 catch (...)
10182 {
10183 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10184 }
10185
10186 if (fNeedsWrite)
10187 {
10188 /* Fire the data change event, even on failure (since we've already
10189 * committed all data). This is done only for SessionMachines because
10190 * mutable Machine instances are always not registered (i.e. private
10191 * to the client process that creates them) and thus don't need to
10192 * inform callbacks. */
10193 if (i_isSessionMachine())
10194 mParent->i_onMachineDataChange(mData->mUuid);
10195 }
10196
10197 LogFlowThisFunc(("rc=%08X\n", rc));
10198 LogFlowThisFuncLeave();
10199 return rc;
10200}
10201
10202/**
10203 * Implementation for saving the machine settings into the given
10204 * settings::MachineConfigFile instance. This copies machine extradata
10205 * from the previous machine config file in the instance data, if any.
10206 *
10207 * This gets called from two locations:
10208 *
10209 * -- Machine::i_saveSettings(), during the regular XML writing;
10210 *
10211 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10212 * exported to OVF and we write the VirtualBox proprietary XML
10213 * into a <vbox:Machine> tag.
10214 *
10215 * This routine fills all the fields in there, including snapshots, *except*
10216 * for the following:
10217 *
10218 * -- fCurrentStateModified. There is some special logic associated with that.
10219 *
10220 * The caller can then call MachineConfigFile::write() or do something else
10221 * with it.
10222 *
10223 * Caller must hold the machine lock!
10224 *
10225 * This throws XML errors and HRESULT, so the caller must have a catch block!
10226 */
10227void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10228{
10229 // deep copy extradata, being extra careful with self assignment (the STL
10230 // map assignment on Mac OS X clang based Xcode isn't checking)
10231 if (&config != mData->pMachineConfigFile)
10232 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10233
10234 config.uuid = mData->mUuid;
10235
10236 // copy name, description, OS type, teleport, UTC etc.
10237 config.machineUserData = mUserData->s;
10238
10239 if ( mData->mMachineState == MachineState_Saved
10240 || mData->mMachineState == MachineState_Restoring
10241 // when doing certain snapshot operations we may or may not have
10242 // a saved state in the current state, so keep everything as is
10243 || ( ( mData->mMachineState == MachineState_Snapshotting
10244 || mData->mMachineState == MachineState_DeletingSnapshot
10245 || mData->mMachineState == MachineState_RestoringSnapshot)
10246 && (!mSSData->strStateFilePath.isEmpty())
10247 )
10248 )
10249 {
10250 Assert(!mSSData->strStateFilePath.isEmpty());
10251 /* try to make the file name relative to the settings file dir */
10252 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10253 }
10254 else
10255 {
10256 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10257 config.strStateFile.setNull();
10258 }
10259
10260 if (mData->mCurrentSnapshot)
10261 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10262 else
10263 config.uuidCurrentSnapshot.clear();
10264
10265 config.timeLastStateChange = mData->mLastStateChange;
10266 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10267 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10268
10269 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10270 if (FAILED(rc)) throw rc;
10271
10272 // save machine's media registry if this is VirtualBox 4.0 or later
10273 if (config.canHaveOwnMediaRegistry())
10274 {
10275 // determine machine folder
10276 Utf8Str strMachineFolder = i_getSettingsFileFull();
10277 strMachineFolder.stripFilename();
10278 mParent->i_saveMediaRegistry(config.mediaRegistry,
10279 i_getId(), // only media with registry ID == machine UUID
10280 strMachineFolder);
10281 // this throws HRESULT
10282 }
10283
10284 // save snapshots
10285 rc = i_saveAllSnapshots(config);
10286 if (FAILED(rc)) throw rc;
10287}
10288
10289/**
10290 * Saves all snapshots of the machine into the given machine config file. Called
10291 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10292 * @param config
10293 * @return
10294 */
10295HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10296{
10297 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10298
10299 HRESULT rc = S_OK;
10300
10301 try
10302 {
10303 config.llFirstSnapshot.clear();
10304
10305 if (mData->mFirstSnapshot)
10306 {
10307 // the settings use a list for "the first snapshot"
10308 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10309
10310 // get reference to the snapshot on the list and work on that
10311 // element straight in the list to avoid excessive copying later
10312 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10313 if (FAILED(rc)) throw rc;
10314 }
10315
10316// if (mType == IsSessionMachine)
10317// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10318
10319 }
10320 catch (HRESULT err)
10321 {
10322 /* we assume that error info is set by the thrower */
10323 rc = err;
10324 }
10325 catch (...)
10326 {
10327 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10328 }
10329
10330 return rc;
10331}
10332
10333/**
10334 * Saves the VM hardware configuration. It is assumed that the
10335 * given node is empty.
10336 *
10337 * @param data Reference to the settings object for the hardware config.
10338 * @param pDbg Pointer to the settings object for the debugging config
10339 * which happens to live in mHWData.
10340 * @param pAutostart Pointer to the settings object for the autostart config
10341 * which happens to live in mHWData.
10342 */
10343HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10344 settings::Autostart *pAutostart)
10345{
10346 HRESULT rc = S_OK;
10347
10348 try
10349 {
10350 /* The hardware version attribute (optional).
10351 Automatically upgrade from 1 to current default hardware version
10352 when there is no saved state. (ugly!) */
10353 if ( mHWData->mHWVersion == "1"
10354 && mSSData->strStateFilePath.isEmpty()
10355 )
10356 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10357
10358 data.strVersion = mHWData->mHWVersion;
10359 data.uuid = mHWData->mHardwareUUID;
10360
10361 // CPU
10362 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10363 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10364 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10365 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10366 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10367 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10368 data.fPAE = !!mHWData->mPAEEnabled;
10369 data.enmLongMode = mHWData->mLongMode;
10370 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10371 data.fAPIC = !!mHWData->mAPIC;
10372 data.fX2APIC = !!mHWData->mX2APIC;
10373 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10374 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10375 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10376 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10377 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10378 data.cCPUs = mHWData->mCPUCount;
10379 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10380 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10381 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10382 data.strCpuProfile = mHWData->mCpuProfile;
10383
10384 data.llCpus.clear();
10385 if (data.fCpuHotPlug)
10386 {
10387 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10388 {
10389 if (mHWData->mCPUAttached[idx])
10390 {
10391 settings::Cpu cpu;
10392 cpu.ulId = idx;
10393 data.llCpus.push_back(cpu);
10394 }
10395 }
10396 }
10397
10398 /* Standard and Extended CPUID leafs. */
10399 data.llCpuIdLeafs.clear();
10400 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10401
10402 // memory
10403 data.ulMemorySizeMB = mHWData->mMemorySize;
10404 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10405
10406 // firmware
10407 data.firmwareType = mHWData->mFirmwareType;
10408
10409 // HID
10410 data.pointingHIDType = mHWData->mPointingHIDType;
10411 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10412
10413 // chipset
10414 data.chipsetType = mHWData->mChipsetType;
10415
10416 // paravirt
10417 data.paravirtProvider = mHWData->mParavirtProvider;
10418 data.strParavirtDebug = mHWData->mParavirtDebug;
10419
10420 // emulated USB card reader
10421 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10422
10423 // HPET
10424 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10425
10426 // boot order
10427 data.mapBootOrder.clear();
10428 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10429 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10430
10431 // display
10432 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10433 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10434 data.cMonitors = mHWData->mMonitorCount;
10435 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10436 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10437 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10438 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10439 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10440 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10441 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10442 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10443 {
10444 if (mHWData->maVideoCaptureScreens[i])
10445 ASMBitSet(&data.u64VideoCaptureScreens, i);
10446 else
10447 ASMBitClear(&data.u64VideoCaptureScreens, i);
10448 }
10449 /* store relative video capture file if possible */
10450 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10451 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10452
10453 /* VRDEServer settings (optional) */
10454 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10455 if (FAILED(rc)) throw rc;
10456
10457 /* BIOS (required) */
10458 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10459 if (FAILED(rc)) throw rc;
10460
10461 /* USB Controller (required) */
10462 data.usbSettings.llUSBControllers.clear();
10463 for (USBControllerList::const_iterator
10464 it = mUSBControllers->begin();
10465 it != mUSBControllers->end();
10466 ++it)
10467 {
10468 ComObjPtr<USBController> ctrl = *it;
10469 settings::USBController settingsCtrl;
10470
10471 settingsCtrl.strName = ctrl->i_getName();
10472 settingsCtrl.enmType = ctrl->i_getControllerType();
10473
10474 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10475 }
10476
10477 /* USB device filters (required) */
10478 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10479 if (FAILED(rc)) throw rc;
10480
10481 /* Network adapters (required) */
10482 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10483 data.llNetworkAdapters.clear();
10484 /* Write out only the nominal number of network adapters for this
10485 * chipset type. Since Machine::commit() hasn't been called there
10486 * may be extra NIC settings in the vector. */
10487 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10488 {
10489 settings::NetworkAdapter nic;
10490 nic.ulSlot = (uint32_t)slot;
10491 /* paranoia check... must not be NULL, but must not crash either. */
10492 if (mNetworkAdapters[slot])
10493 {
10494 if (mNetworkAdapters[slot]->i_hasDefaults())
10495 continue;
10496
10497 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10498 if (FAILED(rc)) throw rc;
10499
10500 data.llNetworkAdapters.push_back(nic);
10501 }
10502 }
10503
10504 /* Serial ports */
10505 data.llSerialPorts.clear();
10506 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10507 {
10508 if (mSerialPorts[slot]->i_hasDefaults())
10509 continue;
10510
10511 settings::SerialPort s;
10512 s.ulSlot = slot;
10513 rc = mSerialPorts[slot]->i_saveSettings(s);
10514 if (FAILED(rc)) return rc;
10515
10516 data.llSerialPorts.push_back(s);
10517 }
10518
10519 /* Parallel ports */
10520 data.llParallelPorts.clear();
10521 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10522 {
10523 if (mParallelPorts[slot]->i_hasDefaults())
10524 continue;
10525
10526 settings::ParallelPort p;
10527 p.ulSlot = slot;
10528 rc = mParallelPorts[slot]->i_saveSettings(p);
10529 if (FAILED(rc)) return rc;
10530
10531 data.llParallelPorts.push_back(p);
10532 }
10533
10534 /* Audio adapter */
10535 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10536 if (FAILED(rc)) return rc;
10537
10538 rc = i_saveStorageControllers(data.storage);
10539 if (FAILED(rc)) return rc;
10540
10541 /* Shared folders */
10542 data.llSharedFolders.clear();
10543 for (HWData::SharedFolderList::const_iterator
10544 it = mHWData->mSharedFolders.begin();
10545 it != mHWData->mSharedFolders.end();
10546 ++it)
10547 {
10548 SharedFolder *pSF = *it;
10549 AutoCaller sfCaller(pSF);
10550 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10551 settings::SharedFolder sf;
10552 sf.strName = pSF->i_getName();
10553 sf.strHostPath = pSF->i_getHostPath();
10554 sf.fWritable = !!pSF->i_isWritable();
10555 sf.fAutoMount = !!pSF->i_isAutoMounted();
10556
10557 data.llSharedFolders.push_back(sf);
10558 }
10559
10560 // clipboard
10561 data.clipboardMode = mHWData->mClipboardMode;
10562
10563 // drag'n'drop
10564 data.dndMode = mHWData->mDnDMode;
10565
10566 /* Guest */
10567 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10568
10569 // IO settings
10570 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10571 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10572
10573 /* BandwidthControl (required) */
10574 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10575 if (FAILED(rc)) throw rc;
10576
10577 /* Host PCI devices */
10578 data.pciAttachments.clear();
10579 for (HWData::PCIDeviceAssignmentList::const_iterator
10580 it = mHWData->mPCIDeviceAssignments.begin();
10581 it != mHWData->mPCIDeviceAssignments.end();
10582 ++it)
10583 {
10584 ComObjPtr<PCIDeviceAttachment> pda = *it;
10585 settings::HostPCIDeviceAttachment hpda;
10586
10587 rc = pda->i_saveSettings(hpda);
10588 if (FAILED(rc)) throw rc;
10589
10590 data.pciAttachments.push_back(hpda);
10591 }
10592
10593 // guest properties
10594 data.llGuestProperties.clear();
10595#ifdef VBOX_WITH_GUEST_PROPS
10596 for (HWData::GuestPropertyMap::const_iterator
10597 it = mHWData->mGuestProperties.begin();
10598 it != mHWData->mGuestProperties.end();
10599 ++it)
10600 {
10601 HWData::GuestProperty property = it->second;
10602
10603 /* Remove transient guest properties at shutdown unless we
10604 * are saving state. Note that restoring snapshot intentionally
10605 * keeps them, they will be removed if appropriate once the final
10606 * machine state is set (as crashes etc. need to work). */
10607 if ( ( mData->mMachineState == MachineState_PoweredOff
10608 || mData->mMachineState == MachineState_Aborted
10609 || mData->mMachineState == MachineState_Teleported)
10610 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10611 continue;
10612 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10613 prop.strName = it->first;
10614 prop.strValue = property.strValue;
10615 prop.timestamp = property.mTimestamp;
10616 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10617 GuestPropWriteFlags(property.mFlags, szFlags);
10618 prop.strFlags = szFlags;
10619
10620 data.llGuestProperties.push_back(prop);
10621 }
10622
10623 /* I presume this doesn't require a backup(). */
10624 mData->mGuestPropertiesModified = FALSE;
10625#endif /* VBOX_WITH_GUEST_PROPS defined */
10626
10627 *pDbg = mHWData->mDebugging;
10628 *pAutostart = mHWData->mAutostart;
10629
10630 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10631 }
10632 catch (std::bad_alloc &)
10633 {
10634 return E_OUTOFMEMORY;
10635 }
10636
10637 AssertComRC(rc);
10638 return rc;
10639}
10640
10641/**
10642 * Saves the storage controller configuration.
10643 *
10644 * @param data storage settings.
10645 */
10646HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10647{
10648 data.llStorageControllers.clear();
10649
10650 for (StorageControllerList::const_iterator
10651 it = mStorageControllers->begin();
10652 it != mStorageControllers->end();
10653 ++it)
10654 {
10655 HRESULT rc;
10656 ComObjPtr<StorageController> pCtl = *it;
10657
10658 settings::StorageController ctl;
10659 ctl.strName = pCtl->i_getName();
10660 ctl.controllerType = pCtl->i_getControllerType();
10661 ctl.storageBus = pCtl->i_getStorageBus();
10662 ctl.ulInstance = pCtl->i_getInstance();
10663 ctl.fBootable = pCtl->i_getBootable();
10664
10665 /* Save the port count. */
10666 ULONG portCount;
10667 rc = pCtl->COMGETTER(PortCount)(&portCount);
10668 ComAssertComRCRet(rc, rc);
10669 ctl.ulPortCount = portCount;
10670
10671 /* Save fUseHostIOCache */
10672 BOOL fUseHostIOCache;
10673 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10674 ComAssertComRCRet(rc, rc);
10675 ctl.fUseHostIOCache = !!fUseHostIOCache;
10676
10677 /* save the devices now. */
10678 rc = i_saveStorageDevices(pCtl, ctl);
10679 ComAssertComRCRet(rc, rc);
10680
10681 data.llStorageControllers.push_back(ctl);
10682 }
10683
10684 return S_OK;
10685}
10686
10687/**
10688 * Saves the hard disk configuration.
10689 */
10690HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10691 settings::StorageController &data)
10692{
10693 MediumAttachmentList atts;
10694
10695 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10696 if (FAILED(rc)) return rc;
10697
10698 data.llAttachedDevices.clear();
10699 for (MediumAttachmentList::const_iterator
10700 it = atts.begin();
10701 it != atts.end();
10702 ++it)
10703 {
10704 settings::AttachedDevice dev;
10705 IMediumAttachment *iA = *it;
10706 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10707 Medium *pMedium = pAttach->i_getMedium();
10708
10709 dev.deviceType = pAttach->i_getType();
10710 dev.lPort = pAttach->i_getPort();
10711 dev.lDevice = pAttach->i_getDevice();
10712 dev.fPassThrough = pAttach->i_getPassthrough();
10713 dev.fHotPluggable = pAttach->i_getHotPluggable();
10714 if (pMedium)
10715 {
10716 if (pMedium->i_isHostDrive())
10717 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10718 else
10719 dev.uuid = pMedium->i_getId();
10720 dev.fTempEject = pAttach->i_getTempEject();
10721 dev.fNonRotational = pAttach->i_getNonRotational();
10722 dev.fDiscard = pAttach->i_getDiscard();
10723 }
10724
10725 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10726
10727 data.llAttachedDevices.push_back(dev);
10728 }
10729
10730 return S_OK;
10731}
10732
10733/**
10734 * Saves machine state settings as defined by aFlags
10735 * (SaveSTS_* values).
10736 *
10737 * @param aFlags Combination of SaveSTS_* flags.
10738 *
10739 * @note Locks objects for writing.
10740 */
10741HRESULT Machine::i_saveStateSettings(int aFlags)
10742{
10743 if (aFlags == 0)
10744 return S_OK;
10745
10746 AutoCaller autoCaller(this);
10747 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10748
10749 /* This object's write lock is also necessary to serialize file access
10750 * (prevent concurrent reads and writes) */
10751 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10752
10753 HRESULT rc = S_OK;
10754
10755 Assert(mData->pMachineConfigFile);
10756
10757 try
10758 {
10759 if (aFlags & SaveSTS_CurStateModified)
10760 mData->pMachineConfigFile->fCurrentStateModified = true;
10761
10762 if (aFlags & SaveSTS_StateFilePath)
10763 {
10764 if (!mSSData->strStateFilePath.isEmpty())
10765 /* try to make the file name relative to the settings file dir */
10766 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10767 else
10768 mData->pMachineConfigFile->strStateFile.setNull();
10769 }
10770
10771 if (aFlags & SaveSTS_StateTimeStamp)
10772 {
10773 Assert( mData->mMachineState != MachineState_Aborted
10774 || mSSData->strStateFilePath.isEmpty());
10775
10776 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10777
10778 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10779/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10780 }
10781
10782 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10783 }
10784 catch (...)
10785 {
10786 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10787 }
10788
10789 return rc;
10790}
10791
10792/**
10793 * Ensures that the given medium is added to a media registry. If this machine
10794 * was created with 4.0 or later, then the machine registry is used. Otherwise
10795 * the global VirtualBox media registry is used.
10796 *
10797 * Caller must NOT hold machine lock, media tree or any medium locks!
10798 *
10799 * @param pMedium
10800 */
10801void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10802{
10803 /* Paranoia checks: do not hold machine or media tree locks. */
10804 AssertReturnVoid(!isWriteLockOnCurrentThread());
10805 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10806
10807 ComObjPtr<Medium> pBase;
10808 {
10809 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10810 pBase = pMedium->i_getBase();
10811 }
10812
10813 /* Paranoia checks: do not hold medium locks. */
10814 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10815 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10816
10817 // decide which medium registry to use now that the medium is attached:
10818 Guid uuid;
10819 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10820 if (fCanHaveOwnMediaRegistry)
10821 // machine XML is VirtualBox 4.0 or higher:
10822 uuid = i_getId(); // machine UUID
10823 else
10824 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10825
10826 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10827 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10828 if (pMedium->i_addRegistry(uuid))
10829 mParent->i_markRegistryModified(uuid);
10830
10831 /* For more complex hard disk structures it can happen that the base
10832 * medium isn't yet associated with any medium registry. Do that now. */
10833 if (pMedium != pBase)
10834 {
10835 /* Tree lock needed by Medium::addRegistry when recursing. */
10836 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10837 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10838 {
10839 treeLock.release();
10840 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10841 treeLock.acquire();
10842 }
10843 if (pBase->i_addRegistryRecursive(uuid))
10844 {
10845 treeLock.release();
10846 mParent->i_markRegistryModified(uuid);
10847 }
10848 }
10849}
10850
10851/**
10852 * Creates differencing hard disks for all normal hard disks attached to this
10853 * machine and a new set of attachments to refer to created disks.
10854 *
10855 * Used when taking a snapshot or when deleting the current state. Gets called
10856 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10857 *
10858 * This method assumes that mMediumAttachments contains the original hard disk
10859 * attachments it needs to create diffs for. On success, these attachments will
10860 * be replaced with the created diffs.
10861 *
10862 * Attachments with non-normal hard disks are left as is.
10863 *
10864 * If @a aOnline is @c false then the original hard disks that require implicit
10865 * diffs will be locked for reading. Otherwise it is assumed that they are
10866 * already locked for writing (when the VM was started). Note that in the latter
10867 * case it is responsibility of the caller to lock the newly created diffs for
10868 * writing if this method succeeds.
10869 *
10870 * @param aProgress Progress object to run (must contain at least as
10871 * many operations left as the number of hard disks
10872 * attached).
10873 * @param aWeight Weight of this operation.
10874 * @param aOnline Whether the VM was online prior to this operation.
10875 *
10876 * @note The progress object is not marked as completed, neither on success nor
10877 * on failure. This is a responsibility of the caller.
10878 *
10879 * @note Locks this object and the media tree for writing.
10880 */
10881HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10882 ULONG aWeight,
10883 bool aOnline)
10884{
10885 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10886
10887 AutoCaller autoCaller(this);
10888 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10889
10890 AutoMultiWriteLock2 alock(this->lockHandle(),
10891 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10892
10893 /* must be in a protective state because we release the lock below */
10894 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10895 || mData->mMachineState == MachineState_OnlineSnapshotting
10896 || mData->mMachineState == MachineState_LiveSnapshotting
10897 || mData->mMachineState == MachineState_RestoringSnapshot
10898 || mData->mMachineState == MachineState_DeletingSnapshot
10899 , E_FAIL);
10900
10901 HRESULT rc = S_OK;
10902
10903 // use appropriate locked media map (online or offline)
10904 MediumLockListMap lockedMediaOffline;
10905 MediumLockListMap *lockedMediaMap;
10906 if (aOnline)
10907 lockedMediaMap = &mData->mSession.mLockedMedia;
10908 else
10909 lockedMediaMap = &lockedMediaOffline;
10910
10911 try
10912 {
10913 if (!aOnline)
10914 {
10915 /* lock all attached hard disks early to detect "in use"
10916 * situations before creating actual diffs */
10917 for (MediumAttachmentList::const_iterator
10918 it = mMediumAttachments->begin();
10919 it != mMediumAttachments->end();
10920 ++it)
10921 {
10922 MediumAttachment *pAtt = *it;
10923 if (pAtt->i_getType() == DeviceType_HardDisk)
10924 {
10925 Medium *pMedium = pAtt->i_getMedium();
10926 Assert(pMedium);
10927
10928 MediumLockList *pMediumLockList(new MediumLockList());
10929 alock.release();
10930 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10931 NULL /* pToLockWrite */,
10932 false /* fMediumLockWriteAll */,
10933 NULL,
10934 *pMediumLockList);
10935 alock.acquire();
10936 if (FAILED(rc))
10937 {
10938 delete pMediumLockList;
10939 throw rc;
10940 }
10941 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10942 if (FAILED(rc))
10943 {
10944 throw setError(rc,
10945 tr("Collecting locking information for all attached media failed"));
10946 }
10947 }
10948 }
10949
10950 /* Now lock all media. If this fails, nothing is locked. */
10951 alock.release();
10952 rc = lockedMediaMap->Lock();
10953 alock.acquire();
10954 if (FAILED(rc))
10955 {
10956 throw setError(rc,
10957 tr("Locking of attached media failed"));
10958 }
10959 }
10960
10961 /* remember the current list (note that we don't use backup() since
10962 * mMediumAttachments may be already backed up) */
10963 MediumAttachmentList atts = *mMediumAttachments.data();
10964
10965 /* start from scratch */
10966 mMediumAttachments->clear();
10967
10968 /* go through remembered attachments and create diffs for normal hard
10969 * disks and attach them */
10970 for (MediumAttachmentList::const_iterator
10971 it = atts.begin();
10972 it != atts.end();
10973 ++it)
10974 {
10975 MediumAttachment *pAtt = *it;
10976
10977 DeviceType_T devType = pAtt->i_getType();
10978 Medium *pMedium = pAtt->i_getMedium();
10979
10980 if ( devType != DeviceType_HardDisk
10981 || pMedium == NULL
10982 || pMedium->i_getType() != MediumType_Normal)
10983 {
10984 /* copy the attachment as is */
10985
10986 /** @todo the progress object created in SessionMachine::TakeSnaphot
10987 * only expects operations for hard disks. Later other
10988 * device types need to show up in the progress as well. */
10989 if (devType == DeviceType_HardDisk)
10990 {
10991 if (pMedium == NULL)
10992 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10993 aWeight); // weight
10994 else
10995 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10996 pMedium->i_getBase()->i_getName().c_str()).raw(),
10997 aWeight); // weight
10998 }
10999
11000 mMediumAttachments->push_back(pAtt);
11001 continue;
11002 }
11003
11004 /* need a diff */
11005 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11006 pMedium->i_getBase()->i_getName().c_str()).raw(),
11007 aWeight); // weight
11008
11009 Utf8Str strFullSnapshotFolder;
11010 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11011
11012 ComObjPtr<Medium> diff;
11013 diff.createObject();
11014 // store the diff in the same registry as the parent
11015 // (this cannot fail here because we can't create implicit diffs for
11016 // unregistered images)
11017 Guid uuidRegistryParent;
11018 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11019 Assert(fInRegistry); NOREF(fInRegistry);
11020 rc = diff->init(mParent,
11021 pMedium->i_getPreferredDiffFormat(),
11022 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11023 uuidRegistryParent,
11024 DeviceType_HardDisk);
11025 if (FAILED(rc)) throw rc;
11026
11027 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11028 * the push_back? Looks like we're going to release medium with the
11029 * wrong kind of lock (general issue with if we fail anywhere at all)
11030 * and an orphaned VDI in the snapshots folder. */
11031
11032 /* update the appropriate lock list */
11033 MediumLockList *pMediumLockList;
11034 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11035 AssertComRCThrowRC(rc);
11036 if (aOnline)
11037 {
11038 alock.release();
11039 /* The currently attached medium will be read-only, change
11040 * the lock type to read. */
11041 rc = pMediumLockList->Update(pMedium, false);
11042 alock.acquire();
11043 AssertComRCThrowRC(rc);
11044 }
11045
11046 /* release the locks before the potentially lengthy operation */
11047 alock.release();
11048 rc = pMedium->i_createDiffStorage(diff,
11049 pMedium->i_getPreferredDiffVariant(),
11050 pMediumLockList,
11051 NULL /* aProgress */,
11052 true /* aWait */);
11053 alock.acquire();
11054 if (FAILED(rc)) throw rc;
11055
11056 /* actual lock list update is done in Machine::i_commitMedia */
11057
11058 rc = diff->i_addBackReference(mData->mUuid);
11059 AssertComRCThrowRC(rc);
11060
11061 /* add a new attachment */
11062 ComObjPtr<MediumAttachment> attachment;
11063 attachment.createObject();
11064 rc = attachment->init(this,
11065 diff,
11066 pAtt->i_getControllerName(),
11067 pAtt->i_getPort(),
11068 pAtt->i_getDevice(),
11069 DeviceType_HardDisk,
11070 true /* aImplicit */,
11071 false /* aPassthrough */,
11072 false /* aTempEject */,
11073 pAtt->i_getNonRotational(),
11074 pAtt->i_getDiscard(),
11075 pAtt->i_getHotPluggable(),
11076 pAtt->i_getBandwidthGroup());
11077 if (FAILED(rc)) throw rc;
11078
11079 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11080 AssertComRCThrowRC(rc);
11081 mMediumAttachments->push_back(attachment);
11082 }
11083 }
11084 catch (HRESULT aRC) { rc = aRC; }
11085
11086 /* unlock all hard disks we locked when there is no VM */
11087 if (!aOnline)
11088 {
11089 ErrorInfoKeeper eik;
11090
11091 HRESULT rc1 = lockedMediaMap->Clear();
11092 AssertComRC(rc1);
11093 }
11094
11095 return rc;
11096}
11097
11098/**
11099 * Deletes implicit differencing hard disks created either by
11100 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11101 * mMediumAttachments.
11102 *
11103 * Note that to delete hard disks created by #attachDevice() this method is
11104 * called from #i_rollbackMedia() when the changes are rolled back.
11105 *
11106 * @note Locks this object and the media tree for writing.
11107 */
11108HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11109{
11110 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11111
11112 AutoCaller autoCaller(this);
11113 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11114
11115 AutoMultiWriteLock2 alock(this->lockHandle(),
11116 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11117
11118 /* We absolutely must have backed up state. */
11119 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11120
11121 /* Check if there are any implicitly created diff images. */
11122 bool fImplicitDiffs = false;
11123 for (MediumAttachmentList::const_iterator
11124 it = mMediumAttachments->begin();
11125 it != mMediumAttachments->end();
11126 ++it)
11127 {
11128 const ComObjPtr<MediumAttachment> &pAtt = *it;
11129 if (pAtt->i_isImplicit())
11130 {
11131 fImplicitDiffs = true;
11132 break;
11133 }
11134 }
11135 /* If there is nothing to do, leave early. This saves lots of image locking
11136 * effort. It also avoids a MachineStateChanged event without real reason.
11137 * This is important e.g. when loading a VM config, because there should be
11138 * no events. Otherwise API clients can become thoroughly confused for
11139 * inaccessible VMs (the code for loading VM configs uses this method for
11140 * cleanup if the config makes no sense), as they take such events as an
11141 * indication that the VM is alive, and they would force the VM config to
11142 * be reread, leading to an endless loop. */
11143 if (!fImplicitDiffs)
11144 return S_OK;
11145
11146 HRESULT rc = S_OK;
11147 MachineState_T oldState = mData->mMachineState;
11148
11149 /* will release the lock before the potentially lengthy operation,
11150 * so protect with the special state (unless already protected) */
11151 if ( oldState != MachineState_Snapshotting
11152 && oldState != MachineState_OnlineSnapshotting
11153 && oldState != MachineState_LiveSnapshotting
11154 && oldState != MachineState_RestoringSnapshot
11155 && oldState != MachineState_DeletingSnapshot
11156 && oldState != MachineState_DeletingSnapshotOnline
11157 && oldState != MachineState_DeletingSnapshotPaused
11158 )
11159 i_setMachineState(MachineState_SettingUp);
11160
11161 // use appropriate locked media map (online or offline)
11162 MediumLockListMap lockedMediaOffline;
11163 MediumLockListMap *lockedMediaMap;
11164 if (aOnline)
11165 lockedMediaMap = &mData->mSession.mLockedMedia;
11166 else
11167 lockedMediaMap = &lockedMediaOffline;
11168
11169 try
11170 {
11171 if (!aOnline)
11172 {
11173 /* lock all attached hard disks early to detect "in use"
11174 * situations before deleting actual diffs */
11175 for (MediumAttachmentList::const_iterator
11176 it = mMediumAttachments->begin();
11177 it != mMediumAttachments->end();
11178 ++it)
11179 {
11180 MediumAttachment *pAtt = *it;
11181 if (pAtt->i_getType() == DeviceType_HardDisk)
11182 {
11183 Medium *pMedium = pAtt->i_getMedium();
11184 Assert(pMedium);
11185
11186 MediumLockList *pMediumLockList(new MediumLockList());
11187 alock.release();
11188 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11189 NULL /* pToLockWrite */,
11190 false /* fMediumLockWriteAll */,
11191 NULL,
11192 *pMediumLockList);
11193 alock.acquire();
11194
11195 if (FAILED(rc))
11196 {
11197 delete pMediumLockList;
11198 throw rc;
11199 }
11200
11201 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11202 if (FAILED(rc))
11203 throw rc;
11204 }
11205 }
11206
11207 if (FAILED(rc))
11208 throw rc;
11209 } // end of offline
11210
11211 /* Lock lists are now up to date and include implicitly created media */
11212
11213 /* Go through remembered attachments and delete all implicitly created
11214 * diffs and fix up the attachment information */
11215 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11216 MediumAttachmentList implicitAtts;
11217 for (MediumAttachmentList::const_iterator
11218 it = mMediumAttachments->begin();
11219 it != mMediumAttachments->end();
11220 ++it)
11221 {
11222 ComObjPtr<MediumAttachment> pAtt = *it;
11223 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11224 if (pMedium.isNull())
11225 continue;
11226
11227 // Implicit attachments go on the list for deletion and back references are removed.
11228 if (pAtt->i_isImplicit())
11229 {
11230 /* Deassociate and mark for deletion */
11231 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11232 rc = pMedium->i_removeBackReference(mData->mUuid);
11233 if (FAILED(rc))
11234 throw rc;
11235 implicitAtts.push_back(pAtt);
11236 continue;
11237 }
11238
11239 /* Was this medium attached before? */
11240 if (!i_findAttachment(oldAtts, pMedium))
11241 {
11242 /* no: de-associate */
11243 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11244 rc = pMedium->i_removeBackReference(mData->mUuid);
11245 if (FAILED(rc))
11246 throw rc;
11247 continue;
11248 }
11249 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11250 }
11251
11252 /* If there are implicit attachments to delete, throw away the lock
11253 * map contents (which will unlock all media) since the medium
11254 * attachments will be rolled back. Below we need to completely
11255 * recreate the lock map anyway since it is infinitely complex to
11256 * do this incrementally (would need reconstructing each attachment
11257 * change, which would be extremely hairy). */
11258 if (implicitAtts.size() != 0)
11259 {
11260 ErrorInfoKeeper eik;
11261
11262 HRESULT rc1 = lockedMediaMap->Clear();
11263 AssertComRC(rc1);
11264 }
11265
11266 /* rollback hard disk changes */
11267 mMediumAttachments.rollback();
11268
11269 MultiResult mrc(S_OK);
11270
11271 // Delete unused implicit diffs.
11272 if (implicitAtts.size() != 0)
11273 {
11274 alock.release();
11275
11276 for (MediumAttachmentList::const_iterator
11277 it = implicitAtts.begin();
11278 it != implicitAtts.end();
11279 ++it)
11280 {
11281 // Remove medium associated with this attachment.
11282 ComObjPtr<MediumAttachment> pAtt = *it;
11283 Assert(pAtt);
11284 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11285 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11286 Assert(pMedium);
11287
11288 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11289 // continue on delete failure, just collect error messages
11290 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11291 pMedium->i_getLocationFull().c_str() ));
11292 mrc = rc;
11293 }
11294 // Clear the list of deleted implicit attachments now, while not
11295 // holding the lock, as it will ultimately trigger Medium::uninit()
11296 // calls which assume that the media tree lock isn't held.
11297 implicitAtts.clear();
11298
11299 alock.acquire();
11300
11301 /* if there is a VM recreate media lock map as mentioned above,
11302 * otherwise it is a waste of time and we leave things unlocked */
11303 if (aOnline)
11304 {
11305 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11306 /* must never be NULL, but better safe than sorry */
11307 if (!pMachine.isNull())
11308 {
11309 alock.release();
11310 rc = mData->mSession.mMachine->i_lockMedia();
11311 alock.acquire();
11312 if (FAILED(rc))
11313 throw rc;
11314 }
11315 }
11316 }
11317 }
11318 catch (HRESULT aRC) {rc = aRC;}
11319
11320 if (mData->mMachineState == MachineState_SettingUp)
11321 i_setMachineState(oldState);
11322
11323 /* unlock all hard disks we locked when there is no VM */
11324 if (!aOnline)
11325 {
11326 ErrorInfoKeeper eik;
11327
11328 HRESULT rc1 = lockedMediaMap->Clear();
11329 AssertComRC(rc1);
11330 }
11331
11332 return rc;
11333}
11334
11335
11336/**
11337 * Looks through the given list of media attachments for one with the given parameters
11338 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11339 * can be searched as well if needed.
11340 *
11341 * @param ll
11342 * @param aControllerName
11343 * @param aControllerPort
11344 * @param aDevice
11345 * @return
11346 */
11347MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11348 const Utf8Str &aControllerName,
11349 LONG aControllerPort,
11350 LONG aDevice)
11351{
11352 for (MediumAttachmentList::const_iterator
11353 it = ll.begin();
11354 it != ll.end();
11355 ++it)
11356 {
11357 MediumAttachment *pAttach = *it;
11358 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11359 return pAttach;
11360 }
11361
11362 return NULL;
11363}
11364
11365/**
11366 * Looks through the given list of media attachments for one with the given parameters
11367 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11368 * can be searched as well if needed.
11369 *
11370 * @param ll
11371 * @param pMedium
11372 * @return
11373 */
11374MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11375 ComObjPtr<Medium> pMedium)
11376{
11377 for (MediumAttachmentList::const_iterator
11378 it = ll.begin();
11379 it != ll.end();
11380 ++it)
11381 {
11382 MediumAttachment *pAttach = *it;
11383 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11384 if (pMediumThis == pMedium)
11385 return pAttach;
11386 }
11387
11388 return NULL;
11389}
11390
11391/**
11392 * Looks through the given list of media attachments for one with the given parameters
11393 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11394 * can be searched as well if needed.
11395 *
11396 * @param ll
11397 * @param id
11398 * @return
11399 */
11400MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11401 Guid &id)
11402{
11403 for (MediumAttachmentList::const_iterator
11404 it = ll.begin();
11405 it != ll.end();
11406 ++it)
11407 {
11408 MediumAttachment *pAttach = *it;
11409 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11410 if (pMediumThis->i_getId() == id)
11411 return pAttach;
11412 }
11413
11414 return NULL;
11415}
11416
11417/**
11418 * Main implementation for Machine::DetachDevice. This also gets called
11419 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11420 *
11421 * @param pAttach Medium attachment to detach.
11422 * @param writeLock Machine write lock which the caller must have locked once.
11423 * This may be released temporarily in here.
11424 * @param pSnapshot If NULL, then the detachment is for the current machine.
11425 * Otherwise this is for a SnapshotMachine, and this must be
11426 * its snapshot.
11427 * @return
11428 */
11429HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11430 AutoWriteLock &writeLock,
11431 Snapshot *pSnapshot)
11432{
11433 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11434 DeviceType_T mediumType = pAttach->i_getType();
11435
11436 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11437
11438 if (pAttach->i_isImplicit())
11439 {
11440 /* attempt to implicitly delete the implicitly created diff */
11441
11442 /// @todo move the implicit flag from MediumAttachment to Medium
11443 /// and forbid any hard disk operation when it is implicit. Or maybe
11444 /// a special media state for it to make it even more simple.
11445
11446 Assert(mMediumAttachments.isBackedUp());
11447
11448 /* will release the lock before the potentially lengthy operation, so
11449 * protect with the special state */
11450 MachineState_T oldState = mData->mMachineState;
11451 i_setMachineState(MachineState_SettingUp);
11452
11453 writeLock.release();
11454
11455 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11456 true /*aWait*/);
11457
11458 writeLock.acquire();
11459
11460 i_setMachineState(oldState);
11461
11462 if (FAILED(rc)) return rc;
11463 }
11464
11465 i_setModified(IsModified_Storage);
11466 mMediumAttachments.backup();
11467 mMediumAttachments->remove(pAttach);
11468
11469 if (!oldmedium.isNull())
11470 {
11471 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11472 if (pSnapshot)
11473 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11474 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11475 else if (mediumType != DeviceType_HardDisk)
11476 oldmedium->i_removeBackReference(mData->mUuid);
11477 }
11478
11479 return S_OK;
11480}
11481
11482/**
11483 * Goes thru all media of the given list and
11484 *
11485 * 1) calls i_detachDevice() on each of them for this machine and
11486 * 2) adds all Medium objects found in the process to the given list,
11487 * depending on cleanupMode.
11488 *
11489 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11490 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11491 * media to the list.
11492 *
11493 * This gets called from Machine::Unregister, both for the actual Machine and
11494 * the SnapshotMachine objects that might be found in the snapshots.
11495 *
11496 * Requires caller and locking. The machine lock must be passed in because it
11497 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11498 *
11499 * @param writeLock Machine lock from top-level caller; this gets passed to
11500 * i_detachDevice.
11501 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11502 * object if called for a SnapshotMachine.
11503 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11504 * added to llMedia; if Full, then all media get added;
11505 * otherwise no media get added.
11506 * @param llMedia Caller's list to receive Medium objects which got detached so
11507 * caller can close() them, depending on cleanupMode.
11508 * @return
11509 */
11510HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11511 Snapshot *pSnapshot,
11512 CleanupMode_T cleanupMode,
11513 MediaList &llMedia)
11514{
11515 Assert(isWriteLockOnCurrentThread());
11516
11517 HRESULT rc;
11518
11519 // make a temporary list because i_detachDevice invalidates iterators into
11520 // mMediumAttachments
11521 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11522
11523 for (MediumAttachmentList::iterator
11524 it = llAttachments2.begin();
11525 it != llAttachments2.end();
11526 ++it)
11527 {
11528 ComObjPtr<MediumAttachment> &pAttach = *it;
11529 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11530
11531 if (!pMedium.isNull())
11532 {
11533 AutoCaller mac(pMedium);
11534 if (FAILED(mac.rc())) return mac.rc();
11535 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11536 DeviceType_T devType = pMedium->i_getDeviceType();
11537 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11538 && devType == DeviceType_HardDisk)
11539 || (cleanupMode == CleanupMode_Full)
11540 )
11541 {
11542 llMedia.push_back(pMedium);
11543 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11544 /* Not allowed to keep this lock as below we need the parent
11545 * medium lock, and the lock order is parent to child. */
11546 lock.release();
11547 /*
11548 * Search for medias which are not attached to any machine, but
11549 * in the chain to an attached disk. Mediums are only consided
11550 * if they are:
11551 * - have only one child
11552 * - no references to any machines
11553 * - are of normal medium type
11554 */
11555 while (!pParent.isNull())
11556 {
11557 AutoCaller mac1(pParent);
11558 if (FAILED(mac1.rc())) return mac1.rc();
11559 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11560 if (pParent->i_getChildren().size() == 1)
11561 {
11562 if ( pParent->i_getMachineBackRefCount() == 0
11563 && pParent->i_getType() == MediumType_Normal
11564 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11565 llMedia.push_back(pParent);
11566 }
11567 else
11568 break;
11569 pParent = pParent->i_getParent();
11570 }
11571 }
11572 }
11573
11574 // real machine: then we need to use the proper method
11575 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11576
11577 if (FAILED(rc))
11578 return rc;
11579 }
11580
11581 return S_OK;
11582}
11583
11584/**
11585 * Perform deferred hard disk detachments.
11586 *
11587 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11588 * changed (not backed up).
11589 *
11590 * If @a aOnline is @c true then this method will also unlock the old hard
11591 * disks for which the new implicit diffs were created and will lock these new
11592 * diffs for writing.
11593 *
11594 * @param aOnline Whether the VM was online prior to this operation.
11595 *
11596 * @note Locks this object for writing!
11597 */
11598void Machine::i_commitMedia(bool aOnline /*= false*/)
11599{
11600 AutoCaller autoCaller(this);
11601 AssertComRCReturnVoid(autoCaller.rc());
11602
11603 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11604
11605 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11606
11607 HRESULT rc = S_OK;
11608
11609 /* no attach/detach operations -- nothing to do */
11610 if (!mMediumAttachments.isBackedUp())
11611 return;
11612
11613 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11614 bool fMediaNeedsLocking = false;
11615
11616 /* enumerate new attachments */
11617 for (MediumAttachmentList::const_iterator
11618 it = mMediumAttachments->begin();
11619 it != mMediumAttachments->end();
11620 ++it)
11621 {
11622 MediumAttachment *pAttach = *it;
11623
11624 pAttach->i_commit();
11625
11626 Medium *pMedium = pAttach->i_getMedium();
11627 bool fImplicit = pAttach->i_isImplicit();
11628
11629 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11630 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11631 fImplicit));
11632
11633 /** @todo convert all this Machine-based voodoo to MediumAttachment
11634 * based commit logic. */
11635 if (fImplicit)
11636 {
11637 /* convert implicit attachment to normal */
11638 pAttach->i_setImplicit(false);
11639
11640 if ( aOnline
11641 && pMedium
11642 && pAttach->i_getType() == DeviceType_HardDisk
11643 )
11644 {
11645 /* update the appropriate lock list */
11646 MediumLockList *pMediumLockList;
11647 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11648 AssertComRC(rc);
11649 if (pMediumLockList)
11650 {
11651 /* unlock if there's a need to change the locking */
11652 if (!fMediaNeedsLocking)
11653 {
11654 rc = mData->mSession.mLockedMedia.Unlock();
11655 AssertComRC(rc);
11656 fMediaNeedsLocking = true;
11657 }
11658 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11659 AssertComRC(rc);
11660 rc = pMediumLockList->Append(pMedium, true);
11661 AssertComRC(rc);
11662 }
11663 }
11664
11665 continue;
11666 }
11667
11668 if (pMedium)
11669 {
11670 /* was this medium attached before? */
11671 for (MediumAttachmentList::iterator
11672 oldIt = oldAtts.begin();
11673 oldIt != oldAtts.end();
11674 ++oldIt)
11675 {
11676 MediumAttachment *pOldAttach = *oldIt;
11677 if (pOldAttach->i_getMedium() == pMedium)
11678 {
11679 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11680
11681 /* yes: remove from old to avoid de-association */
11682 oldAtts.erase(oldIt);
11683 break;
11684 }
11685 }
11686 }
11687 }
11688
11689 /* enumerate remaining old attachments and de-associate from the
11690 * current machine state */
11691 for (MediumAttachmentList::const_iterator
11692 it = oldAtts.begin();
11693 it != oldAtts.end();
11694 ++it)
11695 {
11696 MediumAttachment *pAttach = *it;
11697 Medium *pMedium = pAttach->i_getMedium();
11698
11699 /* Detach only hard disks, since DVD/floppy media is detached
11700 * instantly in MountMedium. */
11701 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11702 {
11703 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11704
11705 /* now de-associate from the current machine state */
11706 rc = pMedium->i_removeBackReference(mData->mUuid);
11707 AssertComRC(rc);
11708
11709 if (aOnline)
11710 {
11711 /* unlock since medium is not used anymore */
11712 MediumLockList *pMediumLockList;
11713 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11714 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11715 {
11716 /* this happens for online snapshots, there the attachment
11717 * is changing, but only to a diff image created under
11718 * the old one, so there is no separate lock list */
11719 Assert(!pMediumLockList);
11720 }
11721 else
11722 {
11723 AssertComRC(rc);
11724 if (pMediumLockList)
11725 {
11726 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11727 AssertComRC(rc);
11728 }
11729 }
11730 }
11731 }
11732 }
11733
11734 /* take media locks again so that the locking state is consistent */
11735 if (fMediaNeedsLocking)
11736 {
11737 Assert(aOnline);
11738 rc = mData->mSession.mLockedMedia.Lock();
11739 AssertComRC(rc);
11740 }
11741
11742 /* commit the hard disk changes */
11743 mMediumAttachments.commit();
11744
11745 if (i_isSessionMachine())
11746 {
11747 /*
11748 * Update the parent machine to point to the new owner.
11749 * This is necessary because the stored parent will point to the
11750 * session machine otherwise and cause crashes or errors later
11751 * when the session machine gets invalid.
11752 */
11753 /** @todo Change the MediumAttachment class to behave like any other
11754 * class in this regard by creating peer MediumAttachment
11755 * objects for session machines and share the data with the peer
11756 * machine.
11757 */
11758 for (MediumAttachmentList::const_iterator
11759 it = mMediumAttachments->begin();
11760 it != mMediumAttachments->end();
11761 ++it)
11762 (*it)->i_updateParentMachine(mPeer);
11763
11764 /* attach new data to the primary machine and reshare it */
11765 mPeer->mMediumAttachments.attach(mMediumAttachments);
11766 }
11767
11768 return;
11769}
11770
11771/**
11772 * Perform deferred deletion of implicitly created diffs.
11773 *
11774 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11775 * changed (not backed up).
11776 *
11777 * @note Locks this object for writing!
11778 */
11779void Machine::i_rollbackMedia()
11780{
11781 AutoCaller autoCaller(this);
11782 AssertComRCReturnVoid(autoCaller.rc());
11783
11784 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11785 LogFlowThisFunc(("Entering rollbackMedia\n"));
11786
11787 HRESULT rc = S_OK;
11788
11789 /* no attach/detach operations -- nothing to do */
11790 if (!mMediumAttachments.isBackedUp())
11791 return;
11792
11793 /* enumerate new attachments */
11794 for (MediumAttachmentList::const_iterator
11795 it = mMediumAttachments->begin();
11796 it != mMediumAttachments->end();
11797 ++it)
11798 {
11799 MediumAttachment *pAttach = *it;
11800 /* Fix up the backrefs for DVD/floppy media. */
11801 if (pAttach->i_getType() != DeviceType_HardDisk)
11802 {
11803 Medium *pMedium = pAttach->i_getMedium();
11804 if (pMedium)
11805 {
11806 rc = pMedium->i_removeBackReference(mData->mUuid);
11807 AssertComRC(rc);
11808 }
11809 }
11810
11811 (*it)->i_rollback();
11812
11813 pAttach = *it;
11814 /* Fix up the backrefs for DVD/floppy media. */
11815 if (pAttach->i_getType() != DeviceType_HardDisk)
11816 {
11817 Medium *pMedium = pAttach->i_getMedium();
11818 if (pMedium)
11819 {
11820 rc = pMedium->i_addBackReference(mData->mUuid);
11821 AssertComRC(rc);
11822 }
11823 }
11824 }
11825
11826 /** @todo convert all this Machine-based voodoo to MediumAttachment
11827 * based rollback logic. */
11828 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11829
11830 return;
11831}
11832
11833/**
11834 * Returns true if the settings file is located in the directory named exactly
11835 * as the machine; this means, among other things, that the machine directory
11836 * should be auto-renamed.
11837 *
11838 * @param aSettingsDir if not NULL, the full machine settings file directory
11839 * name will be assigned there.
11840 *
11841 * @note Doesn't lock anything.
11842 * @note Not thread safe (must be called from this object's lock).
11843 */
11844bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11845{
11846 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11847 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11848 if (aSettingsDir)
11849 *aSettingsDir = strMachineDirName;
11850 strMachineDirName.stripPath(); // vmname
11851 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11852 strConfigFileOnly.stripPath() // vmname.vbox
11853 .stripSuffix(); // vmname
11854 /** @todo hack, make somehow use of ComposeMachineFilename */
11855 if (mUserData->s.fDirectoryIncludesUUID)
11856 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11857
11858 AssertReturn(!strMachineDirName.isEmpty(), false);
11859 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11860
11861 return strMachineDirName == strConfigFileOnly;
11862}
11863
11864/**
11865 * Discards all changes to machine settings.
11866 *
11867 * @param aNotify Whether to notify the direct session about changes or not.
11868 *
11869 * @note Locks objects for writing!
11870 */
11871void Machine::i_rollback(bool aNotify)
11872{
11873 AutoCaller autoCaller(this);
11874 AssertComRCReturn(autoCaller.rc(), (void)0);
11875
11876 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11877
11878 if (!mStorageControllers.isNull())
11879 {
11880 if (mStorageControllers.isBackedUp())
11881 {
11882 /* unitialize all new devices (absent in the backed up list). */
11883 StorageControllerList *backedList = mStorageControllers.backedUpData();
11884 for (StorageControllerList::const_iterator
11885 it = mStorageControllers->begin();
11886 it != mStorageControllers->end();
11887 ++it)
11888 {
11889 if ( std::find(backedList->begin(), backedList->end(), *it)
11890 == backedList->end()
11891 )
11892 {
11893 (*it)->uninit();
11894 }
11895 }
11896
11897 /* restore the list */
11898 mStorageControllers.rollback();
11899 }
11900
11901 /* rollback any changes to devices after restoring the list */
11902 if (mData->flModifications & IsModified_Storage)
11903 {
11904 for (StorageControllerList::const_iterator
11905 it = mStorageControllers->begin();
11906 it != mStorageControllers->end();
11907 ++it)
11908 {
11909 (*it)->i_rollback();
11910 }
11911 }
11912 }
11913
11914 if (!mUSBControllers.isNull())
11915 {
11916 if (mUSBControllers.isBackedUp())
11917 {
11918 /* unitialize all new devices (absent in the backed up list). */
11919 USBControllerList *backedList = mUSBControllers.backedUpData();
11920 for (USBControllerList::const_iterator
11921 it = mUSBControllers->begin();
11922 it != mUSBControllers->end();
11923 ++it)
11924 {
11925 if ( std::find(backedList->begin(), backedList->end(), *it)
11926 == backedList->end()
11927 )
11928 {
11929 (*it)->uninit();
11930 }
11931 }
11932
11933 /* restore the list */
11934 mUSBControllers.rollback();
11935 }
11936
11937 /* rollback any changes to devices after restoring the list */
11938 if (mData->flModifications & IsModified_USB)
11939 {
11940 for (USBControllerList::const_iterator
11941 it = mUSBControllers->begin();
11942 it != mUSBControllers->end();
11943 ++it)
11944 {
11945 (*it)->i_rollback();
11946 }
11947 }
11948 }
11949
11950 mUserData.rollback();
11951
11952 mHWData.rollback();
11953
11954 if (mData->flModifications & IsModified_Storage)
11955 i_rollbackMedia();
11956
11957 if (mBIOSSettings)
11958 mBIOSSettings->i_rollback();
11959
11960 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11961 mVRDEServer->i_rollback();
11962
11963 if (mAudioAdapter)
11964 mAudioAdapter->i_rollback();
11965
11966 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11967 mUSBDeviceFilters->i_rollback();
11968
11969 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11970 mBandwidthControl->i_rollback();
11971
11972 if (!mHWData.isNull())
11973 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11974 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11975 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11976 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11977
11978 if (mData->flModifications & IsModified_NetworkAdapters)
11979 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11980 if ( mNetworkAdapters[slot]
11981 && mNetworkAdapters[slot]->i_isModified())
11982 {
11983 mNetworkAdapters[slot]->i_rollback();
11984 networkAdapters[slot] = mNetworkAdapters[slot];
11985 }
11986
11987 if (mData->flModifications & IsModified_SerialPorts)
11988 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11989 if ( mSerialPorts[slot]
11990 && mSerialPorts[slot]->i_isModified())
11991 {
11992 mSerialPorts[slot]->i_rollback();
11993 serialPorts[slot] = mSerialPorts[slot];
11994 }
11995
11996 if (mData->flModifications & IsModified_ParallelPorts)
11997 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11998 if ( mParallelPorts[slot]
11999 && mParallelPorts[slot]->i_isModified())
12000 {
12001 mParallelPorts[slot]->i_rollback();
12002 parallelPorts[slot] = mParallelPorts[slot];
12003 }
12004
12005 if (aNotify)
12006 {
12007 /* inform the direct session about changes */
12008
12009 ComObjPtr<Machine> that = this;
12010 uint32_t flModifications = mData->flModifications;
12011 alock.release();
12012
12013 if (flModifications & IsModified_SharedFolders)
12014 that->i_onSharedFolderChange();
12015
12016 if (flModifications & IsModified_VRDEServer)
12017 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12018 if (flModifications & IsModified_USB)
12019 that->i_onUSBControllerChange();
12020
12021 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12022 if (networkAdapters[slot])
12023 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12024 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12025 if (serialPorts[slot])
12026 that->i_onSerialPortChange(serialPorts[slot]);
12027 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12028 if (parallelPorts[slot])
12029 that->i_onParallelPortChange(parallelPorts[slot]);
12030
12031 if (flModifications & IsModified_Storage)
12032 that->i_onStorageControllerChange();
12033
12034#if 0
12035 if (flModifications & IsModified_BandwidthControl)
12036 that->onBandwidthControlChange();
12037#endif
12038 }
12039}
12040
12041/**
12042 * Commits all the changes to machine settings.
12043 *
12044 * Note that this operation is supposed to never fail.
12045 *
12046 * @note Locks this object and children for writing.
12047 */
12048void Machine::i_commit()
12049{
12050 AutoCaller autoCaller(this);
12051 AssertComRCReturnVoid(autoCaller.rc());
12052
12053 AutoCaller peerCaller(mPeer);
12054 AssertComRCReturnVoid(peerCaller.rc());
12055
12056 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12057
12058 /*
12059 * use safe commit to ensure Snapshot machines (that share mUserData)
12060 * will still refer to a valid memory location
12061 */
12062 mUserData.commitCopy();
12063
12064 mHWData.commit();
12065
12066 if (mMediumAttachments.isBackedUp())
12067 i_commitMedia(Global::IsOnline(mData->mMachineState));
12068
12069 mBIOSSettings->i_commit();
12070 mVRDEServer->i_commit();
12071 mAudioAdapter->i_commit();
12072 mUSBDeviceFilters->i_commit();
12073 mBandwidthControl->i_commit();
12074
12075 /* Since mNetworkAdapters is a list which might have been changed (resized)
12076 * without using the Backupable<> template we need to handle the copying
12077 * of the list entries manually, including the creation of peers for the
12078 * new objects. */
12079 bool commitNetworkAdapters = false;
12080 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12081 if (mPeer)
12082 {
12083 /* commit everything, even the ones which will go away */
12084 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12085 mNetworkAdapters[slot]->i_commit();
12086 /* copy over the new entries, creating a peer and uninit the original */
12087 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12088 for (size_t slot = 0; slot < newSize; slot++)
12089 {
12090 /* look if this adapter has a peer device */
12091 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12092 if (!peer)
12093 {
12094 /* no peer means the adapter is a newly created one;
12095 * create a peer owning data this data share it with */
12096 peer.createObject();
12097 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12098 }
12099 mPeer->mNetworkAdapters[slot] = peer;
12100 }
12101 /* uninit any no longer needed network adapters */
12102 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12103 mNetworkAdapters[slot]->uninit();
12104 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12105 {
12106 if (mPeer->mNetworkAdapters[slot])
12107 mPeer->mNetworkAdapters[slot]->uninit();
12108 }
12109 /* Keep the original network adapter count until this point, so that
12110 * discarding a chipset type change will not lose settings. */
12111 mNetworkAdapters.resize(newSize);
12112 mPeer->mNetworkAdapters.resize(newSize);
12113 }
12114 else
12115 {
12116 /* we have no peer (our parent is the newly created machine);
12117 * just commit changes to the network adapters */
12118 commitNetworkAdapters = true;
12119 }
12120 if (commitNetworkAdapters)
12121 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12122 mNetworkAdapters[slot]->i_commit();
12123
12124 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12125 mSerialPorts[slot]->i_commit();
12126 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12127 mParallelPorts[slot]->i_commit();
12128
12129 bool commitStorageControllers = false;
12130
12131 if (mStorageControllers.isBackedUp())
12132 {
12133 mStorageControllers.commit();
12134
12135 if (mPeer)
12136 {
12137 /* Commit all changes to new controllers (this will reshare data with
12138 * peers for those who have peers) */
12139 StorageControllerList *newList = new StorageControllerList();
12140 for (StorageControllerList::const_iterator
12141 it = mStorageControllers->begin();
12142 it != mStorageControllers->end();
12143 ++it)
12144 {
12145 (*it)->i_commit();
12146
12147 /* look if this controller has a peer device */
12148 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12149 if (!peer)
12150 {
12151 /* no peer means the device is a newly created one;
12152 * create a peer owning data this device share it with */
12153 peer.createObject();
12154 peer->init(mPeer, *it, true /* aReshare */);
12155 }
12156 else
12157 {
12158 /* remove peer from the old list */
12159 mPeer->mStorageControllers->remove(peer);
12160 }
12161 /* and add it to the new list */
12162 newList->push_back(peer);
12163 }
12164
12165 /* uninit old peer's controllers that are left */
12166 for (StorageControllerList::const_iterator
12167 it = mPeer->mStorageControllers->begin();
12168 it != mPeer->mStorageControllers->end();
12169 ++it)
12170 {
12171 (*it)->uninit();
12172 }
12173
12174 /* attach new list of controllers to our peer */
12175 mPeer->mStorageControllers.attach(newList);
12176 }
12177 else
12178 {
12179 /* we have no peer (our parent is the newly created machine);
12180 * just commit changes to devices */
12181 commitStorageControllers = true;
12182 }
12183 }
12184 else
12185 {
12186 /* the list of controllers itself is not changed,
12187 * just commit changes to controllers themselves */
12188 commitStorageControllers = true;
12189 }
12190
12191 if (commitStorageControllers)
12192 {
12193 for (StorageControllerList::const_iterator
12194 it = mStorageControllers->begin();
12195 it != mStorageControllers->end();
12196 ++it)
12197 {
12198 (*it)->i_commit();
12199 }
12200 }
12201
12202 bool commitUSBControllers = false;
12203
12204 if (mUSBControllers.isBackedUp())
12205 {
12206 mUSBControllers.commit();
12207
12208 if (mPeer)
12209 {
12210 /* Commit all changes to new controllers (this will reshare data with
12211 * peers for those who have peers) */
12212 USBControllerList *newList = new USBControllerList();
12213 for (USBControllerList::const_iterator
12214 it = mUSBControllers->begin();
12215 it != mUSBControllers->end();
12216 ++it)
12217 {
12218 (*it)->i_commit();
12219
12220 /* look if this controller has a peer device */
12221 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12222 if (!peer)
12223 {
12224 /* no peer means the device is a newly created one;
12225 * create a peer owning data this device share it with */
12226 peer.createObject();
12227 peer->init(mPeer, *it, true /* aReshare */);
12228 }
12229 else
12230 {
12231 /* remove peer from the old list */
12232 mPeer->mUSBControllers->remove(peer);
12233 }
12234 /* and add it to the new list */
12235 newList->push_back(peer);
12236 }
12237
12238 /* uninit old peer's controllers that are left */
12239 for (USBControllerList::const_iterator
12240 it = mPeer->mUSBControllers->begin();
12241 it != mPeer->mUSBControllers->end();
12242 ++it)
12243 {
12244 (*it)->uninit();
12245 }
12246
12247 /* attach new list of controllers to our peer */
12248 mPeer->mUSBControllers.attach(newList);
12249 }
12250 else
12251 {
12252 /* we have no peer (our parent is the newly created machine);
12253 * just commit changes to devices */
12254 commitUSBControllers = true;
12255 }
12256 }
12257 else
12258 {
12259 /* the list of controllers itself is not changed,
12260 * just commit changes to controllers themselves */
12261 commitUSBControllers = true;
12262 }
12263
12264 if (commitUSBControllers)
12265 {
12266 for (USBControllerList::const_iterator
12267 it = mUSBControllers->begin();
12268 it != mUSBControllers->end();
12269 ++it)
12270 {
12271 (*it)->i_commit();
12272 }
12273 }
12274
12275 if (i_isSessionMachine())
12276 {
12277 /* attach new data to the primary machine and reshare it */
12278 mPeer->mUserData.attach(mUserData);
12279 mPeer->mHWData.attach(mHWData);
12280 /* mmMediumAttachments is reshared by fixupMedia */
12281 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12282 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12283 }
12284}
12285
12286/**
12287 * Copies all the hardware data from the given machine.
12288 *
12289 * Currently, only called when the VM is being restored from a snapshot. In
12290 * particular, this implies that the VM is not running during this method's
12291 * call.
12292 *
12293 * @note This method must be called from under this object's lock.
12294 *
12295 * @note This method doesn't call #i_commit(), so all data remains backed up and
12296 * unsaved.
12297 */
12298void Machine::i_copyFrom(Machine *aThat)
12299{
12300 AssertReturnVoid(!i_isSnapshotMachine());
12301 AssertReturnVoid(aThat->i_isSnapshotMachine());
12302
12303 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12304
12305 mHWData.assignCopy(aThat->mHWData);
12306
12307 // create copies of all shared folders (mHWData after attaching a copy
12308 // contains just references to original objects)
12309 for (HWData::SharedFolderList::iterator
12310 it = mHWData->mSharedFolders.begin();
12311 it != mHWData->mSharedFolders.end();
12312 ++it)
12313 {
12314 ComObjPtr<SharedFolder> folder;
12315 folder.createObject();
12316 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12317 AssertComRC(rc);
12318 *it = folder;
12319 }
12320
12321 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12322 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12323 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12324 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12325 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12326
12327 /* create private copies of all controllers */
12328 mStorageControllers.backup();
12329 mStorageControllers->clear();
12330 for (StorageControllerList::const_iterator
12331 it = aThat->mStorageControllers->begin();
12332 it != aThat->mStorageControllers->end();
12333 ++it)
12334 {
12335 ComObjPtr<StorageController> ctrl;
12336 ctrl.createObject();
12337 ctrl->initCopy(this, *it);
12338 mStorageControllers->push_back(ctrl);
12339 }
12340
12341 /* create private copies of all USB controllers */
12342 mUSBControllers.backup();
12343 mUSBControllers->clear();
12344 for (USBControllerList::const_iterator
12345 it = aThat->mUSBControllers->begin();
12346 it != aThat->mUSBControllers->end();
12347 ++it)
12348 {
12349 ComObjPtr<USBController> ctrl;
12350 ctrl.createObject();
12351 ctrl->initCopy(this, *it);
12352 mUSBControllers->push_back(ctrl);
12353 }
12354
12355 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12356 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12357 {
12358 if (mNetworkAdapters[slot].isNotNull())
12359 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12360 else
12361 {
12362 unconst(mNetworkAdapters[slot]).createObject();
12363 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12364 }
12365 }
12366 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12367 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12368 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12369 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12370}
12371
12372/**
12373 * Returns whether the given storage controller is hotplug capable.
12374 *
12375 * @returns true if the controller supports hotplugging
12376 * false otherwise.
12377 * @param enmCtrlType The controller type to check for.
12378 */
12379bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12380{
12381 ComPtr<ISystemProperties> systemProperties;
12382 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12383 if (FAILED(rc))
12384 return false;
12385
12386 BOOL aHotplugCapable = FALSE;
12387 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12388
12389 return RT_BOOL(aHotplugCapable);
12390}
12391
12392#ifdef VBOX_WITH_RESOURCE_USAGE_API
12393
12394void Machine::i_getDiskList(MediaList &list)
12395{
12396 for (MediumAttachmentList::const_iterator
12397 it = mMediumAttachments->begin();
12398 it != mMediumAttachments->end();
12399 ++it)
12400 {
12401 MediumAttachment *pAttach = *it;
12402 /* just in case */
12403 AssertContinue(pAttach);
12404
12405 AutoCaller localAutoCallerA(pAttach);
12406 if (FAILED(localAutoCallerA.rc())) continue;
12407
12408 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12409
12410 if (pAttach->i_getType() == DeviceType_HardDisk)
12411 list.push_back(pAttach->i_getMedium());
12412 }
12413}
12414
12415void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12416{
12417 AssertReturnVoid(isWriteLockOnCurrentThread());
12418 AssertPtrReturnVoid(aCollector);
12419
12420 pm::CollectorHAL *hal = aCollector->getHAL();
12421 /* Create sub metrics */
12422 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12423 "Percentage of processor time spent in user mode by the VM process.");
12424 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12425 "Percentage of processor time spent in kernel mode by the VM process.");
12426 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12427 "Size of resident portion of VM process in memory.");
12428 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12429 "Actual size of all VM disks combined.");
12430 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12431 "Network receive rate.");
12432 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12433 "Network transmit rate.");
12434 /* Create and register base metrics */
12435 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12436 cpuLoadUser, cpuLoadKernel);
12437 aCollector->registerBaseMetric(cpuLoad);
12438 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12439 ramUsageUsed);
12440 aCollector->registerBaseMetric(ramUsage);
12441 MediaList disks;
12442 i_getDiskList(disks);
12443 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12444 diskUsageUsed);
12445 aCollector->registerBaseMetric(diskUsage);
12446
12447 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12448 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12449 new pm::AggregateAvg()));
12450 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12451 new pm::AggregateMin()));
12452 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12453 new pm::AggregateMax()));
12454 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12455 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12456 new pm::AggregateAvg()));
12457 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12458 new pm::AggregateMin()));
12459 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12460 new pm::AggregateMax()));
12461
12462 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12463 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12464 new pm::AggregateAvg()));
12465 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12466 new pm::AggregateMin()));
12467 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12468 new pm::AggregateMax()));
12469
12470 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12471 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12472 new pm::AggregateAvg()));
12473 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12474 new pm::AggregateMin()));
12475 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12476 new pm::AggregateMax()));
12477
12478
12479 /* Guest metrics collector */
12480 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12481 aCollector->registerGuest(mCollectorGuest);
12482 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12483
12484 /* Create sub metrics */
12485 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12486 "Percentage of processor time spent in user mode as seen by the guest.");
12487 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12488 "Percentage of processor time spent in kernel mode as seen by the guest.");
12489 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12490 "Percentage of processor time spent idling as seen by the guest.");
12491
12492 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12493 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12494 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12495 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12496 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12497 pm::SubMetric *guestMemCache = new pm::SubMetric(
12498 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12499
12500 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12501 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12502
12503 /* Create and register base metrics */
12504 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12505 machineNetRx, machineNetTx);
12506 aCollector->registerBaseMetric(machineNetRate);
12507
12508 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12509 guestLoadUser, guestLoadKernel, guestLoadIdle);
12510 aCollector->registerBaseMetric(guestCpuLoad);
12511
12512 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12513 guestMemTotal, guestMemFree,
12514 guestMemBalloon, guestMemShared,
12515 guestMemCache, guestPagedTotal);
12516 aCollector->registerBaseMetric(guestCpuMem);
12517
12518 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12519 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12520 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12521 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12522
12523 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12524 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12525 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12526 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12527
12528 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12529 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12530 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12531 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12532
12533 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12534 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12535 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12536 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12537
12538 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12539 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12540 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12541 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12542
12543 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12544 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12545 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12546 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12547
12548 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12549 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12550 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12551 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12552
12553 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12554 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12555 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12556 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12557
12558 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12559 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12560 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12561 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12562
12563 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12564 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12565 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12566 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12567
12568 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12569 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12570 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12571 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12572}
12573
12574void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12575{
12576 AssertReturnVoid(isWriteLockOnCurrentThread());
12577
12578 if (aCollector)
12579 {
12580 aCollector->unregisterMetricsFor(aMachine);
12581 aCollector->unregisterBaseMetricsFor(aMachine);
12582 }
12583}
12584
12585#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12586
12587
12588////////////////////////////////////////////////////////////////////////////////
12589
12590DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12591
12592HRESULT SessionMachine::FinalConstruct()
12593{
12594 LogFlowThisFunc(("\n"));
12595
12596 mClientToken = NULL;
12597
12598 return BaseFinalConstruct();
12599}
12600
12601void SessionMachine::FinalRelease()
12602{
12603 LogFlowThisFunc(("\n"));
12604
12605 Assert(!mClientToken);
12606 /* paranoia, should not hang around any more */
12607 if (mClientToken)
12608 {
12609 delete mClientToken;
12610 mClientToken = NULL;
12611 }
12612
12613 uninit(Uninit::Unexpected);
12614
12615 BaseFinalRelease();
12616}
12617
12618/**
12619 * @note Must be called only by Machine::LockMachine() from its own write lock.
12620 */
12621HRESULT SessionMachine::init(Machine *aMachine)
12622{
12623 LogFlowThisFuncEnter();
12624 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12625
12626 AssertReturn(aMachine, E_INVALIDARG);
12627
12628 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12629
12630 /* Enclose the state transition NotReady->InInit->Ready */
12631 AutoInitSpan autoInitSpan(this);
12632 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12633
12634 HRESULT rc = S_OK;
12635
12636 RT_ZERO(mAuthLibCtx);
12637
12638 /* create the machine client token */
12639 try
12640 {
12641 mClientToken = new ClientToken(aMachine, this);
12642 if (!mClientToken->isReady())
12643 {
12644 delete mClientToken;
12645 mClientToken = NULL;
12646 rc = E_FAIL;
12647 }
12648 }
12649 catch (std::bad_alloc &)
12650 {
12651 rc = E_OUTOFMEMORY;
12652 }
12653 if (FAILED(rc))
12654 return rc;
12655
12656 /* memorize the peer Machine */
12657 unconst(mPeer) = aMachine;
12658 /* share the parent pointer */
12659 unconst(mParent) = aMachine->mParent;
12660
12661 /* take the pointers to data to share */
12662 mData.share(aMachine->mData);
12663 mSSData.share(aMachine->mSSData);
12664
12665 mUserData.share(aMachine->mUserData);
12666 mHWData.share(aMachine->mHWData);
12667 mMediumAttachments.share(aMachine->mMediumAttachments);
12668
12669 mStorageControllers.allocate();
12670 for (StorageControllerList::const_iterator
12671 it = aMachine->mStorageControllers->begin();
12672 it != aMachine->mStorageControllers->end();
12673 ++it)
12674 {
12675 ComObjPtr<StorageController> ctl;
12676 ctl.createObject();
12677 ctl->init(this, *it);
12678 mStorageControllers->push_back(ctl);
12679 }
12680
12681 mUSBControllers.allocate();
12682 for (USBControllerList::const_iterator
12683 it = aMachine->mUSBControllers->begin();
12684 it != aMachine->mUSBControllers->end();
12685 ++it)
12686 {
12687 ComObjPtr<USBController> ctl;
12688 ctl.createObject();
12689 ctl->init(this, *it);
12690 mUSBControllers->push_back(ctl);
12691 }
12692
12693 unconst(mBIOSSettings).createObject();
12694 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12695 /* create another VRDEServer object that will be mutable */
12696 unconst(mVRDEServer).createObject();
12697 mVRDEServer->init(this, aMachine->mVRDEServer);
12698 /* create another audio adapter object that will be mutable */
12699 unconst(mAudioAdapter).createObject();
12700 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12701 /* create a list of serial ports that will be mutable */
12702 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12703 {
12704 unconst(mSerialPorts[slot]).createObject();
12705 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12706 }
12707 /* create a list of parallel ports that will be mutable */
12708 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12709 {
12710 unconst(mParallelPorts[slot]).createObject();
12711 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12712 }
12713
12714 /* create another USB device filters object that will be mutable */
12715 unconst(mUSBDeviceFilters).createObject();
12716 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12717
12718 /* create a list of network adapters that will be mutable */
12719 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12720 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12721 {
12722 unconst(mNetworkAdapters[slot]).createObject();
12723 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12724 }
12725
12726 /* create another bandwidth control object that will be mutable */
12727 unconst(mBandwidthControl).createObject();
12728 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12729
12730 /* default is to delete saved state on Saved -> PoweredOff transition */
12731 mRemoveSavedState = true;
12732
12733 /* Confirm a successful initialization when it's the case */
12734 autoInitSpan.setSucceeded();
12735
12736 miNATNetworksStarted = 0;
12737
12738 LogFlowThisFuncLeave();
12739 return rc;
12740}
12741
12742/**
12743 * Uninitializes this session object. If the reason is other than
12744 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12745 * or the client watcher code.
12746 *
12747 * @param aReason uninitialization reason
12748 *
12749 * @note Locks mParent + this object for writing.
12750 */
12751void SessionMachine::uninit(Uninit::Reason aReason)
12752{
12753 LogFlowThisFuncEnter();
12754 LogFlowThisFunc(("reason=%d\n", aReason));
12755
12756 /*
12757 * Strongly reference ourselves to prevent this object deletion after
12758 * mData->mSession.mMachine.setNull() below (which can release the last
12759 * reference and call the destructor). Important: this must be done before
12760 * accessing any members (and before AutoUninitSpan that does it as well).
12761 * This self reference will be released as the very last step on return.
12762 */
12763 ComObjPtr<SessionMachine> selfRef;
12764 if (aReason != Uninit::Unexpected)
12765 selfRef = this;
12766
12767 /* Enclose the state transition Ready->InUninit->NotReady */
12768 AutoUninitSpan autoUninitSpan(this);
12769 if (autoUninitSpan.uninitDone())
12770 {
12771 LogFlowThisFunc(("Already uninitialized\n"));
12772 LogFlowThisFuncLeave();
12773 return;
12774 }
12775
12776 if (autoUninitSpan.initFailed())
12777 {
12778 /* We've been called by init() because it's failed. It's not really
12779 * necessary (nor it's safe) to perform the regular uninit sequence
12780 * below, the following is enough.
12781 */
12782 LogFlowThisFunc(("Initialization failed.\n"));
12783 /* destroy the machine client token */
12784 if (mClientToken)
12785 {
12786 delete mClientToken;
12787 mClientToken = NULL;
12788 }
12789 uninitDataAndChildObjects();
12790 mData.free();
12791 unconst(mParent) = NULL;
12792 unconst(mPeer) = NULL;
12793 LogFlowThisFuncLeave();
12794 return;
12795 }
12796
12797 MachineState_T lastState;
12798 {
12799 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12800 lastState = mData->mMachineState;
12801 }
12802 NOREF(lastState);
12803
12804#ifdef VBOX_WITH_USB
12805 // release all captured USB devices, but do this before requesting the locks below
12806 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12807 {
12808 /* Console::captureUSBDevices() is called in the VM process only after
12809 * setting the machine state to Starting or Restoring.
12810 * Console::detachAllUSBDevices() will be called upon successful
12811 * termination. So, we need to release USB devices only if there was
12812 * an abnormal termination of a running VM.
12813 *
12814 * This is identical to SessionMachine::DetachAllUSBDevices except
12815 * for the aAbnormal argument. */
12816 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12817 AssertComRC(rc);
12818 NOREF(rc);
12819
12820 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12821 if (service)
12822 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12823 }
12824#endif /* VBOX_WITH_USB */
12825
12826 // we need to lock this object in uninit() because the lock is shared
12827 // with mPeer (as well as data we modify below). mParent lock is needed
12828 // by several calls to it.
12829 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12830
12831#ifdef VBOX_WITH_RESOURCE_USAGE_API
12832 /*
12833 * It is safe to call Machine::i_unregisterMetrics() here because
12834 * PerformanceCollector::samplerCallback no longer accesses guest methods
12835 * holding the lock.
12836 */
12837 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12838 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12839 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12840 if (mCollectorGuest)
12841 {
12842 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12843 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12844 mCollectorGuest = NULL;
12845 }
12846#endif
12847
12848 if (aReason == Uninit::Abnormal)
12849 {
12850 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12851
12852 /* reset the state to Aborted */
12853 if (mData->mMachineState != MachineState_Aborted)
12854 i_setMachineState(MachineState_Aborted);
12855 }
12856
12857 // any machine settings modified?
12858 if (mData->flModifications)
12859 {
12860 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12861 i_rollback(false /* aNotify */);
12862 }
12863
12864 mData->mSession.mPID = NIL_RTPROCESS;
12865
12866 if (aReason == Uninit::Unexpected)
12867 {
12868 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12869 * client watcher thread to update the set of machines that have open
12870 * sessions. */
12871 mParent->i_updateClientWatcher();
12872 }
12873
12874 /* uninitialize all remote controls */
12875 if (mData->mSession.mRemoteControls.size())
12876 {
12877 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12878 mData->mSession.mRemoteControls.size()));
12879
12880 /* Always restart a the beginning, since the iterator is invalidated
12881 * by using erase(). */
12882 for (Data::Session::RemoteControlList::iterator
12883 it = mData->mSession.mRemoteControls.begin();
12884 it != mData->mSession.mRemoteControls.end();
12885 it = mData->mSession.mRemoteControls.begin())
12886 {
12887 ComPtr<IInternalSessionControl> pControl = *it;
12888 mData->mSession.mRemoteControls.erase(it);
12889 multilock.release();
12890 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12891 HRESULT rc = pControl->Uninitialize();
12892 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12893 if (FAILED(rc))
12894 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12895 multilock.acquire();
12896 }
12897 mData->mSession.mRemoteControls.clear();
12898 }
12899
12900 /* Remove all references to the NAT network service. The service will stop
12901 * if all references (also from other VMs) are removed. */
12902 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12903 {
12904 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12905 {
12906 BOOL enabled;
12907 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12908 if ( FAILED(hrc)
12909 || !enabled)
12910 continue;
12911
12912 NetworkAttachmentType_T type;
12913 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12914 if ( SUCCEEDED(hrc)
12915 && type == NetworkAttachmentType_NATNetwork)
12916 {
12917 Bstr name;
12918 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12919 if (SUCCEEDED(hrc))
12920 {
12921 multilock.release();
12922 Utf8Str strName(name);
12923 LogRel(("VM '%s' stops using NAT network '%s'\n",
12924 mUserData->s.strName.c_str(), strName.c_str()));
12925 mParent->i_natNetworkRefDec(strName);
12926 multilock.acquire();
12927 }
12928 }
12929 }
12930 }
12931
12932 /*
12933 * An expected uninitialization can come only from #i_checkForDeath().
12934 * Otherwise it means that something's gone really wrong (for example,
12935 * the Session implementation has released the VirtualBox reference
12936 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12937 * etc). However, it's also possible, that the client releases the IPC
12938 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12939 * but the VirtualBox release event comes first to the server process.
12940 * This case is practically possible, so we should not assert on an
12941 * unexpected uninit, just log a warning.
12942 */
12943
12944 if (aReason == Uninit::Unexpected)
12945 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12946
12947 if (aReason != Uninit::Normal)
12948 {
12949 mData->mSession.mDirectControl.setNull();
12950 }
12951 else
12952 {
12953 /* this must be null here (see #OnSessionEnd()) */
12954 Assert(mData->mSession.mDirectControl.isNull());
12955 Assert(mData->mSession.mState == SessionState_Unlocking);
12956 Assert(!mData->mSession.mProgress.isNull());
12957 }
12958 if (mData->mSession.mProgress)
12959 {
12960 if (aReason == Uninit::Normal)
12961 mData->mSession.mProgress->i_notifyComplete(S_OK);
12962 else
12963 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12964 COM_IIDOF(ISession),
12965 getComponentName(),
12966 tr("The VM session was aborted"));
12967 mData->mSession.mProgress.setNull();
12968 }
12969
12970 if (mConsoleTaskData.mProgress)
12971 {
12972 Assert(aReason == Uninit::Abnormal);
12973 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12974 COM_IIDOF(ISession),
12975 getComponentName(),
12976 tr("The VM session was aborted"));
12977 mConsoleTaskData.mProgress.setNull();
12978 }
12979
12980 /* remove the association between the peer machine and this session machine */
12981 Assert( (SessionMachine*)mData->mSession.mMachine == this
12982 || aReason == Uninit::Unexpected);
12983
12984 /* reset the rest of session data */
12985 mData->mSession.mLockType = LockType_Null;
12986 mData->mSession.mMachine.setNull();
12987 mData->mSession.mState = SessionState_Unlocked;
12988 mData->mSession.mName.setNull();
12989
12990 /* destroy the machine client token before leaving the exclusive lock */
12991 if (mClientToken)
12992 {
12993 delete mClientToken;
12994 mClientToken = NULL;
12995 }
12996
12997 /* fire an event */
12998 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12999
13000 uninitDataAndChildObjects();
13001
13002 /* free the essential data structure last */
13003 mData.free();
13004
13005 /* release the exclusive lock before setting the below two to NULL */
13006 multilock.release();
13007
13008 unconst(mParent) = NULL;
13009 unconst(mPeer) = NULL;
13010
13011 AuthLibUnload(&mAuthLibCtx);
13012
13013 LogFlowThisFuncLeave();
13014}
13015
13016// util::Lockable interface
13017////////////////////////////////////////////////////////////////////////////////
13018
13019/**
13020 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13021 * with the primary Machine instance (mPeer).
13022 */
13023RWLockHandle *SessionMachine::lockHandle() const
13024{
13025 AssertReturn(mPeer != NULL, NULL);
13026 return mPeer->lockHandle();
13027}
13028
13029// IInternalMachineControl methods
13030////////////////////////////////////////////////////////////////////////////////
13031
13032/**
13033 * Passes collected guest statistics to performance collector object
13034 */
13035HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13036 ULONG aCpuKernel, ULONG aCpuIdle,
13037 ULONG aMemTotal, ULONG aMemFree,
13038 ULONG aMemBalloon, ULONG aMemShared,
13039 ULONG aMemCache, ULONG aPageTotal,
13040 ULONG aAllocVMM, ULONG aFreeVMM,
13041 ULONG aBalloonedVMM, ULONG aSharedVMM,
13042 ULONG aVmNetRx, ULONG aVmNetTx)
13043{
13044#ifdef VBOX_WITH_RESOURCE_USAGE_API
13045 if (mCollectorGuest)
13046 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13047 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13048 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13049 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13050
13051 return S_OK;
13052#else
13053 NOREF(aValidStats);
13054 NOREF(aCpuUser);
13055 NOREF(aCpuKernel);
13056 NOREF(aCpuIdle);
13057 NOREF(aMemTotal);
13058 NOREF(aMemFree);
13059 NOREF(aMemBalloon);
13060 NOREF(aMemShared);
13061 NOREF(aMemCache);
13062 NOREF(aPageTotal);
13063 NOREF(aAllocVMM);
13064 NOREF(aFreeVMM);
13065 NOREF(aBalloonedVMM);
13066 NOREF(aSharedVMM);
13067 NOREF(aVmNetRx);
13068 NOREF(aVmNetTx);
13069 return E_NOTIMPL;
13070#endif
13071}
13072
13073////////////////////////////////////////////////////////////////////////////////
13074//
13075// SessionMachine task records
13076//
13077////////////////////////////////////////////////////////////////////////////////
13078
13079/**
13080 * Task record for saving the machine state.
13081 */
13082class SessionMachine::SaveStateTask
13083 : public Machine::Task
13084{
13085public:
13086 SaveStateTask(SessionMachine *m,
13087 Progress *p,
13088 const Utf8Str &t,
13089 Reason_T enmReason,
13090 const Utf8Str &strStateFilePath)
13091 : Task(m, p, t),
13092 m_enmReason(enmReason),
13093 m_strStateFilePath(strStateFilePath)
13094 {}
13095
13096private:
13097 void handler()
13098 {
13099 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13100 }
13101
13102 Reason_T m_enmReason;
13103 Utf8Str m_strStateFilePath;
13104
13105 friend class SessionMachine;
13106};
13107
13108/**
13109 * Task thread implementation for SessionMachine::SaveState(), called from
13110 * SessionMachine::taskHandler().
13111 *
13112 * @note Locks this object for writing.
13113 *
13114 * @param task
13115 * @return
13116 */
13117void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13118{
13119 LogFlowThisFuncEnter();
13120
13121 AutoCaller autoCaller(this);
13122 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13123 if (FAILED(autoCaller.rc()))
13124 {
13125 /* we might have been uninitialized because the session was accidentally
13126 * closed by the client, so don't assert */
13127 HRESULT rc = setError(E_FAIL,
13128 tr("The session has been accidentally closed"));
13129 task.m_pProgress->i_notifyComplete(rc);
13130 LogFlowThisFuncLeave();
13131 return;
13132 }
13133
13134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13135
13136 HRESULT rc = S_OK;
13137
13138 try
13139 {
13140 ComPtr<IInternalSessionControl> directControl;
13141 if (mData->mSession.mLockType == LockType_VM)
13142 directControl = mData->mSession.mDirectControl;
13143 if (directControl.isNull())
13144 throw setError(VBOX_E_INVALID_VM_STATE,
13145 tr("Trying to save state without a running VM"));
13146 alock.release();
13147 BOOL fSuspendedBySave;
13148 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13149 Assert(!fSuspendedBySave);
13150 alock.acquire();
13151
13152 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13153 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13154 throw E_FAIL);
13155
13156 if (SUCCEEDED(rc))
13157 {
13158 mSSData->strStateFilePath = task.m_strStateFilePath;
13159
13160 /* save all VM settings */
13161 rc = i_saveSettings(NULL);
13162 // no need to check whether VirtualBox.xml needs saving also since
13163 // we can't have a name change pending at this point
13164 }
13165 else
13166 {
13167 // On failure, set the state to the state we had at the beginning.
13168 i_setMachineState(task.m_machineStateBackup);
13169 i_updateMachineStateOnClient();
13170
13171 // Delete the saved state file (might have been already created).
13172 // No need to check whether this is shared with a snapshot here
13173 // because we certainly created a fresh saved state file here.
13174 RTFileDelete(task.m_strStateFilePath.c_str());
13175 }
13176 }
13177 catch (HRESULT aRC) { rc = aRC; }
13178
13179 task.m_pProgress->i_notifyComplete(rc);
13180
13181 LogFlowThisFuncLeave();
13182}
13183
13184/**
13185 * @note Locks this object for writing.
13186 */
13187HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13188{
13189 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13190}
13191
13192HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13193{
13194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13195
13196 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13197 if (FAILED(rc)) return rc;
13198
13199 if ( mData->mMachineState != MachineState_Running
13200 && mData->mMachineState != MachineState_Paused
13201 )
13202 return setError(VBOX_E_INVALID_VM_STATE,
13203 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13204 Global::stringifyMachineState(mData->mMachineState));
13205
13206 ComObjPtr<Progress> pProgress;
13207 pProgress.createObject();
13208 rc = pProgress->init(i_getVirtualBox(),
13209 static_cast<IMachine *>(this) /* aInitiator */,
13210 tr("Saving the execution state of the virtual machine"),
13211 FALSE /* aCancelable */);
13212 if (FAILED(rc))
13213 return rc;
13214
13215 Utf8Str strStateFilePath;
13216 i_composeSavedStateFilename(strStateFilePath);
13217
13218 /* create and start the task on a separate thread (note that it will not
13219 * start working until we release alock) */
13220 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13221 rc = pTask->createThread();
13222 if (FAILED(rc))
13223 return rc;
13224
13225 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13226 i_setMachineState(MachineState_Saving);
13227 i_updateMachineStateOnClient();
13228
13229 pProgress.queryInterfaceTo(aProgress.asOutParam());
13230
13231 return S_OK;
13232}
13233
13234/**
13235 * @note Locks this object for writing.
13236 */
13237HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13238{
13239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13240
13241 HRESULT rc = i_checkStateDependency(MutableStateDep);
13242 if (FAILED(rc)) return rc;
13243
13244 if ( mData->mMachineState != MachineState_PoweredOff
13245 && mData->mMachineState != MachineState_Teleported
13246 && mData->mMachineState != MachineState_Aborted
13247 )
13248 return setError(VBOX_E_INVALID_VM_STATE,
13249 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13250 Global::stringifyMachineState(mData->mMachineState));
13251
13252 com::Utf8Str stateFilePathFull;
13253 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13254 if (RT_FAILURE(vrc))
13255 return setError(VBOX_E_FILE_ERROR,
13256 tr("Invalid saved state file path '%s' (%Rrc)"),
13257 aSavedStateFile.c_str(),
13258 vrc);
13259
13260 mSSData->strStateFilePath = stateFilePathFull;
13261
13262 /* The below i_setMachineState() will detect the state transition and will
13263 * update the settings file */
13264
13265 return i_setMachineState(MachineState_Saved);
13266}
13267
13268/**
13269 * @note Locks this object for writing.
13270 */
13271HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13272{
13273 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13274
13275 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13276 if (FAILED(rc)) return rc;
13277
13278 if (mData->mMachineState != MachineState_Saved)
13279 return setError(VBOX_E_INVALID_VM_STATE,
13280 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13281 Global::stringifyMachineState(mData->mMachineState));
13282
13283 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13284
13285 /*
13286 * Saved -> PoweredOff transition will be detected in the SessionMachine
13287 * and properly handled.
13288 */
13289 rc = i_setMachineState(MachineState_PoweredOff);
13290 return rc;
13291}
13292
13293
13294/**
13295 * @note Locks the same as #i_setMachineState() does.
13296 */
13297HRESULT SessionMachine::updateState(MachineState_T aState)
13298{
13299 return i_setMachineState(aState);
13300}
13301
13302/**
13303 * @note Locks this object for writing.
13304 */
13305HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13306{
13307 IProgress *pProgress(aProgress);
13308
13309 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13310
13311 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13312
13313 if (mData->mSession.mState != SessionState_Locked)
13314 return VBOX_E_INVALID_OBJECT_STATE;
13315
13316 if (!mData->mSession.mProgress.isNull())
13317 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13318
13319 /* If we didn't reference the NAT network service yet, add a reference to
13320 * force a start */
13321 if (miNATNetworksStarted < 1)
13322 {
13323 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13324 {
13325 BOOL enabled;
13326 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13327 if ( FAILED(hrc)
13328 || !enabled)
13329 continue;
13330
13331 NetworkAttachmentType_T type;
13332 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13333 if ( SUCCEEDED(hrc)
13334 && type == NetworkAttachmentType_NATNetwork)
13335 {
13336 Bstr name;
13337 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13338 if (SUCCEEDED(hrc))
13339 {
13340 Utf8Str strName(name);
13341 LogRel(("VM '%s' starts using NAT network '%s'\n",
13342 mUserData->s.strName.c_str(), strName.c_str()));
13343 mPeer->lockHandle()->unlockWrite();
13344 mParent->i_natNetworkRefInc(strName);
13345#ifdef RT_LOCK_STRICT
13346 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13347#else
13348 mPeer->lockHandle()->lockWrite();
13349#endif
13350 }
13351 }
13352 }
13353 miNATNetworksStarted++;
13354 }
13355
13356 LogFlowThisFunc(("returns S_OK.\n"));
13357 return S_OK;
13358}
13359
13360/**
13361 * @note Locks this object for writing.
13362 */
13363HRESULT SessionMachine::endPowerUp(LONG aResult)
13364{
13365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13366
13367 if (mData->mSession.mState != SessionState_Locked)
13368 return VBOX_E_INVALID_OBJECT_STATE;
13369
13370 /* Finalize the LaunchVMProcess progress object. */
13371 if (mData->mSession.mProgress)
13372 {
13373 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13374 mData->mSession.mProgress.setNull();
13375 }
13376
13377 if (SUCCEEDED((HRESULT)aResult))
13378 {
13379#ifdef VBOX_WITH_RESOURCE_USAGE_API
13380 /* The VM has been powered up successfully, so it makes sense
13381 * now to offer the performance metrics for a running machine
13382 * object. Doing it earlier wouldn't be safe. */
13383 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13384 mData->mSession.mPID);
13385#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13386 }
13387
13388 return S_OK;
13389}
13390
13391/**
13392 * @note Locks this object for writing.
13393 */
13394HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13395{
13396 LogFlowThisFuncEnter();
13397
13398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13399
13400 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13401 E_FAIL);
13402
13403 /* create a progress object to track operation completion */
13404 ComObjPtr<Progress> pProgress;
13405 pProgress.createObject();
13406 pProgress->init(i_getVirtualBox(),
13407 static_cast<IMachine *>(this) /* aInitiator */,
13408 tr("Stopping the virtual machine"),
13409 FALSE /* aCancelable */);
13410
13411 /* fill in the console task data */
13412 mConsoleTaskData.mLastState = mData->mMachineState;
13413 mConsoleTaskData.mProgress = pProgress;
13414
13415 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13416 i_setMachineState(MachineState_Stopping);
13417
13418 pProgress.queryInterfaceTo(aProgress.asOutParam());
13419
13420 return S_OK;
13421}
13422
13423/**
13424 * @note Locks this object for writing.
13425 */
13426HRESULT SessionMachine::endPoweringDown(LONG aResult,
13427 const com::Utf8Str &aErrMsg)
13428{
13429 LogFlowThisFuncEnter();
13430
13431 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13432
13433 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13434 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13435 && mConsoleTaskData.mLastState != MachineState_Null,
13436 E_FAIL);
13437
13438 /*
13439 * On failure, set the state to the state we had when BeginPoweringDown()
13440 * was called (this is expected by Console::PowerDown() and the associated
13441 * task). On success the VM process already changed the state to
13442 * MachineState_PoweredOff, so no need to do anything.
13443 */
13444 if (FAILED(aResult))
13445 i_setMachineState(mConsoleTaskData.mLastState);
13446
13447 /* notify the progress object about operation completion */
13448 Assert(mConsoleTaskData.mProgress);
13449 if (SUCCEEDED(aResult))
13450 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13451 else
13452 {
13453 if (aErrMsg.length())
13454 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13455 COM_IIDOF(ISession),
13456 getComponentName(),
13457 aErrMsg.c_str());
13458 else
13459 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13460 }
13461
13462 /* clear out the temporary saved state data */
13463 mConsoleTaskData.mLastState = MachineState_Null;
13464 mConsoleTaskData.mProgress.setNull();
13465
13466 LogFlowThisFuncLeave();
13467 return S_OK;
13468}
13469
13470
13471/**
13472 * Goes through the USB filters of the given machine to see if the given
13473 * device matches any filter or not.
13474 *
13475 * @note Locks the same as USBController::hasMatchingFilter() does.
13476 */
13477HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13478 BOOL *aMatched,
13479 ULONG *aMaskedInterfaces)
13480{
13481 LogFlowThisFunc(("\n"));
13482
13483#ifdef VBOX_WITH_USB
13484 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13485#else
13486 NOREF(aDevice);
13487 NOREF(aMaskedInterfaces);
13488 *aMatched = FALSE;
13489#endif
13490
13491 return S_OK;
13492}
13493
13494/**
13495 * @note Locks the same as Host::captureUSBDevice() does.
13496 */
13497HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13498{
13499 LogFlowThisFunc(("\n"));
13500
13501#ifdef VBOX_WITH_USB
13502 /* if captureDeviceForVM() fails, it must have set extended error info */
13503 clearError();
13504 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13505 if (FAILED(rc)) return rc;
13506
13507 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13508 AssertReturn(service, E_FAIL);
13509 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13510#else
13511 NOREF(aId);
13512 return E_NOTIMPL;
13513#endif
13514}
13515
13516/**
13517 * @note Locks the same as Host::detachUSBDevice() does.
13518 */
13519HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13520 BOOL aDone)
13521{
13522 LogFlowThisFunc(("\n"));
13523
13524#ifdef VBOX_WITH_USB
13525 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13526 AssertReturn(service, E_FAIL);
13527 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13528#else
13529 NOREF(aId);
13530 NOREF(aDone);
13531 return E_NOTIMPL;
13532#endif
13533}
13534
13535/**
13536 * Inserts all machine filters to the USB proxy service and then calls
13537 * Host::autoCaptureUSBDevices().
13538 *
13539 * Called by Console from the VM process upon VM startup.
13540 *
13541 * @note Locks what called methods lock.
13542 */
13543HRESULT SessionMachine::autoCaptureUSBDevices()
13544{
13545 LogFlowThisFunc(("\n"));
13546
13547#ifdef VBOX_WITH_USB
13548 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13549 AssertComRC(rc);
13550 NOREF(rc);
13551
13552 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13553 AssertReturn(service, E_FAIL);
13554 return service->autoCaptureDevicesForVM(this);
13555#else
13556 return S_OK;
13557#endif
13558}
13559
13560/**
13561 * Removes all machine filters from the USB proxy service and then calls
13562 * Host::detachAllUSBDevices().
13563 *
13564 * Called by Console from the VM process upon normal VM termination or by
13565 * SessionMachine::uninit() upon abnormal VM termination (from under the
13566 * Machine/SessionMachine lock).
13567 *
13568 * @note Locks what called methods lock.
13569 */
13570HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13571{
13572 LogFlowThisFunc(("\n"));
13573
13574#ifdef VBOX_WITH_USB
13575 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13576 AssertComRC(rc);
13577 NOREF(rc);
13578
13579 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13580 AssertReturn(service, E_FAIL);
13581 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13582#else
13583 NOREF(aDone);
13584 return S_OK;
13585#endif
13586}
13587
13588/**
13589 * @note Locks this object for writing.
13590 */
13591HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13592 ComPtr<IProgress> &aProgress)
13593{
13594 LogFlowThisFuncEnter();
13595
13596 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13597 /*
13598 * We don't assert below because it might happen that a non-direct session
13599 * informs us it is closed right after we've been uninitialized -- it's ok.
13600 */
13601
13602 /* get IInternalSessionControl interface */
13603 ComPtr<IInternalSessionControl> control(aSession);
13604
13605 ComAssertRet(!control.isNull(), E_INVALIDARG);
13606
13607 /* Creating a Progress object requires the VirtualBox lock, and
13608 * thus locking it here is required by the lock order rules. */
13609 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13610
13611 if (control == mData->mSession.mDirectControl)
13612 {
13613 /* The direct session is being normally closed by the client process
13614 * ----------------------------------------------------------------- */
13615
13616 /* go to the closing state (essential for all open*Session() calls and
13617 * for #i_checkForDeath()) */
13618 Assert(mData->mSession.mState == SessionState_Locked);
13619 mData->mSession.mState = SessionState_Unlocking;
13620
13621 /* set direct control to NULL to release the remote instance */
13622 mData->mSession.mDirectControl.setNull();
13623 LogFlowThisFunc(("Direct control is set to NULL\n"));
13624
13625 if (mData->mSession.mProgress)
13626 {
13627 /* finalize the progress, someone might wait if a frontend
13628 * closes the session before powering on the VM. */
13629 mData->mSession.mProgress->notifyComplete(E_FAIL,
13630 COM_IIDOF(ISession),
13631 getComponentName(),
13632 tr("The VM session was closed before any attempt to power it on"));
13633 mData->mSession.mProgress.setNull();
13634 }
13635
13636 /* Create the progress object the client will use to wait until
13637 * #i_checkForDeath() is called to uninitialize this session object after
13638 * it releases the IPC semaphore.
13639 * Note! Because we're "reusing" mProgress here, this must be a proxy
13640 * object just like for LaunchVMProcess. */
13641 Assert(mData->mSession.mProgress.isNull());
13642 ComObjPtr<ProgressProxy> progress;
13643 progress.createObject();
13644 ComPtr<IUnknown> pPeer(mPeer);
13645 progress->init(mParent, pPeer,
13646 Bstr(tr("Closing session")).raw(),
13647 FALSE /* aCancelable */);
13648 progress.queryInterfaceTo(aProgress.asOutParam());
13649 mData->mSession.mProgress = progress;
13650 }
13651 else
13652 {
13653 /* the remote session is being normally closed */
13654 bool found = false;
13655 for (Data::Session::RemoteControlList::iterator
13656 it = mData->mSession.mRemoteControls.begin();
13657 it != mData->mSession.mRemoteControls.end();
13658 ++it)
13659 {
13660 if (control == *it)
13661 {
13662 found = true;
13663 // This MUST be erase(it), not remove(*it) as the latter
13664 // triggers a very nasty use after free due to the place where
13665 // the value "lives".
13666 mData->mSession.mRemoteControls.erase(it);
13667 break;
13668 }
13669 }
13670 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13671 E_INVALIDARG);
13672 }
13673
13674 /* signal the client watcher thread, because the client is going away */
13675 mParent->i_updateClientWatcher();
13676
13677 LogFlowThisFuncLeave();
13678 return S_OK;
13679}
13680
13681HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13682 std::vector<com::Utf8Str> &aValues,
13683 std::vector<LONG64> &aTimestamps,
13684 std::vector<com::Utf8Str> &aFlags)
13685{
13686 LogFlowThisFunc(("\n"));
13687
13688#ifdef VBOX_WITH_GUEST_PROPS
13689 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13690
13691 size_t cEntries = mHWData->mGuestProperties.size();
13692 aNames.resize(cEntries);
13693 aValues.resize(cEntries);
13694 aTimestamps.resize(cEntries);
13695 aFlags.resize(cEntries);
13696
13697 size_t i = 0;
13698 for (HWData::GuestPropertyMap::const_iterator
13699 it = mHWData->mGuestProperties.begin();
13700 it != mHWData->mGuestProperties.end();
13701 ++it, ++i)
13702 {
13703 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13704 aNames[i] = it->first;
13705 aValues[i] = it->second.strValue;
13706 aTimestamps[i] = it->second.mTimestamp;
13707
13708 /* If it is NULL, keep it NULL. */
13709 if (it->second.mFlags)
13710 {
13711 GuestPropWriteFlags(it->second.mFlags, szFlags);
13712 aFlags[i] = szFlags;
13713 }
13714 else
13715 aFlags[i] = "";
13716 }
13717 return S_OK;
13718#else
13719 ReturnComNotImplemented();
13720#endif
13721}
13722
13723HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13724 const com::Utf8Str &aValue,
13725 LONG64 aTimestamp,
13726 const com::Utf8Str &aFlags)
13727{
13728 LogFlowThisFunc(("\n"));
13729
13730#ifdef VBOX_WITH_GUEST_PROPS
13731 try
13732 {
13733 /*
13734 * Convert input up front.
13735 */
13736 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13737 if (aFlags.length())
13738 {
13739 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13740 AssertRCReturn(vrc, E_INVALIDARG);
13741 }
13742
13743 /*
13744 * Now grab the object lock, validate the state and do the update.
13745 */
13746
13747 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13748
13749 if (!Global::IsOnline(mData->mMachineState))
13750 {
13751 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13752 VBOX_E_INVALID_VM_STATE);
13753 }
13754
13755 i_setModified(IsModified_MachineData);
13756 mHWData.backup();
13757
13758 bool fDelete = !aValue.length();
13759 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13760 if (it != mHWData->mGuestProperties.end())
13761 {
13762 if (!fDelete)
13763 {
13764 it->second.strValue = aValue;
13765 it->second.mTimestamp = aTimestamp;
13766 it->second.mFlags = fFlags;
13767 }
13768 else
13769 mHWData->mGuestProperties.erase(it);
13770
13771 mData->mGuestPropertiesModified = TRUE;
13772 }
13773 else if (!fDelete)
13774 {
13775 HWData::GuestProperty prop;
13776 prop.strValue = aValue;
13777 prop.mTimestamp = aTimestamp;
13778 prop.mFlags = fFlags;
13779
13780 mHWData->mGuestProperties[aName] = prop;
13781 mData->mGuestPropertiesModified = TRUE;
13782 }
13783
13784 alock.release();
13785
13786 mParent->i_onGuestPropertyChange(mData->mUuid,
13787 Bstr(aName).raw(),
13788 Bstr(aValue).raw(),
13789 Bstr(aFlags).raw());
13790 }
13791 catch (...)
13792 {
13793 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13794 }
13795 return S_OK;
13796#else
13797 ReturnComNotImplemented();
13798#endif
13799}
13800
13801
13802HRESULT SessionMachine::lockMedia()
13803{
13804 AutoMultiWriteLock2 alock(this->lockHandle(),
13805 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13806
13807 AssertReturn( mData->mMachineState == MachineState_Starting
13808 || mData->mMachineState == MachineState_Restoring
13809 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13810
13811 clearError();
13812 alock.release();
13813 return i_lockMedia();
13814}
13815
13816HRESULT SessionMachine::unlockMedia()
13817{
13818 HRESULT hrc = i_unlockMedia();
13819 return hrc;
13820}
13821
13822HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13823 ComPtr<IMediumAttachment> &aNewAttachment)
13824{
13825 // request the host lock first, since might be calling Host methods for getting host drives;
13826 // next, protect the media tree all the while we're in here, as well as our member variables
13827 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13828 this->lockHandle(),
13829 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13830
13831 IMediumAttachment *iAttach = aAttachment;
13832 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13833
13834 Utf8Str ctrlName;
13835 LONG lPort;
13836 LONG lDevice;
13837 bool fTempEject;
13838 {
13839 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13840
13841 /* Need to query the details first, as the IMediumAttachment reference
13842 * might be to the original settings, which we are going to change. */
13843 ctrlName = pAttach->i_getControllerName();
13844 lPort = pAttach->i_getPort();
13845 lDevice = pAttach->i_getDevice();
13846 fTempEject = pAttach->i_getTempEject();
13847 }
13848
13849 if (!fTempEject)
13850 {
13851 /* Remember previously mounted medium. The medium before taking the
13852 * backup is not necessarily the same thing. */
13853 ComObjPtr<Medium> oldmedium;
13854 oldmedium = pAttach->i_getMedium();
13855
13856 i_setModified(IsModified_Storage);
13857 mMediumAttachments.backup();
13858
13859 // The backup operation makes the pAttach reference point to the
13860 // old settings. Re-get the correct reference.
13861 pAttach = i_findAttachment(*mMediumAttachments.data(),
13862 ctrlName,
13863 lPort,
13864 lDevice);
13865
13866 {
13867 AutoCaller autoAttachCaller(this);
13868 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13869
13870 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13871 if (!oldmedium.isNull())
13872 oldmedium->i_removeBackReference(mData->mUuid);
13873
13874 pAttach->i_updateMedium(NULL);
13875 pAttach->i_updateEjected();
13876 }
13877
13878 i_setModified(IsModified_Storage);
13879 }
13880 else
13881 {
13882 {
13883 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13884 pAttach->i_updateEjected();
13885 }
13886 }
13887
13888 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13889
13890 return S_OK;
13891}
13892
13893HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13894 com::Utf8Str &aResult)
13895{
13896 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13897
13898 HRESULT hr = S_OK;
13899
13900 if (!mAuthLibCtx.hAuthLibrary)
13901 {
13902 /* Load the external authentication library. */
13903 Bstr authLibrary;
13904 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13905
13906 Utf8Str filename = authLibrary;
13907
13908 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13909 if (RT_FAILURE(rc))
13910 {
13911 hr = setError(E_FAIL,
13912 tr("Could not load the external authentication library '%s' (%Rrc)"),
13913 filename.c_str(), rc);
13914 }
13915 }
13916
13917 /* The auth library might need the machine lock. */
13918 alock.release();
13919
13920 if (FAILED(hr))
13921 return hr;
13922
13923 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13924 {
13925 enum VRDEAuthParams
13926 {
13927 parmUuid = 1,
13928 parmGuestJudgement,
13929 parmUser,
13930 parmPassword,
13931 parmDomain,
13932 parmClientId
13933 };
13934
13935 AuthResult result = AuthResultAccessDenied;
13936
13937 Guid uuid(aAuthParams[parmUuid]);
13938 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13939 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13940
13941 result = AuthLibAuthenticate(&mAuthLibCtx,
13942 uuid.raw(), guestJudgement,
13943 aAuthParams[parmUser].c_str(),
13944 aAuthParams[parmPassword].c_str(),
13945 aAuthParams[parmDomain].c_str(),
13946 u32ClientId);
13947
13948 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13949 size_t cbPassword = aAuthParams[parmPassword].length();
13950 if (cbPassword)
13951 {
13952 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13953 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13954 }
13955
13956 if (result == AuthResultAccessGranted)
13957 aResult = "granted";
13958 else
13959 aResult = "denied";
13960
13961 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13962 aAuthParams[parmUser].c_str(), aResult.c_str()));
13963 }
13964 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13965 {
13966 enum VRDEAuthDisconnectParams
13967 {
13968 parmUuid = 1,
13969 parmClientId
13970 };
13971
13972 Guid uuid(aAuthParams[parmUuid]);
13973 uint32_t u32ClientId = 0;
13974 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13975 }
13976 else
13977 {
13978 hr = E_INVALIDARG;
13979 }
13980
13981 return hr;
13982}
13983
13984// public methods only for internal purposes
13985/////////////////////////////////////////////////////////////////////////////
13986
13987#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13988/**
13989 * Called from the client watcher thread to check for expected or unexpected
13990 * death of the client process that has a direct session to this machine.
13991 *
13992 * On Win32 and on OS/2, this method is called only when we've got the
13993 * mutex (i.e. the client has either died or terminated normally) so it always
13994 * returns @c true (the client is terminated, the session machine is
13995 * uninitialized).
13996 *
13997 * On other platforms, the method returns @c true if the client process has
13998 * terminated normally or abnormally and the session machine was uninitialized,
13999 * and @c false if the client process is still alive.
14000 *
14001 * @note Locks this object for writing.
14002 */
14003bool SessionMachine::i_checkForDeath()
14004{
14005 Uninit::Reason reason;
14006 bool terminated = false;
14007
14008 /* Enclose autoCaller with a block because calling uninit() from under it
14009 * will deadlock. */
14010 {
14011 AutoCaller autoCaller(this);
14012 if (!autoCaller.isOk())
14013 {
14014 /* return true if not ready, to cause the client watcher to exclude
14015 * the corresponding session from watching */
14016 LogFlowThisFunc(("Already uninitialized!\n"));
14017 return true;
14018 }
14019
14020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14021
14022 /* Determine the reason of death: if the session state is Closing here,
14023 * everything is fine. Otherwise it means that the client did not call
14024 * OnSessionEnd() before it released the IPC semaphore. This may happen
14025 * either because the client process has abnormally terminated, or
14026 * because it simply forgot to call ISession::Close() before exiting. We
14027 * threat the latter also as an abnormal termination (see
14028 * Session::uninit() for details). */
14029 reason = mData->mSession.mState == SessionState_Unlocking ?
14030 Uninit::Normal :
14031 Uninit::Abnormal;
14032
14033 if (mClientToken)
14034 terminated = mClientToken->release();
14035 } /* AutoCaller block */
14036
14037 if (terminated)
14038 uninit(reason);
14039
14040 return terminated;
14041}
14042
14043void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14044{
14045 LogFlowThisFunc(("\n"));
14046
14047 strTokenId.setNull();
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturnVoid(autoCaller.rc());
14051
14052 Assert(mClientToken);
14053 if (mClientToken)
14054 mClientToken->getId(strTokenId);
14055}
14056#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14057IToken *SessionMachine::i_getToken()
14058{
14059 LogFlowThisFunc(("\n"));
14060
14061 AutoCaller autoCaller(this);
14062 AssertComRCReturn(autoCaller.rc(), NULL);
14063
14064 Assert(mClientToken);
14065 if (mClientToken)
14066 return mClientToken->getToken();
14067 else
14068 return NULL;
14069}
14070#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14071
14072Machine::ClientToken *SessionMachine::i_getClientToken()
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077 AssertComRCReturn(autoCaller.rc(), NULL);
14078
14079 return mClientToken;
14080}
14081
14082
14083/**
14084 * @note Locks this object for reading.
14085 */
14086HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14087{
14088 LogFlowThisFunc(("\n"));
14089
14090 AutoCaller autoCaller(this);
14091 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14092
14093 ComPtr<IInternalSessionControl> directControl;
14094 {
14095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14096 if (mData->mSession.mLockType == LockType_VM)
14097 directControl = mData->mSession.mDirectControl;
14098 }
14099
14100 /* ignore notifications sent after #OnSessionEnd() is called */
14101 if (!directControl)
14102 return S_OK;
14103
14104 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14105}
14106
14107/**
14108 * @note Locks this object for reading.
14109 */
14110HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14111 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14112 IN_BSTR aGuestIp, LONG aGuestPort)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 if (mData->mSession.mLockType == LockType_VM)
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129 /*
14130 * instead acting like callback we ask IVirtualBox deliver corresponding event
14131 */
14132
14133 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14134 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14135 return S_OK;
14136}
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 if (mData->mSession.mLockType == LockType_VM)
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158
14159 return directControl->OnAudioAdapterChange(audioAdapter);
14160}
14161
14162/**
14163 * @note Locks this object for reading.
14164 */
14165HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14166{
14167 LogFlowThisFunc(("\n"));
14168
14169 AutoCaller autoCaller(this);
14170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14171
14172 ComPtr<IInternalSessionControl> directControl;
14173 {
14174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14175 if (mData->mSession.mLockType == LockType_VM)
14176 directControl = mData->mSession.mDirectControl;
14177 }
14178
14179 /* ignore notifications sent after #OnSessionEnd() is called */
14180 if (!directControl)
14181 return S_OK;
14182
14183 return directControl->OnSerialPortChange(serialPort);
14184}
14185
14186/**
14187 * @note Locks this object for reading.
14188 */
14189HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14190{
14191 LogFlowThisFunc(("\n"));
14192
14193 AutoCaller autoCaller(this);
14194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14195
14196 ComPtr<IInternalSessionControl> directControl;
14197 {
14198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14199 if (mData->mSession.mLockType == LockType_VM)
14200 directControl = mData->mSession.mDirectControl;
14201 }
14202
14203 /* ignore notifications sent after #OnSessionEnd() is called */
14204 if (!directControl)
14205 return S_OK;
14206
14207 return directControl->OnParallelPortChange(parallelPort);
14208}
14209
14210/**
14211 * @note Locks this object for reading.
14212 */
14213HRESULT SessionMachine::i_onStorageControllerChange()
14214{
14215 LogFlowThisFunc(("\n"));
14216
14217 AutoCaller autoCaller(this);
14218 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14219
14220 ComPtr<IInternalSessionControl> directControl;
14221 {
14222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14223 if (mData->mSession.mLockType == LockType_VM)
14224 directControl = mData->mSession.mDirectControl;
14225 }
14226
14227 /* ignore notifications sent after #OnSessionEnd() is called */
14228 if (!directControl)
14229 return S_OK;
14230
14231 return directControl->OnStorageControllerChange();
14232}
14233
14234/**
14235 * @note Locks this object for reading.
14236 */
14237HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14238{
14239 LogFlowThisFunc(("\n"));
14240
14241 AutoCaller autoCaller(this);
14242 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14243
14244 ComPtr<IInternalSessionControl> directControl;
14245 {
14246 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14247 if (mData->mSession.mLockType == LockType_VM)
14248 directControl = mData->mSession.mDirectControl;
14249 }
14250
14251 /* ignore notifications sent after #OnSessionEnd() is called */
14252 if (!directControl)
14253 return S_OK;
14254
14255 return directControl->OnMediumChange(aAttachment, aForce);
14256}
14257
14258/**
14259 * @note Locks this object for reading.
14260 */
14261HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14262{
14263 LogFlowThisFunc(("\n"));
14264
14265 AutoCaller autoCaller(this);
14266 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14267
14268 ComPtr<IInternalSessionControl> directControl;
14269 {
14270 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14271 if (mData->mSession.mLockType == LockType_VM)
14272 directControl = mData->mSession.mDirectControl;
14273 }
14274
14275 /* ignore notifications sent after #OnSessionEnd() is called */
14276 if (!directControl)
14277 return S_OK;
14278
14279 return directControl->OnCPUChange(aCPU, aRemove);
14280}
14281
14282HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 if (mData->mSession.mLockType == LockType_VM)
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14301}
14302
14303/**
14304 * @note Locks this object for reading.
14305 */
14306HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14307{
14308 LogFlowThisFunc(("\n"));
14309
14310 AutoCaller autoCaller(this);
14311 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14312
14313 ComPtr<IInternalSessionControl> directControl;
14314 {
14315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14316 if (mData->mSession.mLockType == LockType_VM)
14317 directControl = mData->mSession.mDirectControl;
14318 }
14319
14320 /* ignore notifications sent after #OnSessionEnd() is called */
14321 if (!directControl)
14322 return S_OK;
14323
14324 return directControl->OnVRDEServerChange(aRestart);
14325}
14326
14327/**
14328 * @note Locks this object for reading.
14329 */
14330HRESULT SessionMachine::i_onVideoCaptureChange()
14331{
14332 LogFlowThisFunc(("\n"));
14333
14334 AutoCaller autoCaller(this);
14335 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14336
14337 ComPtr<IInternalSessionControl> directControl;
14338 {
14339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14340 if (mData->mSession.mLockType == LockType_VM)
14341 directControl = mData->mSession.mDirectControl;
14342 }
14343
14344 /* ignore notifications sent after #OnSessionEnd() is called */
14345 if (!directControl)
14346 return S_OK;
14347
14348 return directControl->OnVideoCaptureChange();
14349}
14350
14351/**
14352 * @note Locks this object for reading.
14353 */
14354HRESULT SessionMachine::i_onUSBControllerChange()
14355{
14356 LogFlowThisFunc(("\n"));
14357
14358 AutoCaller autoCaller(this);
14359 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14360
14361 ComPtr<IInternalSessionControl> directControl;
14362 {
14363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14364 if (mData->mSession.mLockType == LockType_VM)
14365 directControl = mData->mSession.mDirectControl;
14366 }
14367
14368 /* ignore notifications sent after #OnSessionEnd() is called */
14369 if (!directControl)
14370 return S_OK;
14371
14372 return directControl->OnUSBControllerChange();
14373}
14374
14375/**
14376 * @note Locks this object for reading.
14377 */
14378HRESULT SessionMachine::i_onSharedFolderChange()
14379{
14380 LogFlowThisFunc(("\n"));
14381
14382 AutoCaller autoCaller(this);
14383 AssertComRCReturnRC(autoCaller.rc());
14384
14385 ComPtr<IInternalSessionControl> directControl;
14386 {
14387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14388 if (mData->mSession.mLockType == LockType_VM)
14389 directControl = mData->mSession.mDirectControl;
14390 }
14391
14392 /* ignore notifications sent after #OnSessionEnd() is called */
14393 if (!directControl)
14394 return S_OK;
14395
14396 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14397}
14398
14399/**
14400 * @note Locks this object for reading.
14401 */
14402HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14403{
14404 LogFlowThisFunc(("\n"));
14405
14406 AutoCaller autoCaller(this);
14407 AssertComRCReturnRC(autoCaller.rc());
14408
14409 ComPtr<IInternalSessionControl> directControl;
14410 {
14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14412 if (mData->mSession.mLockType == LockType_VM)
14413 directControl = mData->mSession.mDirectControl;
14414 }
14415
14416 /* ignore notifications sent after #OnSessionEnd() is called */
14417 if (!directControl)
14418 return S_OK;
14419
14420 return directControl->OnClipboardModeChange(aClipboardMode);
14421}
14422
14423/**
14424 * @note Locks this object for reading.
14425 */
14426HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14427{
14428 LogFlowThisFunc(("\n"));
14429
14430 AutoCaller autoCaller(this);
14431 AssertComRCReturnRC(autoCaller.rc());
14432
14433 ComPtr<IInternalSessionControl> directControl;
14434 {
14435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14436 if (mData->mSession.mLockType == LockType_VM)
14437 directControl = mData->mSession.mDirectControl;
14438 }
14439
14440 /* ignore notifications sent after #OnSessionEnd() is called */
14441 if (!directControl)
14442 return S_OK;
14443
14444 return directControl->OnDnDModeChange(aDnDMode);
14445}
14446
14447/**
14448 * @note Locks this object for reading.
14449 */
14450HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14451{
14452 LogFlowThisFunc(("\n"));
14453
14454 AutoCaller autoCaller(this);
14455 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14456
14457 ComPtr<IInternalSessionControl> directControl;
14458 {
14459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14460 if (mData->mSession.mLockType == LockType_VM)
14461 directControl = mData->mSession.mDirectControl;
14462 }
14463
14464 /* ignore notifications sent after #OnSessionEnd() is called */
14465 if (!directControl)
14466 return S_OK;
14467
14468 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14469}
14470
14471/**
14472 * @note Locks this object for reading.
14473 */
14474HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14475{
14476 LogFlowThisFunc(("\n"));
14477
14478 AutoCaller autoCaller(this);
14479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14480
14481 ComPtr<IInternalSessionControl> directControl;
14482 {
14483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14484 if (mData->mSession.mLockType == LockType_VM)
14485 directControl = mData->mSession.mDirectControl;
14486 }
14487
14488 /* ignore notifications sent after #OnSessionEnd() is called */
14489 if (!directControl)
14490 return S_OK;
14491
14492 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14493}
14494
14495/**
14496 * Returns @c true if this machine's USB controller reports it has a matching
14497 * filter for the given USB device and @c false otherwise.
14498 *
14499 * @note locks this object for reading.
14500 */
14501bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14502{
14503 AutoCaller autoCaller(this);
14504 /* silently return if not ready -- this method may be called after the
14505 * direct machine session has been called */
14506 if (!autoCaller.isOk())
14507 return false;
14508
14509#ifdef VBOX_WITH_USB
14510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14511
14512 switch (mData->mMachineState)
14513 {
14514 case MachineState_Starting:
14515 case MachineState_Restoring:
14516 case MachineState_TeleportingIn:
14517 case MachineState_Paused:
14518 case MachineState_Running:
14519 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14520 * elsewhere... */
14521 alock.release();
14522 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14523 default: break;
14524 }
14525#else
14526 NOREF(aDevice);
14527 NOREF(aMaskedIfs);
14528#endif
14529 return false;
14530}
14531
14532/**
14533 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14534 */
14535HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14536 IVirtualBoxErrorInfo *aError,
14537 ULONG aMaskedIfs,
14538 const com::Utf8Str &aCaptureFilename)
14539{
14540 LogFlowThisFunc(("\n"));
14541
14542 AutoCaller autoCaller(this);
14543
14544 /* This notification may happen after the machine object has been
14545 * uninitialized (the session was closed), so don't assert. */
14546 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14547
14548 ComPtr<IInternalSessionControl> directControl;
14549 {
14550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14551 if (mData->mSession.mLockType == LockType_VM)
14552 directControl = mData->mSession.mDirectControl;
14553 }
14554
14555 /* fail on notifications sent after #OnSessionEnd() is called, it is
14556 * expected by the caller */
14557 if (!directControl)
14558 return E_FAIL;
14559
14560 /* No locks should be held at this point. */
14561 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14562 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14563
14564 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14565}
14566
14567/**
14568 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14569 */
14570HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14571 IVirtualBoxErrorInfo *aError)
14572{
14573 LogFlowThisFunc(("\n"));
14574
14575 AutoCaller autoCaller(this);
14576
14577 /* This notification may happen after the machine object has been
14578 * uninitialized (the session was closed), so don't assert. */
14579 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14580
14581 ComPtr<IInternalSessionControl> directControl;
14582 {
14583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14584 if (mData->mSession.mLockType == LockType_VM)
14585 directControl = mData->mSession.mDirectControl;
14586 }
14587
14588 /* fail on notifications sent after #OnSessionEnd() is called, it is
14589 * expected by the caller */
14590 if (!directControl)
14591 return E_FAIL;
14592
14593 /* No locks should be held at this point. */
14594 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14595 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14596
14597 return directControl->OnUSBDeviceDetach(aId, aError);
14598}
14599
14600// protected methods
14601/////////////////////////////////////////////////////////////////////////////
14602
14603/**
14604 * Deletes the given file if it is no longer in use by either the current machine state
14605 * (if the machine is "saved") or any of the machine's snapshots.
14606 *
14607 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14608 * but is different for each SnapshotMachine. When calling this, the order of calling this
14609 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14610 * is therefore critical. I know, it's all rather messy.
14611 *
14612 * @param strStateFile
14613 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14614 * the test for whether the saved state file is in use.
14615 */
14616void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14617 Snapshot *pSnapshotToIgnore)
14618{
14619 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14620 if ( (strStateFile.isNotEmpty())
14621 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14622 )
14623 // ... and it must also not be shared with other snapshots
14624 if ( !mData->mFirstSnapshot
14625 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14626 // this checks the SnapshotMachine's state file paths
14627 )
14628 RTFileDelete(strStateFile.c_str());
14629}
14630
14631/**
14632 * Locks the attached media.
14633 *
14634 * All attached hard disks are locked for writing and DVD/floppy are locked for
14635 * reading. Parents of attached hard disks (if any) are locked for reading.
14636 *
14637 * This method also performs accessibility check of all media it locks: if some
14638 * media is inaccessible, the method will return a failure and a bunch of
14639 * extended error info objects per each inaccessible medium.
14640 *
14641 * Note that this method is atomic: if it returns a success, all media are
14642 * locked as described above; on failure no media is locked at all (all
14643 * succeeded individual locks will be undone).
14644 *
14645 * The caller is responsible for doing the necessary state sanity checks.
14646 *
14647 * The locks made by this method must be undone by calling #unlockMedia() when
14648 * no more needed.
14649 */
14650HRESULT SessionMachine::i_lockMedia()
14651{
14652 AutoCaller autoCaller(this);
14653 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14654
14655 AutoMultiWriteLock2 alock(this->lockHandle(),
14656 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14657
14658 /* bail out if trying to lock things with already set up locking */
14659 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14660
14661 MultiResult mrc(S_OK);
14662
14663 /* Collect locking information for all medium objects attached to the VM. */
14664 for (MediumAttachmentList::const_iterator
14665 it = mMediumAttachments->begin();
14666 it != mMediumAttachments->end();
14667 ++it)
14668 {
14669 MediumAttachment *pAtt = *it;
14670 DeviceType_T devType = pAtt->i_getType();
14671 Medium *pMedium = pAtt->i_getMedium();
14672
14673 MediumLockList *pMediumLockList(new MediumLockList());
14674 // There can be attachments without a medium (floppy/dvd), and thus
14675 // it's impossible to create a medium lock list. It still makes sense
14676 // to have the empty medium lock list in the map in case a medium is
14677 // attached later.
14678 if (pMedium != NULL)
14679 {
14680 MediumType_T mediumType = pMedium->i_getType();
14681 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14682 || mediumType == MediumType_Shareable;
14683 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14684
14685 alock.release();
14686 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14687 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14688 false /* fMediumLockWriteAll */,
14689 NULL,
14690 *pMediumLockList);
14691 alock.acquire();
14692 if (FAILED(mrc))
14693 {
14694 delete pMediumLockList;
14695 mData->mSession.mLockedMedia.Clear();
14696 break;
14697 }
14698 }
14699
14700 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14701 if (FAILED(rc))
14702 {
14703 mData->mSession.mLockedMedia.Clear();
14704 mrc = setError(rc,
14705 tr("Collecting locking information for all attached media failed"));
14706 break;
14707 }
14708 }
14709
14710 if (SUCCEEDED(mrc))
14711 {
14712 /* Now lock all media. If this fails, nothing is locked. */
14713 alock.release();
14714 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14715 alock.acquire();
14716 if (FAILED(rc))
14717 {
14718 mrc = setError(rc,
14719 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14720 }
14721 }
14722
14723 return mrc;
14724}
14725
14726/**
14727 * Undoes the locks made by by #lockMedia().
14728 */
14729HRESULT SessionMachine::i_unlockMedia()
14730{
14731 AutoCaller autoCaller(this);
14732 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14733
14734 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14735
14736 /* we may be holding important error info on the current thread;
14737 * preserve it */
14738 ErrorInfoKeeper eik;
14739
14740 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14741 AssertComRC(rc);
14742 return rc;
14743}
14744
14745/**
14746 * Helper to change the machine state (reimplementation).
14747 *
14748 * @note Locks this object for writing.
14749 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14750 * it can cause crashes in random places due to unexpectedly committing
14751 * the current settings. The caller is responsible for that. The call
14752 * to saveStateSettings is fine, because this method does not commit.
14753 */
14754HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14755{
14756 LogFlowThisFuncEnter();
14757 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14758
14759 AutoCaller autoCaller(this);
14760 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14761
14762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14763
14764 MachineState_T oldMachineState = mData->mMachineState;
14765
14766 AssertMsgReturn(oldMachineState != aMachineState,
14767 ("oldMachineState=%s, aMachineState=%s\n",
14768 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14769 E_FAIL);
14770
14771 HRESULT rc = S_OK;
14772
14773 int stsFlags = 0;
14774 bool deleteSavedState = false;
14775
14776 /* detect some state transitions */
14777
14778 if ( ( oldMachineState == MachineState_Saved
14779 && aMachineState == MachineState_Restoring)
14780 || ( ( oldMachineState == MachineState_PoweredOff
14781 || oldMachineState == MachineState_Teleported
14782 || oldMachineState == MachineState_Aborted
14783 )
14784 && ( aMachineState == MachineState_TeleportingIn
14785 || aMachineState == MachineState_Starting
14786 )
14787 )
14788 )
14789 {
14790 /* The EMT thread is about to start */
14791
14792 /* Nothing to do here for now... */
14793
14794 /// @todo NEWMEDIA don't let mDVDDrive and other children
14795 /// change anything when in the Starting/Restoring state
14796 }
14797 else if ( ( oldMachineState == MachineState_Running
14798 || oldMachineState == MachineState_Paused
14799 || oldMachineState == MachineState_Teleporting
14800 || oldMachineState == MachineState_OnlineSnapshotting
14801 || oldMachineState == MachineState_LiveSnapshotting
14802 || oldMachineState == MachineState_Stuck
14803 || oldMachineState == MachineState_Starting
14804 || oldMachineState == MachineState_Stopping
14805 || oldMachineState == MachineState_Saving
14806 || oldMachineState == MachineState_Restoring
14807 || oldMachineState == MachineState_TeleportingPausedVM
14808 || oldMachineState == MachineState_TeleportingIn
14809 )
14810 && ( aMachineState == MachineState_PoweredOff
14811 || aMachineState == MachineState_Saved
14812 || aMachineState == MachineState_Teleported
14813 || aMachineState == MachineState_Aborted
14814 )
14815 )
14816 {
14817 /* The EMT thread has just stopped, unlock attached media. Note that as
14818 * opposed to locking that is done from Console, we do unlocking here
14819 * because the VM process may have aborted before having a chance to
14820 * properly unlock all media it locked. */
14821
14822 unlockMedia();
14823 }
14824
14825 if (oldMachineState == MachineState_Restoring)
14826 {
14827 if (aMachineState != MachineState_Saved)
14828 {
14829 /*
14830 * delete the saved state file once the machine has finished
14831 * restoring from it (note that Console sets the state from
14832 * Restoring to Saved if the VM couldn't restore successfully,
14833 * to give the user an ability to fix an error and retry --
14834 * we keep the saved state file in this case)
14835 */
14836 deleteSavedState = true;
14837 }
14838 }
14839 else if ( oldMachineState == MachineState_Saved
14840 && ( aMachineState == MachineState_PoweredOff
14841 || aMachineState == MachineState_Aborted
14842 || aMachineState == MachineState_Teleported
14843 )
14844 )
14845 {
14846 /*
14847 * delete the saved state after SessionMachine::ForgetSavedState() is called
14848 * or if the VM process (owning a direct VM session) crashed while the
14849 * VM was Saved
14850 */
14851
14852 /// @todo (dmik)
14853 // Not sure that deleting the saved state file just because of the
14854 // client death before it attempted to restore the VM is a good
14855 // thing. But when it crashes we need to go to the Aborted state
14856 // which cannot have the saved state file associated... The only
14857 // way to fix this is to make the Aborted condition not a VM state
14858 // but a bool flag: i.e., when a crash occurs, set it to true and
14859 // change the state to PoweredOff or Saved depending on the
14860 // saved state presence.
14861
14862 deleteSavedState = true;
14863 mData->mCurrentStateModified = TRUE;
14864 stsFlags |= SaveSTS_CurStateModified;
14865 }
14866
14867 if ( aMachineState == MachineState_Starting
14868 || aMachineState == MachineState_Restoring
14869 || aMachineState == MachineState_TeleportingIn
14870 )
14871 {
14872 /* set the current state modified flag to indicate that the current
14873 * state is no more identical to the state in the
14874 * current snapshot */
14875 if (!mData->mCurrentSnapshot.isNull())
14876 {
14877 mData->mCurrentStateModified = TRUE;
14878 stsFlags |= SaveSTS_CurStateModified;
14879 }
14880 }
14881
14882 if (deleteSavedState)
14883 {
14884 if (mRemoveSavedState)
14885 {
14886 Assert(!mSSData->strStateFilePath.isEmpty());
14887
14888 // it is safe to delete the saved state file if ...
14889 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14890 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14891 // ... none of the snapshots share the saved state file
14892 )
14893 RTFileDelete(mSSData->strStateFilePath.c_str());
14894 }
14895
14896 mSSData->strStateFilePath.setNull();
14897 stsFlags |= SaveSTS_StateFilePath;
14898 }
14899
14900 /* redirect to the underlying peer machine */
14901 mPeer->i_setMachineState(aMachineState);
14902
14903 if ( oldMachineState != MachineState_RestoringSnapshot
14904 && ( aMachineState == MachineState_PoweredOff
14905 || aMachineState == MachineState_Teleported
14906 || aMachineState == MachineState_Aborted
14907 || aMachineState == MachineState_Saved))
14908 {
14909 /* the machine has stopped execution
14910 * (or the saved state file was adopted) */
14911 stsFlags |= SaveSTS_StateTimeStamp;
14912 }
14913
14914 if ( ( oldMachineState == MachineState_PoweredOff
14915 || oldMachineState == MachineState_Aborted
14916 || oldMachineState == MachineState_Teleported
14917 )
14918 && aMachineState == MachineState_Saved)
14919 {
14920 /* the saved state file was adopted */
14921 Assert(!mSSData->strStateFilePath.isEmpty());
14922 stsFlags |= SaveSTS_StateFilePath;
14923 }
14924
14925#ifdef VBOX_WITH_GUEST_PROPS
14926 if ( aMachineState == MachineState_PoweredOff
14927 || aMachineState == MachineState_Aborted
14928 || aMachineState == MachineState_Teleported)
14929 {
14930 /* Make sure any transient guest properties get removed from the
14931 * property store on shutdown. */
14932 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14933
14934 /* remove it from the settings representation */
14935 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14936 for (settings::GuestPropertiesList::iterator
14937 it = llGuestProperties.begin();
14938 it != llGuestProperties.end();
14939 /*nothing*/)
14940 {
14941 const settings::GuestProperty &prop = *it;
14942 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14943 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14944 {
14945 it = llGuestProperties.erase(it);
14946 fNeedsSaving = true;
14947 }
14948 else
14949 {
14950 ++it;
14951 }
14952 }
14953
14954 /* Additionally remove it from the HWData representation. Required to
14955 * keep everything in sync, as this is what the API keeps using. */
14956 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14957 for (HWData::GuestPropertyMap::iterator
14958 it = llHWGuestProperties.begin();
14959 it != llHWGuestProperties.end();
14960 /*nothing*/)
14961 {
14962 uint32_t fFlags = it->second.mFlags;
14963 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14964 {
14965 /* iterator where we need to continue after the erase call
14966 * (C++03 is a fact still, and it doesn't return the iterator
14967 * which would allow continuing) */
14968 HWData::GuestPropertyMap::iterator it2 = it;
14969 ++it2;
14970 llHWGuestProperties.erase(it);
14971 it = it2;
14972 fNeedsSaving = true;
14973 }
14974 else
14975 {
14976 ++it;
14977 }
14978 }
14979
14980 if (fNeedsSaving)
14981 {
14982 mData->mCurrentStateModified = TRUE;
14983 stsFlags |= SaveSTS_CurStateModified;
14984 }
14985 }
14986#endif /* VBOX_WITH_GUEST_PROPS */
14987
14988 rc = i_saveStateSettings(stsFlags);
14989
14990 if ( ( oldMachineState != MachineState_PoweredOff
14991 && oldMachineState != MachineState_Aborted
14992 && oldMachineState != MachineState_Teleported
14993 )
14994 && ( aMachineState == MachineState_PoweredOff
14995 || aMachineState == MachineState_Aborted
14996 || aMachineState == MachineState_Teleported
14997 )
14998 )
14999 {
15000 /* we've been shut down for any reason */
15001 /* no special action so far */
15002 }
15003
15004 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15005 LogFlowThisFuncLeave();
15006 return rc;
15007}
15008
15009/**
15010 * Sends the current machine state value to the VM process.
15011 *
15012 * @note Locks this object for reading, then calls a client process.
15013 */
15014HRESULT SessionMachine::i_updateMachineStateOnClient()
15015{
15016 AutoCaller autoCaller(this);
15017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15018
15019 ComPtr<IInternalSessionControl> directControl;
15020 {
15021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15022 AssertReturn(!!mData, E_FAIL);
15023 if (mData->mSession.mLockType == LockType_VM)
15024 directControl = mData->mSession.mDirectControl;
15025
15026 /* directControl may be already set to NULL here in #OnSessionEnd()
15027 * called too early by the direct session process while there is still
15028 * some operation (like deleting the snapshot) in progress. The client
15029 * process in this case is waiting inside Session::close() for the
15030 * "end session" process object to complete, while #uninit() called by
15031 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15032 * operation to complete. For now, we accept this inconsistent behavior
15033 * and simply do nothing here. */
15034
15035 if (mData->mSession.mState == SessionState_Unlocking)
15036 return S_OK;
15037 }
15038
15039 /* ignore notifications sent after #OnSessionEnd() is called */
15040 if (!directControl)
15041 return S_OK;
15042
15043 return directControl->UpdateMachineState(mData->mMachineState);
15044}
15045
15046
15047/*static*/
15048HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15049{
15050 va_list args;
15051 va_start(args, pcszMsg);
15052 HRESULT rc = setErrorInternal(aResultCode,
15053 getStaticClassIID(),
15054 getStaticComponentName(),
15055 Utf8Str(pcszMsg, args),
15056 false /* aWarning */,
15057 true /* aLogIt */);
15058 va_end(args);
15059 return rc;
15060}
15061
15062
15063HRESULT Machine::updateState(MachineState_T aState)
15064{
15065 NOREF(aState);
15066 ReturnComNotImplemented();
15067}
15068
15069HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15070{
15071 NOREF(aProgress);
15072 ReturnComNotImplemented();
15073}
15074
15075HRESULT Machine::endPowerUp(LONG aResult)
15076{
15077 NOREF(aResult);
15078 ReturnComNotImplemented();
15079}
15080
15081HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15082{
15083 NOREF(aProgress);
15084 ReturnComNotImplemented();
15085}
15086
15087HRESULT Machine::endPoweringDown(LONG aResult,
15088 const com::Utf8Str &aErrMsg)
15089{
15090 NOREF(aResult);
15091 NOREF(aErrMsg);
15092 ReturnComNotImplemented();
15093}
15094
15095HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15096 BOOL *aMatched,
15097 ULONG *aMaskedInterfaces)
15098{
15099 NOREF(aDevice);
15100 NOREF(aMatched);
15101 NOREF(aMaskedInterfaces);
15102 ReturnComNotImplemented();
15103
15104}
15105
15106HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15107{
15108 NOREF(aId); NOREF(aCaptureFilename);
15109 ReturnComNotImplemented();
15110}
15111
15112HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15113 BOOL aDone)
15114{
15115 NOREF(aId);
15116 NOREF(aDone);
15117 ReturnComNotImplemented();
15118}
15119
15120HRESULT Machine::autoCaptureUSBDevices()
15121{
15122 ReturnComNotImplemented();
15123}
15124
15125HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15126{
15127 NOREF(aDone);
15128 ReturnComNotImplemented();
15129}
15130
15131HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15132 ComPtr<IProgress> &aProgress)
15133{
15134 NOREF(aSession);
15135 NOREF(aProgress);
15136 ReturnComNotImplemented();
15137}
15138
15139HRESULT Machine::finishOnlineMergeMedium()
15140{
15141 ReturnComNotImplemented();
15142}
15143
15144HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15145 std::vector<com::Utf8Str> &aValues,
15146 std::vector<LONG64> &aTimestamps,
15147 std::vector<com::Utf8Str> &aFlags)
15148{
15149 NOREF(aNames);
15150 NOREF(aValues);
15151 NOREF(aTimestamps);
15152 NOREF(aFlags);
15153 ReturnComNotImplemented();
15154}
15155
15156HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15157 const com::Utf8Str &aValue,
15158 LONG64 aTimestamp,
15159 const com::Utf8Str &aFlags)
15160{
15161 NOREF(aName);
15162 NOREF(aValue);
15163 NOREF(aTimestamp);
15164 NOREF(aFlags);
15165 ReturnComNotImplemented();
15166}
15167
15168HRESULT Machine::lockMedia()
15169{
15170 ReturnComNotImplemented();
15171}
15172
15173HRESULT Machine::unlockMedia()
15174{
15175 ReturnComNotImplemented();
15176}
15177
15178HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15179 ComPtr<IMediumAttachment> &aNewAttachment)
15180{
15181 NOREF(aAttachment);
15182 NOREF(aNewAttachment);
15183 ReturnComNotImplemented();
15184}
15185
15186HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15187 ULONG aCpuUser,
15188 ULONG aCpuKernel,
15189 ULONG aCpuIdle,
15190 ULONG aMemTotal,
15191 ULONG aMemFree,
15192 ULONG aMemBalloon,
15193 ULONG aMemShared,
15194 ULONG aMemCache,
15195 ULONG aPagedTotal,
15196 ULONG aMemAllocTotal,
15197 ULONG aMemFreeTotal,
15198 ULONG aMemBalloonTotal,
15199 ULONG aMemSharedTotal,
15200 ULONG aVmNetRx,
15201 ULONG aVmNetTx)
15202{
15203 NOREF(aValidStats);
15204 NOREF(aCpuUser);
15205 NOREF(aCpuKernel);
15206 NOREF(aCpuIdle);
15207 NOREF(aMemTotal);
15208 NOREF(aMemFree);
15209 NOREF(aMemBalloon);
15210 NOREF(aMemShared);
15211 NOREF(aMemCache);
15212 NOREF(aPagedTotal);
15213 NOREF(aMemAllocTotal);
15214 NOREF(aMemFreeTotal);
15215 NOREF(aMemBalloonTotal);
15216 NOREF(aMemSharedTotal);
15217 NOREF(aVmNetRx);
15218 NOREF(aVmNetTx);
15219 ReturnComNotImplemented();
15220}
15221
15222HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15223 com::Utf8Str &aResult)
15224{
15225 NOREF(aAuthParams);
15226 NOREF(aResult);
15227 ReturnComNotImplemented();
15228}
15229
15230HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15231{
15232 NOREF(aFlags);
15233 ReturnComNotImplemented();
15234}
15235
15236/* This isn't handled entirely by the wrapper generator yet. */
15237#ifdef VBOX_WITH_XPCOM
15238NS_DECL_CLASSINFO(SessionMachine)
15239NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15240
15241NS_DECL_CLASSINFO(SnapshotMachine)
15242NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15243#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