VirtualBox

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

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

Added speculation control settings to API, refined implementation.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 526.1 KB
Line 
1/* $Id: MachineImpl.cpp 71108 2018-02-22 15:38:35Z 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 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10820 // machine XML is VirtualBox 4.0 or higher:
10821 uuid = i_getId(); // machine UUID
10822 else
10823 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10824
10825 if (pMedium->i_addRegistry(uuid))
10826 mParent->i_markRegistryModified(uuid);
10827
10828 /* For more complex hard disk structures it can happen that the base
10829 * medium isn't yet associated with any medium registry. Do that now. */
10830 if (pMedium != pBase)
10831 {
10832 /* Tree lock needed by Medium::addRegistry when recursing. */
10833 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10834 if (pBase->i_addRegistryRecursive(uuid))
10835 {
10836 treeLock.release();
10837 mParent->i_markRegistryModified(uuid);
10838 }
10839 }
10840}
10841
10842/**
10843 * Creates differencing hard disks for all normal hard disks attached to this
10844 * machine and a new set of attachments to refer to created disks.
10845 *
10846 * Used when taking a snapshot or when deleting the current state. Gets called
10847 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10848 *
10849 * This method assumes that mMediumAttachments contains the original hard disk
10850 * attachments it needs to create diffs for. On success, these attachments will
10851 * be replaced with the created diffs.
10852 *
10853 * Attachments with non-normal hard disks are left as is.
10854 *
10855 * If @a aOnline is @c false then the original hard disks that require implicit
10856 * diffs will be locked for reading. Otherwise it is assumed that they are
10857 * already locked for writing (when the VM was started). Note that in the latter
10858 * case it is responsibility of the caller to lock the newly created diffs for
10859 * writing if this method succeeds.
10860 *
10861 * @param aProgress Progress object to run (must contain at least as
10862 * many operations left as the number of hard disks
10863 * attached).
10864 * @param aWeight Weight of this operation.
10865 * @param aOnline Whether the VM was online prior to this operation.
10866 *
10867 * @note The progress object is not marked as completed, neither on success nor
10868 * on failure. This is a responsibility of the caller.
10869 *
10870 * @note Locks this object and the media tree for writing.
10871 */
10872HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10873 ULONG aWeight,
10874 bool aOnline)
10875{
10876 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10877
10878 AutoCaller autoCaller(this);
10879 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10880
10881 AutoMultiWriteLock2 alock(this->lockHandle(),
10882 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10883
10884 /* must be in a protective state because we release the lock below */
10885 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10886 || mData->mMachineState == MachineState_OnlineSnapshotting
10887 || mData->mMachineState == MachineState_LiveSnapshotting
10888 || mData->mMachineState == MachineState_RestoringSnapshot
10889 || mData->mMachineState == MachineState_DeletingSnapshot
10890 , E_FAIL);
10891
10892 HRESULT rc = S_OK;
10893
10894 // use appropriate locked media map (online or offline)
10895 MediumLockListMap lockedMediaOffline;
10896 MediumLockListMap *lockedMediaMap;
10897 if (aOnline)
10898 lockedMediaMap = &mData->mSession.mLockedMedia;
10899 else
10900 lockedMediaMap = &lockedMediaOffline;
10901
10902 try
10903 {
10904 if (!aOnline)
10905 {
10906 /* lock all attached hard disks early to detect "in use"
10907 * situations before creating actual diffs */
10908 for (MediumAttachmentList::const_iterator
10909 it = mMediumAttachments->begin();
10910 it != mMediumAttachments->end();
10911 ++it)
10912 {
10913 MediumAttachment *pAtt = *it;
10914 if (pAtt->i_getType() == DeviceType_HardDisk)
10915 {
10916 Medium *pMedium = pAtt->i_getMedium();
10917 Assert(pMedium);
10918
10919 MediumLockList *pMediumLockList(new MediumLockList());
10920 alock.release();
10921 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10922 NULL /* pToLockWrite */,
10923 false /* fMediumLockWriteAll */,
10924 NULL,
10925 *pMediumLockList);
10926 alock.acquire();
10927 if (FAILED(rc))
10928 {
10929 delete pMediumLockList;
10930 throw rc;
10931 }
10932 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10933 if (FAILED(rc))
10934 {
10935 throw setError(rc,
10936 tr("Collecting locking information for all attached media failed"));
10937 }
10938 }
10939 }
10940
10941 /* Now lock all media. If this fails, nothing is locked. */
10942 alock.release();
10943 rc = lockedMediaMap->Lock();
10944 alock.acquire();
10945 if (FAILED(rc))
10946 {
10947 throw setError(rc,
10948 tr("Locking of attached media failed"));
10949 }
10950 }
10951
10952 /* remember the current list (note that we don't use backup() since
10953 * mMediumAttachments may be already backed up) */
10954 MediumAttachmentList atts = *mMediumAttachments.data();
10955
10956 /* start from scratch */
10957 mMediumAttachments->clear();
10958
10959 /* go through remembered attachments and create diffs for normal hard
10960 * disks and attach them */
10961 for (MediumAttachmentList::const_iterator
10962 it = atts.begin();
10963 it != atts.end();
10964 ++it)
10965 {
10966 MediumAttachment *pAtt = *it;
10967
10968 DeviceType_T devType = pAtt->i_getType();
10969 Medium *pMedium = pAtt->i_getMedium();
10970
10971 if ( devType != DeviceType_HardDisk
10972 || pMedium == NULL
10973 || pMedium->i_getType() != MediumType_Normal)
10974 {
10975 /* copy the attachment as is */
10976
10977 /** @todo the progress object created in SessionMachine::TakeSnaphot
10978 * only expects operations for hard disks. Later other
10979 * device types need to show up in the progress as well. */
10980 if (devType == DeviceType_HardDisk)
10981 {
10982 if (pMedium == NULL)
10983 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10984 aWeight); // weight
10985 else
10986 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10987 pMedium->i_getBase()->i_getName().c_str()).raw(),
10988 aWeight); // weight
10989 }
10990
10991 mMediumAttachments->push_back(pAtt);
10992 continue;
10993 }
10994
10995 /* need a diff */
10996 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10997 pMedium->i_getBase()->i_getName().c_str()).raw(),
10998 aWeight); // weight
10999
11000 Utf8Str strFullSnapshotFolder;
11001 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11002
11003 ComObjPtr<Medium> diff;
11004 diff.createObject();
11005 // store the diff in the same registry as the parent
11006 // (this cannot fail here because we can't create implicit diffs for
11007 // unregistered images)
11008 Guid uuidRegistryParent;
11009 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11010 Assert(fInRegistry); NOREF(fInRegistry);
11011 rc = diff->init(mParent,
11012 pMedium->i_getPreferredDiffFormat(),
11013 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11014 uuidRegistryParent,
11015 DeviceType_HardDisk);
11016 if (FAILED(rc)) throw rc;
11017
11018 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11019 * the push_back? Looks like we're going to release medium with the
11020 * wrong kind of lock (general issue with if we fail anywhere at all)
11021 * and an orphaned VDI in the snapshots folder. */
11022
11023 /* update the appropriate lock list */
11024 MediumLockList *pMediumLockList;
11025 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11026 AssertComRCThrowRC(rc);
11027 if (aOnline)
11028 {
11029 alock.release();
11030 /* The currently attached medium will be read-only, change
11031 * the lock type to read. */
11032 rc = pMediumLockList->Update(pMedium, false);
11033 alock.acquire();
11034 AssertComRCThrowRC(rc);
11035 }
11036
11037 /* release the locks before the potentially lengthy operation */
11038 alock.release();
11039 rc = pMedium->i_createDiffStorage(diff,
11040 pMedium->i_getPreferredDiffVariant(),
11041 pMediumLockList,
11042 NULL /* aProgress */,
11043 true /* aWait */);
11044 alock.acquire();
11045 if (FAILED(rc)) throw rc;
11046
11047 /* actual lock list update is done in Machine::i_commitMedia */
11048
11049 rc = diff->i_addBackReference(mData->mUuid);
11050 AssertComRCThrowRC(rc);
11051
11052 /* add a new attachment */
11053 ComObjPtr<MediumAttachment> attachment;
11054 attachment.createObject();
11055 rc = attachment->init(this,
11056 diff,
11057 pAtt->i_getControllerName(),
11058 pAtt->i_getPort(),
11059 pAtt->i_getDevice(),
11060 DeviceType_HardDisk,
11061 true /* aImplicit */,
11062 false /* aPassthrough */,
11063 false /* aTempEject */,
11064 pAtt->i_getNonRotational(),
11065 pAtt->i_getDiscard(),
11066 pAtt->i_getHotPluggable(),
11067 pAtt->i_getBandwidthGroup());
11068 if (FAILED(rc)) throw rc;
11069
11070 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11071 AssertComRCThrowRC(rc);
11072 mMediumAttachments->push_back(attachment);
11073 }
11074 }
11075 catch (HRESULT aRC) { rc = aRC; }
11076
11077 /* unlock all hard disks we locked when there is no VM */
11078 if (!aOnline)
11079 {
11080 ErrorInfoKeeper eik;
11081
11082 HRESULT rc1 = lockedMediaMap->Clear();
11083 AssertComRC(rc1);
11084 }
11085
11086 return rc;
11087}
11088
11089/**
11090 * Deletes implicit differencing hard disks created either by
11091 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11092 * mMediumAttachments.
11093 *
11094 * Note that to delete hard disks created by #attachDevice() this method is
11095 * called from #i_rollbackMedia() when the changes are rolled back.
11096 *
11097 * @note Locks this object and the media tree for writing.
11098 */
11099HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11100{
11101 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11102
11103 AutoCaller autoCaller(this);
11104 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11105
11106 AutoMultiWriteLock2 alock(this->lockHandle(),
11107 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11108
11109 /* We absolutely must have backed up state. */
11110 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11111
11112 /* Check if there are any implicitly created diff images. */
11113 bool fImplicitDiffs = false;
11114 for (MediumAttachmentList::const_iterator
11115 it = mMediumAttachments->begin();
11116 it != mMediumAttachments->end();
11117 ++it)
11118 {
11119 const ComObjPtr<MediumAttachment> &pAtt = *it;
11120 if (pAtt->i_isImplicit())
11121 {
11122 fImplicitDiffs = true;
11123 break;
11124 }
11125 }
11126 /* If there is nothing to do, leave early. This saves lots of image locking
11127 * effort. It also avoids a MachineStateChanged event without real reason.
11128 * This is important e.g. when loading a VM config, because there should be
11129 * no events. Otherwise API clients can become thoroughly confused for
11130 * inaccessible VMs (the code for loading VM configs uses this method for
11131 * cleanup if the config makes no sense), as they take such events as an
11132 * indication that the VM is alive, and they would force the VM config to
11133 * be reread, leading to an endless loop. */
11134 if (!fImplicitDiffs)
11135 return S_OK;
11136
11137 HRESULT rc = S_OK;
11138 MachineState_T oldState = mData->mMachineState;
11139
11140 /* will release the lock before the potentially lengthy operation,
11141 * so protect with the special state (unless already protected) */
11142 if ( oldState != MachineState_Snapshotting
11143 && oldState != MachineState_OnlineSnapshotting
11144 && oldState != MachineState_LiveSnapshotting
11145 && oldState != MachineState_RestoringSnapshot
11146 && oldState != MachineState_DeletingSnapshot
11147 && oldState != MachineState_DeletingSnapshotOnline
11148 && oldState != MachineState_DeletingSnapshotPaused
11149 )
11150 i_setMachineState(MachineState_SettingUp);
11151
11152 // use appropriate locked media map (online or offline)
11153 MediumLockListMap lockedMediaOffline;
11154 MediumLockListMap *lockedMediaMap;
11155 if (aOnline)
11156 lockedMediaMap = &mData->mSession.mLockedMedia;
11157 else
11158 lockedMediaMap = &lockedMediaOffline;
11159
11160 try
11161 {
11162 if (!aOnline)
11163 {
11164 /* lock all attached hard disks early to detect "in use"
11165 * situations before deleting actual diffs */
11166 for (MediumAttachmentList::const_iterator
11167 it = mMediumAttachments->begin();
11168 it != mMediumAttachments->end();
11169 ++it)
11170 {
11171 MediumAttachment *pAtt = *it;
11172 if (pAtt->i_getType() == DeviceType_HardDisk)
11173 {
11174 Medium *pMedium = pAtt->i_getMedium();
11175 Assert(pMedium);
11176
11177 MediumLockList *pMediumLockList(new MediumLockList());
11178 alock.release();
11179 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11180 NULL /* pToLockWrite */,
11181 false /* fMediumLockWriteAll */,
11182 NULL,
11183 *pMediumLockList);
11184 alock.acquire();
11185
11186 if (FAILED(rc))
11187 {
11188 delete pMediumLockList;
11189 throw rc;
11190 }
11191
11192 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11193 if (FAILED(rc))
11194 throw rc;
11195 }
11196 }
11197
11198 if (FAILED(rc))
11199 throw rc;
11200 } // end of offline
11201
11202 /* Lock lists are now up to date and include implicitly created media */
11203
11204 /* Go through remembered attachments and delete all implicitly created
11205 * diffs and fix up the attachment information */
11206 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11207 MediumAttachmentList implicitAtts;
11208 for (MediumAttachmentList::const_iterator
11209 it = mMediumAttachments->begin();
11210 it != mMediumAttachments->end();
11211 ++it)
11212 {
11213 ComObjPtr<MediumAttachment> pAtt = *it;
11214 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11215 if (pMedium.isNull())
11216 continue;
11217
11218 // Implicit attachments go on the list for deletion and back references are removed.
11219 if (pAtt->i_isImplicit())
11220 {
11221 /* Deassociate and mark for deletion */
11222 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11223 rc = pMedium->i_removeBackReference(mData->mUuid);
11224 if (FAILED(rc))
11225 throw rc;
11226 implicitAtts.push_back(pAtt);
11227 continue;
11228 }
11229
11230 /* Was this medium attached before? */
11231 if (!i_findAttachment(oldAtts, pMedium))
11232 {
11233 /* no: de-associate */
11234 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11235 rc = pMedium->i_removeBackReference(mData->mUuid);
11236 if (FAILED(rc))
11237 throw rc;
11238 continue;
11239 }
11240 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11241 }
11242
11243 /* If there are implicit attachments to delete, throw away the lock
11244 * map contents (which will unlock all media) since the medium
11245 * attachments will be rolled back. Below we need to completely
11246 * recreate the lock map anyway since it is infinitely complex to
11247 * do this incrementally (would need reconstructing each attachment
11248 * change, which would be extremely hairy). */
11249 if (implicitAtts.size() != 0)
11250 {
11251 ErrorInfoKeeper eik;
11252
11253 HRESULT rc1 = lockedMediaMap->Clear();
11254 AssertComRC(rc1);
11255 }
11256
11257 /* rollback hard disk changes */
11258 mMediumAttachments.rollback();
11259
11260 MultiResult mrc(S_OK);
11261
11262 // Delete unused implicit diffs.
11263 if (implicitAtts.size() != 0)
11264 {
11265 alock.release();
11266
11267 for (MediumAttachmentList::const_iterator
11268 it = implicitAtts.begin();
11269 it != implicitAtts.end();
11270 ++it)
11271 {
11272 // Remove medium associated with this attachment.
11273 ComObjPtr<MediumAttachment> pAtt = *it;
11274 Assert(pAtt);
11275 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11276 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11277 Assert(pMedium);
11278
11279 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11280 // continue on delete failure, just collect error messages
11281 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11282 pMedium->i_getLocationFull().c_str() ));
11283 mrc = rc;
11284 }
11285 // Clear the list of deleted implicit attachments now, while not
11286 // holding the lock, as it will ultimately trigger Medium::uninit()
11287 // calls which assume that the media tree lock isn't held.
11288 implicitAtts.clear();
11289
11290 alock.acquire();
11291
11292 /* if there is a VM recreate media lock map as mentioned above,
11293 * otherwise it is a waste of time and we leave things unlocked */
11294 if (aOnline)
11295 {
11296 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11297 /* must never be NULL, but better safe than sorry */
11298 if (!pMachine.isNull())
11299 {
11300 alock.release();
11301 rc = mData->mSession.mMachine->i_lockMedia();
11302 alock.acquire();
11303 if (FAILED(rc))
11304 throw rc;
11305 }
11306 }
11307 }
11308 }
11309 catch (HRESULT aRC) {rc = aRC;}
11310
11311 if (mData->mMachineState == MachineState_SettingUp)
11312 i_setMachineState(oldState);
11313
11314 /* unlock all hard disks we locked when there is no VM */
11315 if (!aOnline)
11316 {
11317 ErrorInfoKeeper eik;
11318
11319 HRESULT rc1 = lockedMediaMap->Clear();
11320 AssertComRC(rc1);
11321 }
11322
11323 return rc;
11324}
11325
11326
11327/**
11328 * Looks through the given list of media attachments for one with the given parameters
11329 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11330 * can be searched as well if needed.
11331 *
11332 * @param ll
11333 * @param aControllerName
11334 * @param aControllerPort
11335 * @param aDevice
11336 * @return
11337 */
11338MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11339 const Utf8Str &aControllerName,
11340 LONG aControllerPort,
11341 LONG aDevice)
11342{
11343 for (MediumAttachmentList::const_iterator
11344 it = ll.begin();
11345 it != ll.end();
11346 ++it)
11347 {
11348 MediumAttachment *pAttach = *it;
11349 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11350 return pAttach;
11351 }
11352
11353 return NULL;
11354}
11355
11356/**
11357 * Looks through the given list of media attachments for one with the given parameters
11358 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11359 * can be searched as well if needed.
11360 *
11361 * @param ll
11362 * @param pMedium
11363 * @return
11364 */
11365MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11366 ComObjPtr<Medium> pMedium)
11367{
11368 for (MediumAttachmentList::const_iterator
11369 it = ll.begin();
11370 it != ll.end();
11371 ++it)
11372 {
11373 MediumAttachment *pAttach = *it;
11374 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11375 if (pMediumThis == pMedium)
11376 return pAttach;
11377 }
11378
11379 return NULL;
11380}
11381
11382/**
11383 * Looks through the given list of media attachments for one with the given parameters
11384 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11385 * can be searched as well if needed.
11386 *
11387 * @param ll
11388 * @param id
11389 * @return
11390 */
11391MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11392 Guid &id)
11393{
11394 for (MediumAttachmentList::const_iterator
11395 it = ll.begin();
11396 it != ll.end();
11397 ++it)
11398 {
11399 MediumAttachment *pAttach = *it;
11400 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11401 if (pMediumThis->i_getId() == id)
11402 return pAttach;
11403 }
11404
11405 return NULL;
11406}
11407
11408/**
11409 * Main implementation for Machine::DetachDevice. This also gets called
11410 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11411 *
11412 * @param pAttach Medium attachment to detach.
11413 * @param writeLock Machine write lock which the caller must have locked once.
11414 * This may be released temporarily in here.
11415 * @param pSnapshot If NULL, then the detachment is for the current machine.
11416 * Otherwise this is for a SnapshotMachine, and this must be
11417 * its snapshot.
11418 * @return
11419 */
11420HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11421 AutoWriteLock &writeLock,
11422 Snapshot *pSnapshot)
11423{
11424 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11425 DeviceType_T mediumType = pAttach->i_getType();
11426
11427 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11428
11429 if (pAttach->i_isImplicit())
11430 {
11431 /* attempt to implicitly delete the implicitly created diff */
11432
11433 /// @todo move the implicit flag from MediumAttachment to Medium
11434 /// and forbid any hard disk operation when it is implicit. Or maybe
11435 /// a special media state for it to make it even more simple.
11436
11437 Assert(mMediumAttachments.isBackedUp());
11438
11439 /* will release the lock before the potentially lengthy operation, so
11440 * protect with the special state */
11441 MachineState_T oldState = mData->mMachineState;
11442 i_setMachineState(MachineState_SettingUp);
11443
11444 writeLock.release();
11445
11446 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11447 true /*aWait*/);
11448
11449 writeLock.acquire();
11450
11451 i_setMachineState(oldState);
11452
11453 if (FAILED(rc)) return rc;
11454 }
11455
11456 i_setModified(IsModified_Storage);
11457 mMediumAttachments.backup();
11458 mMediumAttachments->remove(pAttach);
11459
11460 if (!oldmedium.isNull())
11461 {
11462 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11463 if (pSnapshot)
11464 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11465 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11466 else if (mediumType != DeviceType_HardDisk)
11467 oldmedium->i_removeBackReference(mData->mUuid);
11468 }
11469
11470 return S_OK;
11471}
11472
11473/**
11474 * Goes thru all media of the given list and
11475 *
11476 * 1) calls i_detachDevice() on each of them for this machine and
11477 * 2) adds all Medium objects found in the process to the given list,
11478 * depending on cleanupMode.
11479 *
11480 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11481 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11482 * media to the list.
11483 *
11484 * This gets called from Machine::Unregister, both for the actual Machine and
11485 * the SnapshotMachine objects that might be found in the snapshots.
11486 *
11487 * Requires caller and locking. The machine lock must be passed in because it
11488 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11489 *
11490 * @param writeLock Machine lock from top-level caller; this gets passed to
11491 * i_detachDevice.
11492 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11493 * object if called for a SnapshotMachine.
11494 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11495 * added to llMedia; if Full, then all media get added;
11496 * otherwise no media get added.
11497 * @param llMedia Caller's list to receive Medium objects which got detached so
11498 * caller can close() them, depending on cleanupMode.
11499 * @return
11500 */
11501HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11502 Snapshot *pSnapshot,
11503 CleanupMode_T cleanupMode,
11504 MediaList &llMedia)
11505{
11506 Assert(isWriteLockOnCurrentThread());
11507
11508 HRESULT rc;
11509
11510 // make a temporary list because i_detachDevice invalidates iterators into
11511 // mMediumAttachments
11512 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11513
11514 for (MediumAttachmentList::iterator
11515 it = llAttachments2.begin();
11516 it != llAttachments2.end();
11517 ++it)
11518 {
11519 ComObjPtr<MediumAttachment> &pAttach = *it;
11520 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11521
11522 if (!pMedium.isNull())
11523 {
11524 AutoCaller mac(pMedium);
11525 if (FAILED(mac.rc())) return mac.rc();
11526 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11527 DeviceType_T devType = pMedium->i_getDeviceType();
11528 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11529 && devType == DeviceType_HardDisk)
11530 || (cleanupMode == CleanupMode_Full)
11531 )
11532 {
11533 llMedia.push_back(pMedium);
11534 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11535 /* Not allowed to keep this lock as below we need the parent
11536 * medium lock, and the lock order is parent to child. */
11537 lock.release();
11538 /*
11539 * Search for medias which are not attached to any machine, but
11540 * in the chain to an attached disk. Mediums are only consided
11541 * if they are:
11542 * - have only one child
11543 * - no references to any machines
11544 * - are of normal medium type
11545 */
11546 while (!pParent.isNull())
11547 {
11548 AutoCaller mac1(pParent);
11549 if (FAILED(mac1.rc())) return mac1.rc();
11550 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11551 if (pParent->i_getChildren().size() == 1)
11552 {
11553 if ( pParent->i_getMachineBackRefCount() == 0
11554 && pParent->i_getType() == MediumType_Normal
11555 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11556 llMedia.push_back(pParent);
11557 }
11558 else
11559 break;
11560 pParent = pParent->i_getParent();
11561 }
11562 }
11563 }
11564
11565 // real machine: then we need to use the proper method
11566 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11567
11568 if (FAILED(rc))
11569 return rc;
11570 }
11571
11572 return S_OK;
11573}
11574
11575/**
11576 * Perform deferred hard disk detachments.
11577 *
11578 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11579 * changed (not backed up).
11580 *
11581 * If @a aOnline is @c true then this method will also unlock the old hard
11582 * disks for which the new implicit diffs were created and will lock these new
11583 * diffs for writing.
11584 *
11585 * @param aOnline Whether the VM was online prior to this operation.
11586 *
11587 * @note Locks this object for writing!
11588 */
11589void Machine::i_commitMedia(bool aOnline /*= false*/)
11590{
11591 AutoCaller autoCaller(this);
11592 AssertComRCReturnVoid(autoCaller.rc());
11593
11594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11595
11596 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11597
11598 HRESULT rc = S_OK;
11599
11600 /* no attach/detach operations -- nothing to do */
11601 if (!mMediumAttachments.isBackedUp())
11602 return;
11603
11604 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11605 bool fMediaNeedsLocking = false;
11606
11607 /* enumerate new attachments */
11608 for (MediumAttachmentList::const_iterator
11609 it = mMediumAttachments->begin();
11610 it != mMediumAttachments->end();
11611 ++it)
11612 {
11613 MediumAttachment *pAttach = *it;
11614
11615 pAttach->i_commit();
11616
11617 Medium *pMedium = pAttach->i_getMedium();
11618 bool fImplicit = pAttach->i_isImplicit();
11619
11620 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11621 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11622 fImplicit));
11623
11624 /** @todo convert all this Machine-based voodoo to MediumAttachment
11625 * based commit logic. */
11626 if (fImplicit)
11627 {
11628 /* convert implicit attachment to normal */
11629 pAttach->i_setImplicit(false);
11630
11631 if ( aOnline
11632 && pMedium
11633 && pAttach->i_getType() == DeviceType_HardDisk
11634 )
11635 {
11636 /* update the appropriate lock list */
11637 MediumLockList *pMediumLockList;
11638 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11639 AssertComRC(rc);
11640 if (pMediumLockList)
11641 {
11642 /* unlock if there's a need to change the locking */
11643 if (!fMediaNeedsLocking)
11644 {
11645 rc = mData->mSession.mLockedMedia.Unlock();
11646 AssertComRC(rc);
11647 fMediaNeedsLocking = true;
11648 }
11649 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11650 AssertComRC(rc);
11651 rc = pMediumLockList->Append(pMedium, true);
11652 AssertComRC(rc);
11653 }
11654 }
11655
11656 continue;
11657 }
11658
11659 if (pMedium)
11660 {
11661 /* was this medium attached before? */
11662 for (MediumAttachmentList::iterator
11663 oldIt = oldAtts.begin();
11664 oldIt != oldAtts.end();
11665 ++oldIt)
11666 {
11667 MediumAttachment *pOldAttach = *oldIt;
11668 if (pOldAttach->i_getMedium() == pMedium)
11669 {
11670 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11671
11672 /* yes: remove from old to avoid de-association */
11673 oldAtts.erase(oldIt);
11674 break;
11675 }
11676 }
11677 }
11678 }
11679
11680 /* enumerate remaining old attachments and de-associate from the
11681 * current machine state */
11682 for (MediumAttachmentList::const_iterator
11683 it = oldAtts.begin();
11684 it != oldAtts.end();
11685 ++it)
11686 {
11687 MediumAttachment *pAttach = *it;
11688 Medium *pMedium = pAttach->i_getMedium();
11689
11690 /* Detach only hard disks, since DVD/floppy media is detached
11691 * instantly in MountMedium. */
11692 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11693 {
11694 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11695
11696 /* now de-associate from the current machine state */
11697 rc = pMedium->i_removeBackReference(mData->mUuid);
11698 AssertComRC(rc);
11699
11700 if (aOnline)
11701 {
11702 /* unlock since medium is not used anymore */
11703 MediumLockList *pMediumLockList;
11704 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11705 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11706 {
11707 /* this happens for online snapshots, there the attachment
11708 * is changing, but only to a diff image created under
11709 * the old one, so there is no separate lock list */
11710 Assert(!pMediumLockList);
11711 }
11712 else
11713 {
11714 AssertComRC(rc);
11715 if (pMediumLockList)
11716 {
11717 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11718 AssertComRC(rc);
11719 }
11720 }
11721 }
11722 }
11723 }
11724
11725 /* take media locks again so that the locking state is consistent */
11726 if (fMediaNeedsLocking)
11727 {
11728 Assert(aOnline);
11729 rc = mData->mSession.mLockedMedia.Lock();
11730 AssertComRC(rc);
11731 }
11732
11733 /* commit the hard disk changes */
11734 mMediumAttachments.commit();
11735
11736 if (i_isSessionMachine())
11737 {
11738 /*
11739 * Update the parent machine to point to the new owner.
11740 * This is necessary because the stored parent will point to the
11741 * session machine otherwise and cause crashes or errors later
11742 * when the session machine gets invalid.
11743 */
11744 /** @todo Change the MediumAttachment class to behave like any other
11745 * class in this regard by creating peer MediumAttachment
11746 * objects for session machines and share the data with the peer
11747 * machine.
11748 */
11749 for (MediumAttachmentList::const_iterator
11750 it = mMediumAttachments->begin();
11751 it != mMediumAttachments->end();
11752 ++it)
11753 (*it)->i_updateParentMachine(mPeer);
11754
11755 /* attach new data to the primary machine and reshare it */
11756 mPeer->mMediumAttachments.attach(mMediumAttachments);
11757 }
11758
11759 return;
11760}
11761
11762/**
11763 * Perform deferred deletion of implicitly created diffs.
11764 *
11765 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11766 * changed (not backed up).
11767 *
11768 * @note Locks this object for writing!
11769 */
11770void Machine::i_rollbackMedia()
11771{
11772 AutoCaller autoCaller(this);
11773 AssertComRCReturnVoid(autoCaller.rc());
11774
11775 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11776 LogFlowThisFunc(("Entering rollbackMedia\n"));
11777
11778 HRESULT rc = S_OK;
11779
11780 /* no attach/detach operations -- nothing to do */
11781 if (!mMediumAttachments.isBackedUp())
11782 return;
11783
11784 /* enumerate new attachments */
11785 for (MediumAttachmentList::const_iterator
11786 it = mMediumAttachments->begin();
11787 it != mMediumAttachments->end();
11788 ++it)
11789 {
11790 MediumAttachment *pAttach = *it;
11791 /* Fix up the backrefs for DVD/floppy media. */
11792 if (pAttach->i_getType() != DeviceType_HardDisk)
11793 {
11794 Medium *pMedium = pAttach->i_getMedium();
11795 if (pMedium)
11796 {
11797 rc = pMedium->i_removeBackReference(mData->mUuid);
11798 AssertComRC(rc);
11799 }
11800 }
11801
11802 (*it)->i_rollback();
11803
11804 pAttach = *it;
11805 /* Fix up the backrefs for DVD/floppy media. */
11806 if (pAttach->i_getType() != DeviceType_HardDisk)
11807 {
11808 Medium *pMedium = pAttach->i_getMedium();
11809 if (pMedium)
11810 {
11811 rc = pMedium->i_addBackReference(mData->mUuid);
11812 AssertComRC(rc);
11813 }
11814 }
11815 }
11816
11817 /** @todo convert all this Machine-based voodoo to MediumAttachment
11818 * based rollback logic. */
11819 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11820
11821 return;
11822}
11823
11824/**
11825 * Returns true if the settings file is located in the directory named exactly
11826 * as the machine; this means, among other things, that the machine directory
11827 * should be auto-renamed.
11828 *
11829 * @param aSettingsDir if not NULL, the full machine settings file directory
11830 * name will be assigned there.
11831 *
11832 * @note Doesn't lock anything.
11833 * @note Not thread safe (must be called from this object's lock).
11834 */
11835bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11836{
11837 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11838 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11839 if (aSettingsDir)
11840 *aSettingsDir = strMachineDirName;
11841 strMachineDirName.stripPath(); // vmname
11842 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11843 strConfigFileOnly.stripPath() // vmname.vbox
11844 .stripSuffix(); // vmname
11845 /** @todo hack, make somehow use of ComposeMachineFilename */
11846 if (mUserData->s.fDirectoryIncludesUUID)
11847 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11848
11849 AssertReturn(!strMachineDirName.isEmpty(), false);
11850 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11851
11852 return strMachineDirName == strConfigFileOnly;
11853}
11854
11855/**
11856 * Discards all changes to machine settings.
11857 *
11858 * @param aNotify Whether to notify the direct session about changes or not.
11859 *
11860 * @note Locks objects for writing!
11861 */
11862void Machine::i_rollback(bool aNotify)
11863{
11864 AutoCaller autoCaller(this);
11865 AssertComRCReturn(autoCaller.rc(), (void)0);
11866
11867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11868
11869 if (!mStorageControllers.isNull())
11870 {
11871 if (mStorageControllers.isBackedUp())
11872 {
11873 /* unitialize all new devices (absent in the backed up list). */
11874 StorageControllerList *backedList = mStorageControllers.backedUpData();
11875 for (StorageControllerList::const_iterator
11876 it = mStorageControllers->begin();
11877 it != mStorageControllers->end();
11878 ++it)
11879 {
11880 if ( std::find(backedList->begin(), backedList->end(), *it)
11881 == backedList->end()
11882 )
11883 {
11884 (*it)->uninit();
11885 }
11886 }
11887
11888 /* restore the list */
11889 mStorageControllers.rollback();
11890 }
11891
11892 /* rollback any changes to devices after restoring the list */
11893 if (mData->flModifications & IsModified_Storage)
11894 {
11895 for (StorageControllerList::const_iterator
11896 it = mStorageControllers->begin();
11897 it != mStorageControllers->end();
11898 ++it)
11899 {
11900 (*it)->i_rollback();
11901 }
11902 }
11903 }
11904
11905 if (!mUSBControllers.isNull())
11906 {
11907 if (mUSBControllers.isBackedUp())
11908 {
11909 /* unitialize all new devices (absent in the backed up list). */
11910 USBControllerList *backedList = mUSBControllers.backedUpData();
11911 for (USBControllerList::const_iterator
11912 it = mUSBControllers->begin();
11913 it != mUSBControllers->end();
11914 ++it)
11915 {
11916 if ( std::find(backedList->begin(), backedList->end(), *it)
11917 == backedList->end()
11918 )
11919 {
11920 (*it)->uninit();
11921 }
11922 }
11923
11924 /* restore the list */
11925 mUSBControllers.rollback();
11926 }
11927
11928 /* rollback any changes to devices after restoring the list */
11929 if (mData->flModifications & IsModified_USB)
11930 {
11931 for (USBControllerList::const_iterator
11932 it = mUSBControllers->begin();
11933 it != mUSBControllers->end();
11934 ++it)
11935 {
11936 (*it)->i_rollback();
11937 }
11938 }
11939 }
11940
11941 mUserData.rollback();
11942
11943 mHWData.rollback();
11944
11945 if (mData->flModifications & IsModified_Storage)
11946 i_rollbackMedia();
11947
11948 if (mBIOSSettings)
11949 mBIOSSettings->i_rollback();
11950
11951 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11952 mVRDEServer->i_rollback();
11953
11954 if (mAudioAdapter)
11955 mAudioAdapter->i_rollback();
11956
11957 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11958 mUSBDeviceFilters->i_rollback();
11959
11960 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11961 mBandwidthControl->i_rollback();
11962
11963 if (!mHWData.isNull())
11964 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11965 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11966 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11967 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11968
11969 if (mData->flModifications & IsModified_NetworkAdapters)
11970 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11971 if ( mNetworkAdapters[slot]
11972 && mNetworkAdapters[slot]->i_isModified())
11973 {
11974 mNetworkAdapters[slot]->i_rollback();
11975 networkAdapters[slot] = mNetworkAdapters[slot];
11976 }
11977
11978 if (mData->flModifications & IsModified_SerialPorts)
11979 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11980 if ( mSerialPorts[slot]
11981 && mSerialPorts[slot]->i_isModified())
11982 {
11983 mSerialPorts[slot]->i_rollback();
11984 serialPorts[slot] = mSerialPorts[slot];
11985 }
11986
11987 if (mData->flModifications & IsModified_ParallelPorts)
11988 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11989 if ( mParallelPorts[slot]
11990 && mParallelPorts[slot]->i_isModified())
11991 {
11992 mParallelPorts[slot]->i_rollback();
11993 parallelPorts[slot] = mParallelPorts[slot];
11994 }
11995
11996 if (aNotify)
11997 {
11998 /* inform the direct session about changes */
11999
12000 ComObjPtr<Machine> that = this;
12001 uint32_t flModifications = mData->flModifications;
12002 alock.release();
12003
12004 if (flModifications & IsModified_SharedFolders)
12005 that->i_onSharedFolderChange();
12006
12007 if (flModifications & IsModified_VRDEServer)
12008 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12009 if (flModifications & IsModified_USB)
12010 that->i_onUSBControllerChange();
12011
12012 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12013 if (networkAdapters[slot])
12014 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12015 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12016 if (serialPorts[slot])
12017 that->i_onSerialPortChange(serialPorts[slot]);
12018 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12019 if (parallelPorts[slot])
12020 that->i_onParallelPortChange(parallelPorts[slot]);
12021
12022 if (flModifications & IsModified_Storage)
12023 that->i_onStorageControllerChange();
12024
12025#if 0
12026 if (flModifications & IsModified_BandwidthControl)
12027 that->onBandwidthControlChange();
12028#endif
12029 }
12030}
12031
12032/**
12033 * Commits all the changes to machine settings.
12034 *
12035 * Note that this operation is supposed to never fail.
12036 *
12037 * @note Locks this object and children for writing.
12038 */
12039void Machine::i_commit()
12040{
12041 AutoCaller autoCaller(this);
12042 AssertComRCReturnVoid(autoCaller.rc());
12043
12044 AutoCaller peerCaller(mPeer);
12045 AssertComRCReturnVoid(peerCaller.rc());
12046
12047 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12048
12049 /*
12050 * use safe commit to ensure Snapshot machines (that share mUserData)
12051 * will still refer to a valid memory location
12052 */
12053 mUserData.commitCopy();
12054
12055 mHWData.commit();
12056
12057 if (mMediumAttachments.isBackedUp())
12058 i_commitMedia(Global::IsOnline(mData->mMachineState));
12059
12060 mBIOSSettings->i_commit();
12061 mVRDEServer->i_commit();
12062 mAudioAdapter->i_commit();
12063 mUSBDeviceFilters->i_commit();
12064 mBandwidthControl->i_commit();
12065
12066 /* Since mNetworkAdapters is a list which might have been changed (resized)
12067 * without using the Backupable<> template we need to handle the copying
12068 * of the list entries manually, including the creation of peers for the
12069 * new objects. */
12070 bool commitNetworkAdapters = false;
12071 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12072 if (mPeer)
12073 {
12074 /* commit everything, even the ones which will go away */
12075 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12076 mNetworkAdapters[slot]->i_commit();
12077 /* copy over the new entries, creating a peer and uninit the original */
12078 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12079 for (size_t slot = 0; slot < newSize; slot++)
12080 {
12081 /* look if this adapter has a peer device */
12082 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12083 if (!peer)
12084 {
12085 /* no peer means the adapter is a newly created one;
12086 * create a peer owning data this data share it with */
12087 peer.createObject();
12088 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12089 }
12090 mPeer->mNetworkAdapters[slot] = peer;
12091 }
12092 /* uninit any no longer needed network adapters */
12093 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12094 mNetworkAdapters[slot]->uninit();
12095 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12096 {
12097 if (mPeer->mNetworkAdapters[slot])
12098 mPeer->mNetworkAdapters[slot]->uninit();
12099 }
12100 /* Keep the original network adapter count until this point, so that
12101 * discarding a chipset type change will not lose settings. */
12102 mNetworkAdapters.resize(newSize);
12103 mPeer->mNetworkAdapters.resize(newSize);
12104 }
12105 else
12106 {
12107 /* we have no peer (our parent is the newly created machine);
12108 * just commit changes to the network adapters */
12109 commitNetworkAdapters = true;
12110 }
12111 if (commitNetworkAdapters)
12112 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12113 mNetworkAdapters[slot]->i_commit();
12114
12115 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12116 mSerialPorts[slot]->i_commit();
12117 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12118 mParallelPorts[slot]->i_commit();
12119
12120 bool commitStorageControllers = false;
12121
12122 if (mStorageControllers.isBackedUp())
12123 {
12124 mStorageControllers.commit();
12125
12126 if (mPeer)
12127 {
12128 /* Commit all changes to new controllers (this will reshare data with
12129 * peers for those who have peers) */
12130 StorageControllerList *newList = new StorageControllerList();
12131 for (StorageControllerList::const_iterator
12132 it = mStorageControllers->begin();
12133 it != mStorageControllers->end();
12134 ++it)
12135 {
12136 (*it)->i_commit();
12137
12138 /* look if this controller has a peer device */
12139 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12140 if (!peer)
12141 {
12142 /* no peer means the device is a newly created one;
12143 * create a peer owning data this device share it with */
12144 peer.createObject();
12145 peer->init(mPeer, *it, true /* aReshare */);
12146 }
12147 else
12148 {
12149 /* remove peer from the old list */
12150 mPeer->mStorageControllers->remove(peer);
12151 }
12152 /* and add it to the new list */
12153 newList->push_back(peer);
12154 }
12155
12156 /* uninit old peer's controllers that are left */
12157 for (StorageControllerList::const_iterator
12158 it = mPeer->mStorageControllers->begin();
12159 it != mPeer->mStorageControllers->end();
12160 ++it)
12161 {
12162 (*it)->uninit();
12163 }
12164
12165 /* attach new list of controllers to our peer */
12166 mPeer->mStorageControllers.attach(newList);
12167 }
12168 else
12169 {
12170 /* we have no peer (our parent is the newly created machine);
12171 * just commit changes to devices */
12172 commitStorageControllers = true;
12173 }
12174 }
12175 else
12176 {
12177 /* the list of controllers itself is not changed,
12178 * just commit changes to controllers themselves */
12179 commitStorageControllers = true;
12180 }
12181
12182 if (commitStorageControllers)
12183 {
12184 for (StorageControllerList::const_iterator
12185 it = mStorageControllers->begin();
12186 it != mStorageControllers->end();
12187 ++it)
12188 {
12189 (*it)->i_commit();
12190 }
12191 }
12192
12193 bool commitUSBControllers = false;
12194
12195 if (mUSBControllers.isBackedUp())
12196 {
12197 mUSBControllers.commit();
12198
12199 if (mPeer)
12200 {
12201 /* Commit all changes to new controllers (this will reshare data with
12202 * peers for those who have peers) */
12203 USBControllerList *newList = new USBControllerList();
12204 for (USBControllerList::const_iterator
12205 it = mUSBControllers->begin();
12206 it != mUSBControllers->end();
12207 ++it)
12208 {
12209 (*it)->i_commit();
12210
12211 /* look if this controller has a peer device */
12212 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12213 if (!peer)
12214 {
12215 /* no peer means the device is a newly created one;
12216 * create a peer owning data this device share it with */
12217 peer.createObject();
12218 peer->init(mPeer, *it, true /* aReshare */);
12219 }
12220 else
12221 {
12222 /* remove peer from the old list */
12223 mPeer->mUSBControllers->remove(peer);
12224 }
12225 /* and add it to the new list */
12226 newList->push_back(peer);
12227 }
12228
12229 /* uninit old peer's controllers that are left */
12230 for (USBControllerList::const_iterator
12231 it = mPeer->mUSBControllers->begin();
12232 it != mPeer->mUSBControllers->end();
12233 ++it)
12234 {
12235 (*it)->uninit();
12236 }
12237
12238 /* attach new list of controllers to our peer */
12239 mPeer->mUSBControllers.attach(newList);
12240 }
12241 else
12242 {
12243 /* we have no peer (our parent is the newly created machine);
12244 * just commit changes to devices */
12245 commitUSBControllers = true;
12246 }
12247 }
12248 else
12249 {
12250 /* the list of controllers itself is not changed,
12251 * just commit changes to controllers themselves */
12252 commitUSBControllers = true;
12253 }
12254
12255 if (commitUSBControllers)
12256 {
12257 for (USBControllerList::const_iterator
12258 it = mUSBControllers->begin();
12259 it != mUSBControllers->end();
12260 ++it)
12261 {
12262 (*it)->i_commit();
12263 }
12264 }
12265
12266 if (i_isSessionMachine())
12267 {
12268 /* attach new data to the primary machine and reshare it */
12269 mPeer->mUserData.attach(mUserData);
12270 mPeer->mHWData.attach(mHWData);
12271 /* mmMediumAttachments is reshared by fixupMedia */
12272 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12273 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12274 }
12275}
12276
12277/**
12278 * Copies all the hardware data from the given machine.
12279 *
12280 * Currently, only called when the VM is being restored from a snapshot. In
12281 * particular, this implies that the VM is not running during this method's
12282 * call.
12283 *
12284 * @note This method must be called from under this object's lock.
12285 *
12286 * @note This method doesn't call #i_commit(), so all data remains backed up and
12287 * unsaved.
12288 */
12289void Machine::i_copyFrom(Machine *aThat)
12290{
12291 AssertReturnVoid(!i_isSnapshotMachine());
12292 AssertReturnVoid(aThat->i_isSnapshotMachine());
12293
12294 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12295
12296 mHWData.assignCopy(aThat->mHWData);
12297
12298 // create copies of all shared folders (mHWData after attaching a copy
12299 // contains just references to original objects)
12300 for (HWData::SharedFolderList::iterator
12301 it = mHWData->mSharedFolders.begin();
12302 it != mHWData->mSharedFolders.end();
12303 ++it)
12304 {
12305 ComObjPtr<SharedFolder> folder;
12306 folder.createObject();
12307 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12308 AssertComRC(rc);
12309 *it = folder;
12310 }
12311
12312 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12313 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12314 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12315 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12316 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12317
12318 /* create private copies of all controllers */
12319 mStorageControllers.backup();
12320 mStorageControllers->clear();
12321 for (StorageControllerList::const_iterator
12322 it = aThat->mStorageControllers->begin();
12323 it != aThat->mStorageControllers->end();
12324 ++it)
12325 {
12326 ComObjPtr<StorageController> ctrl;
12327 ctrl.createObject();
12328 ctrl->initCopy(this, *it);
12329 mStorageControllers->push_back(ctrl);
12330 }
12331
12332 /* create private copies of all USB controllers */
12333 mUSBControllers.backup();
12334 mUSBControllers->clear();
12335 for (USBControllerList::const_iterator
12336 it = aThat->mUSBControllers->begin();
12337 it != aThat->mUSBControllers->end();
12338 ++it)
12339 {
12340 ComObjPtr<USBController> ctrl;
12341 ctrl.createObject();
12342 ctrl->initCopy(this, *it);
12343 mUSBControllers->push_back(ctrl);
12344 }
12345
12346 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12347 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12348 {
12349 if (mNetworkAdapters[slot].isNotNull())
12350 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12351 else
12352 {
12353 unconst(mNetworkAdapters[slot]).createObject();
12354 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12355 }
12356 }
12357 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12358 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12359 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12360 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12361}
12362
12363/**
12364 * Returns whether the given storage controller is hotplug capable.
12365 *
12366 * @returns true if the controller supports hotplugging
12367 * false otherwise.
12368 * @param enmCtrlType The controller type to check for.
12369 */
12370bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12371{
12372 ComPtr<ISystemProperties> systemProperties;
12373 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12374 if (FAILED(rc))
12375 return false;
12376
12377 BOOL aHotplugCapable = FALSE;
12378 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12379
12380 return RT_BOOL(aHotplugCapable);
12381}
12382
12383#ifdef VBOX_WITH_RESOURCE_USAGE_API
12384
12385void Machine::i_getDiskList(MediaList &list)
12386{
12387 for (MediumAttachmentList::const_iterator
12388 it = mMediumAttachments->begin();
12389 it != mMediumAttachments->end();
12390 ++it)
12391 {
12392 MediumAttachment *pAttach = *it;
12393 /* just in case */
12394 AssertContinue(pAttach);
12395
12396 AutoCaller localAutoCallerA(pAttach);
12397 if (FAILED(localAutoCallerA.rc())) continue;
12398
12399 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12400
12401 if (pAttach->i_getType() == DeviceType_HardDisk)
12402 list.push_back(pAttach->i_getMedium());
12403 }
12404}
12405
12406void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12407{
12408 AssertReturnVoid(isWriteLockOnCurrentThread());
12409 AssertPtrReturnVoid(aCollector);
12410
12411 pm::CollectorHAL *hal = aCollector->getHAL();
12412 /* Create sub metrics */
12413 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12414 "Percentage of processor time spent in user mode by the VM process.");
12415 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12416 "Percentage of processor time spent in kernel mode by the VM process.");
12417 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12418 "Size of resident portion of VM process in memory.");
12419 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12420 "Actual size of all VM disks combined.");
12421 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12422 "Network receive rate.");
12423 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12424 "Network transmit rate.");
12425 /* Create and register base metrics */
12426 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12427 cpuLoadUser, cpuLoadKernel);
12428 aCollector->registerBaseMetric(cpuLoad);
12429 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12430 ramUsageUsed);
12431 aCollector->registerBaseMetric(ramUsage);
12432 MediaList disks;
12433 i_getDiskList(disks);
12434 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12435 diskUsageUsed);
12436 aCollector->registerBaseMetric(diskUsage);
12437
12438 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12439 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12440 new pm::AggregateAvg()));
12441 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12442 new pm::AggregateMin()));
12443 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12444 new pm::AggregateMax()));
12445 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12446 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12447 new pm::AggregateAvg()));
12448 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12449 new pm::AggregateMin()));
12450 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12451 new pm::AggregateMax()));
12452
12453 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12454 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12455 new pm::AggregateAvg()));
12456 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12457 new pm::AggregateMin()));
12458 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12459 new pm::AggregateMax()));
12460
12461 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12462 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12463 new pm::AggregateAvg()));
12464 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12465 new pm::AggregateMin()));
12466 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12467 new pm::AggregateMax()));
12468
12469
12470 /* Guest metrics collector */
12471 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12472 aCollector->registerGuest(mCollectorGuest);
12473 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12474
12475 /* Create sub metrics */
12476 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12477 "Percentage of processor time spent in user mode as seen by the guest.");
12478 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12479 "Percentage of processor time spent in kernel mode as seen by the guest.");
12480 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12481 "Percentage of processor time spent idling as seen by the guest.");
12482
12483 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12484 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12485 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12486 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12487 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12488 pm::SubMetric *guestMemCache = new pm::SubMetric(
12489 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12490
12491 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12492 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12493
12494 /* Create and register base metrics */
12495 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12496 machineNetRx, machineNetTx);
12497 aCollector->registerBaseMetric(machineNetRate);
12498
12499 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12500 guestLoadUser, guestLoadKernel, guestLoadIdle);
12501 aCollector->registerBaseMetric(guestCpuLoad);
12502
12503 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12504 guestMemTotal, guestMemFree,
12505 guestMemBalloon, guestMemShared,
12506 guestMemCache, guestPagedTotal);
12507 aCollector->registerBaseMetric(guestCpuMem);
12508
12509 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12510 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12511 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12512 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12513
12514 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12515 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12516 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12517 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12518
12519 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12520 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12521 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12522 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12523
12524 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12525 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12526 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12527 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12528
12529 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12530 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12531 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12532 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12533
12534 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12535 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12536 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12537 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12538
12539 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12540 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12541 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12542 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12543
12544 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12545 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12546 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12547 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12548
12549 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12550 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12551 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12552 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12553
12554 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12555 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12556 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12557 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12558
12559 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12560 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12561 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12562 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12563}
12564
12565void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12566{
12567 AssertReturnVoid(isWriteLockOnCurrentThread());
12568
12569 if (aCollector)
12570 {
12571 aCollector->unregisterMetricsFor(aMachine);
12572 aCollector->unregisterBaseMetricsFor(aMachine);
12573 }
12574}
12575
12576#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12577
12578
12579////////////////////////////////////////////////////////////////////////////////
12580
12581DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12582
12583HRESULT SessionMachine::FinalConstruct()
12584{
12585 LogFlowThisFunc(("\n"));
12586
12587 mClientToken = NULL;
12588
12589 return BaseFinalConstruct();
12590}
12591
12592void SessionMachine::FinalRelease()
12593{
12594 LogFlowThisFunc(("\n"));
12595
12596 Assert(!mClientToken);
12597 /* paranoia, should not hang around any more */
12598 if (mClientToken)
12599 {
12600 delete mClientToken;
12601 mClientToken = NULL;
12602 }
12603
12604 uninit(Uninit::Unexpected);
12605
12606 BaseFinalRelease();
12607}
12608
12609/**
12610 * @note Must be called only by Machine::LockMachine() from its own write lock.
12611 */
12612HRESULT SessionMachine::init(Machine *aMachine)
12613{
12614 LogFlowThisFuncEnter();
12615 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12616
12617 AssertReturn(aMachine, E_INVALIDARG);
12618
12619 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12620
12621 /* Enclose the state transition NotReady->InInit->Ready */
12622 AutoInitSpan autoInitSpan(this);
12623 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12624
12625 HRESULT rc = S_OK;
12626
12627 RT_ZERO(mAuthLibCtx);
12628
12629 /* create the machine client token */
12630 try
12631 {
12632 mClientToken = new ClientToken(aMachine, this);
12633 if (!mClientToken->isReady())
12634 {
12635 delete mClientToken;
12636 mClientToken = NULL;
12637 rc = E_FAIL;
12638 }
12639 }
12640 catch (std::bad_alloc &)
12641 {
12642 rc = E_OUTOFMEMORY;
12643 }
12644 if (FAILED(rc))
12645 return rc;
12646
12647 /* memorize the peer Machine */
12648 unconst(mPeer) = aMachine;
12649 /* share the parent pointer */
12650 unconst(mParent) = aMachine->mParent;
12651
12652 /* take the pointers to data to share */
12653 mData.share(aMachine->mData);
12654 mSSData.share(aMachine->mSSData);
12655
12656 mUserData.share(aMachine->mUserData);
12657 mHWData.share(aMachine->mHWData);
12658 mMediumAttachments.share(aMachine->mMediumAttachments);
12659
12660 mStorageControllers.allocate();
12661 for (StorageControllerList::const_iterator
12662 it = aMachine->mStorageControllers->begin();
12663 it != aMachine->mStorageControllers->end();
12664 ++it)
12665 {
12666 ComObjPtr<StorageController> ctl;
12667 ctl.createObject();
12668 ctl->init(this, *it);
12669 mStorageControllers->push_back(ctl);
12670 }
12671
12672 mUSBControllers.allocate();
12673 for (USBControllerList::const_iterator
12674 it = aMachine->mUSBControllers->begin();
12675 it != aMachine->mUSBControllers->end();
12676 ++it)
12677 {
12678 ComObjPtr<USBController> ctl;
12679 ctl.createObject();
12680 ctl->init(this, *it);
12681 mUSBControllers->push_back(ctl);
12682 }
12683
12684 unconst(mBIOSSettings).createObject();
12685 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12686 /* create another VRDEServer object that will be mutable */
12687 unconst(mVRDEServer).createObject();
12688 mVRDEServer->init(this, aMachine->mVRDEServer);
12689 /* create another audio adapter object that will be mutable */
12690 unconst(mAudioAdapter).createObject();
12691 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12692 /* create a list of serial ports that will be mutable */
12693 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12694 {
12695 unconst(mSerialPorts[slot]).createObject();
12696 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12697 }
12698 /* create a list of parallel ports that will be mutable */
12699 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12700 {
12701 unconst(mParallelPorts[slot]).createObject();
12702 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12703 }
12704
12705 /* create another USB device filters object that will be mutable */
12706 unconst(mUSBDeviceFilters).createObject();
12707 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12708
12709 /* create a list of network adapters that will be mutable */
12710 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12711 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12712 {
12713 unconst(mNetworkAdapters[slot]).createObject();
12714 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12715 }
12716
12717 /* create another bandwidth control object that will be mutable */
12718 unconst(mBandwidthControl).createObject();
12719 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12720
12721 /* default is to delete saved state on Saved -> PoweredOff transition */
12722 mRemoveSavedState = true;
12723
12724 /* Confirm a successful initialization when it's the case */
12725 autoInitSpan.setSucceeded();
12726
12727 miNATNetworksStarted = 0;
12728
12729 LogFlowThisFuncLeave();
12730 return rc;
12731}
12732
12733/**
12734 * Uninitializes this session object. If the reason is other than
12735 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12736 * or the client watcher code.
12737 *
12738 * @param aReason uninitialization reason
12739 *
12740 * @note Locks mParent + this object for writing.
12741 */
12742void SessionMachine::uninit(Uninit::Reason aReason)
12743{
12744 LogFlowThisFuncEnter();
12745 LogFlowThisFunc(("reason=%d\n", aReason));
12746
12747 /*
12748 * Strongly reference ourselves to prevent this object deletion after
12749 * mData->mSession.mMachine.setNull() below (which can release the last
12750 * reference and call the destructor). Important: this must be done before
12751 * accessing any members (and before AutoUninitSpan that does it as well).
12752 * This self reference will be released as the very last step on return.
12753 */
12754 ComObjPtr<SessionMachine> selfRef;
12755 if (aReason != Uninit::Unexpected)
12756 selfRef = this;
12757
12758 /* Enclose the state transition Ready->InUninit->NotReady */
12759 AutoUninitSpan autoUninitSpan(this);
12760 if (autoUninitSpan.uninitDone())
12761 {
12762 LogFlowThisFunc(("Already uninitialized\n"));
12763 LogFlowThisFuncLeave();
12764 return;
12765 }
12766
12767 if (autoUninitSpan.initFailed())
12768 {
12769 /* We've been called by init() because it's failed. It's not really
12770 * necessary (nor it's safe) to perform the regular uninit sequence
12771 * below, the following is enough.
12772 */
12773 LogFlowThisFunc(("Initialization failed.\n"));
12774 /* destroy the machine client token */
12775 if (mClientToken)
12776 {
12777 delete mClientToken;
12778 mClientToken = NULL;
12779 }
12780 uninitDataAndChildObjects();
12781 mData.free();
12782 unconst(mParent) = NULL;
12783 unconst(mPeer) = NULL;
12784 LogFlowThisFuncLeave();
12785 return;
12786 }
12787
12788 MachineState_T lastState;
12789 {
12790 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12791 lastState = mData->mMachineState;
12792 }
12793 NOREF(lastState);
12794
12795#ifdef VBOX_WITH_USB
12796 // release all captured USB devices, but do this before requesting the locks below
12797 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12798 {
12799 /* Console::captureUSBDevices() is called in the VM process only after
12800 * setting the machine state to Starting or Restoring.
12801 * Console::detachAllUSBDevices() will be called upon successful
12802 * termination. So, we need to release USB devices only if there was
12803 * an abnormal termination of a running VM.
12804 *
12805 * This is identical to SessionMachine::DetachAllUSBDevices except
12806 * for the aAbnormal argument. */
12807 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12808 AssertComRC(rc);
12809 NOREF(rc);
12810
12811 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12812 if (service)
12813 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12814 }
12815#endif /* VBOX_WITH_USB */
12816
12817 // we need to lock this object in uninit() because the lock is shared
12818 // with mPeer (as well as data we modify below). mParent lock is needed
12819 // by several calls to it.
12820 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12821
12822#ifdef VBOX_WITH_RESOURCE_USAGE_API
12823 /*
12824 * It is safe to call Machine::i_unregisterMetrics() here because
12825 * PerformanceCollector::samplerCallback no longer accesses guest methods
12826 * holding the lock.
12827 */
12828 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12829 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12830 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12831 if (mCollectorGuest)
12832 {
12833 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12834 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12835 mCollectorGuest = NULL;
12836 }
12837#endif
12838
12839 if (aReason == Uninit::Abnormal)
12840 {
12841 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12842
12843 /* reset the state to Aborted */
12844 if (mData->mMachineState != MachineState_Aborted)
12845 i_setMachineState(MachineState_Aborted);
12846 }
12847
12848 // any machine settings modified?
12849 if (mData->flModifications)
12850 {
12851 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12852 i_rollback(false /* aNotify */);
12853 }
12854
12855 mData->mSession.mPID = NIL_RTPROCESS;
12856
12857 if (aReason == Uninit::Unexpected)
12858 {
12859 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12860 * client watcher thread to update the set of machines that have open
12861 * sessions. */
12862 mParent->i_updateClientWatcher();
12863 }
12864
12865 /* uninitialize all remote controls */
12866 if (mData->mSession.mRemoteControls.size())
12867 {
12868 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12869 mData->mSession.mRemoteControls.size()));
12870
12871 /* Always restart a the beginning, since the iterator is invalidated
12872 * by using erase(). */
12873 for (Data::Session::RemoteControlList::iterator
12874 it = mData->mSession.mRemoteControls.begin();
12875 it != mData->mSession.mRemoteControls.end();
12876 it = mData->mSession.mRemoteControls.begin())
12877 {
12878 ComPtr<IInternalSessionControl> pControl = *it;
12879 mData->mSession.mRemoteControls.erase(it);
12880 multilock.release();
12881 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12882 HRESULT rc = pControl->Uninitialize();
12883 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12884 if (FAILED(rc))
12885 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12886 multilock.acquire();
12887 }
12888 mData->mSession.mRemoteControls.clear();
12889 }
12890
12891 /* Remove all references to the NAT network service. The service will stop
12892 * if all references (also from other VMs) are removed. */
12893 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12894 {
12895 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12896 {
12897 BOOL enabled;
12898 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12899 if ( FAILED(hrc)
12900 || !enabled)
12901 continue;
12902
12903 NetworkAttachmentType_T type;
12904 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12905 if ( SUCCEEDED(hrc)
12906 && type == NetworkAttachmentType_NATNetwork)
12907 {
12908 Bstr name;
12909 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12910 if (SUCCEEDED(hrc))
12911 {
12912 multilock.release();
12913 Utf8Str strName(name);
12914 LogRel(("VM '%s' stops using NAT network '%s'\n",
12915 mUserData->s.strName.c_str(), strName.c_str()));
12916 mParent->i_natNetworkRefDec(strName);
12917 multilock.acquire();
12918 }
12919 }
12920 }
12921 }
12922
12923 /*
12924 * An expected uninitialization can come only from #i_checkForDeath().
12925 * Otherwise it means that something's gone really wrong (for example,
12926 * the Session implementation has released the VirtualBox reference
12927 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12928 * etc). However, it's also possible, that the client releases the IPC
12929 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12930 * but the VirtualBox release event comes first to the server process.
12931 * This case is practically possible, so we should not assert on an
12932 * unexpected uninit, just log a warning.
12933 */
12934
12935 if (aReason == Uninit::Unexpected)
12936 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12937
12938 if (aReason != Uninit::Normal)
12939 {
12940 mData->mSession.mDirectControl.setNull();
12941 }
12942 else
12943 {
12944 /* this must be null here (see #OnSessionEnd()) */
12945 Assert(mData->mSession.mDirectControl.isNull());
12946 Assert(mData->mSession.mState == SessionState_Unlocking);
12947 Assert(!mData->mSession.mProgress.isNull());
12948 }
12949 if (mData->mSession.mProgress)
12950 {
12951 if (aReason == Uninit::Normal)
12952 mData->mSession.mProgress->i_notifyComplete(S_OK);
12953 else
12954 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12955 COM_IIDOF(ISession),
12956 getComponentName(),
12957 tr("The VM session was aborted"));
12958 mData->mSession.mProgress.setNull();
12959 }
12960
12961 if (mConsoleTaskData.mProgress)
12962 {
12963 Assert(aReason == Uninit::Abnormal);
12964 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12965 COM_IIDOF(ISession),
12966 getComponentName(),
12967 tr("The VM session was aborted"));
12968 mConsoleTaskData.mProgress.setNull();
12969 }
12970
12971 /* remove the association between the peer machine and this session machine */
12972 Assert( (SessionMachine*)mData->mSession.mMachine == this
12973 || aReason == Uninit::Unexpected);
12974
12975 /* reset the rest of session data */
12976 mData->mSession.mLockType = LockType_Null;
12977 mData->mSession.mMachine.setNull();
12978 mData->mSession.mState = SessionState_Unlocked;
12979 mData->mSession.mName.setNull();
12980
12981 /* destroy the machine client token before leaving the exclusive lock */
12982 if (mClientToken)
12983 {
12984 delete mClientToken;
12985 mClientToken = NULL;
12986 }
12987
12988 /* fire an event */
12989 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12990
12991 uninitDataAndChildObjects();
12992
12993 /* free the essential data structure last */
12994 mData.free();
12995
12996 /* release the exclusive lock before setting the below two to NULL */
12997 multilock.release();
12998
12999 unconst(mParent) = NULL;
13000 unconst(mPeer) = NULL;
13001
13002 AuthLibUnload(&mAuthLibCtx);
13003
13004 LogFlowThisFuncLeave();
13005}
13006
13007// util::Lockable interface
13008////////////////////////////////////////////////////////////////////////////////
13009
13010/**
13011 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13012 * with the primary Machine instance (mPeer).
13013 */
13014RWLockHandle *SessionMachine::lockHandle() const
13015{
13016 AssertReturn(mPeer != NULL, NULL);
13017 return mPeer->lockHandle();
13018}
13019
13020// IInternalMachineControl methods
13021////////////////////////////////////////////////////////////////////////////////
13022
13023/**
13024 * Passes collected guest statistics to performance collector object
13025 */
13026HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13027 ULONG aCpuKernel, ULONG aCpuIdle,
13028 ULONG aMemTotal, ULONG aMemFree,
13029 ULONG aMemBalloon, ULONG aMemShared,
13030 ULONG aMemCache, ULONG aPageTotal,
13031 ULONG aAllocVMM, ULONG aFreeVMM,
13032 ULONG aBalloonedVMM, ULONG aSharedVMM,
13033 ULONG aVmNetRx, ULONG aVmNetTx)
13034{
13035#ifdef VBOX_WITH_RESOURCE_USAGE_API
13036 if (mCollectorGuest)
13037 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13038 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13039 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13040 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13041
13042 return S_OK;
13043#else
13044 NOREF(aValidStats);
13045 NOREF(aCpuUser);
13046 NOREF(aCpuKernel);
13047 NOREF(aCpuIdle);
13048 NOREF(aMemTotal);
13049 NOREF(aMemFree);
13050 NOREF(aMemBalloon);
13051 NOREF(aMemShared);
13052 NOREF(aMemCache);
13053 NOREF(aPageTotal);
13054 NOREF(aAllocVMM);
13055 NOREF(aFreeVMM);
13056 NOREF(aBalloonedVMM);
13057 NOREF(aSharedVMM);
13058 NOREF(aVmNetRx);
13059 NOREF(aVmNetTx);
13060 return E_NOTIMPL;
13061#endif
13062}
13063
13064////////////////////////////////////////////////////////////////////////////////
13065//
13066// SessionMachine task records
13067//
13068////////////////////////////////////////////////////////////////////////////////
13069
13070/**
13071 * Task record for saving the machine state.
13072 */
13073class SessionMachine::SaveStateTask
13074 : public Machine::Task
13075{
13076public:
13077 SaveStateTask(SessionMachine *m,
13078 Progress *p,
13079 const Utf8Str &t,
13080 Reason_T enmReason,
13081 const Utf8Str &strStateFilePath)
13082 : Task(m, p, t),
13083 m_enmReason(enmReason),
13084 m_strStateFilePath(strStateFilePath)
13085 {}
13086
13087private:
13088 void handler()
13089 {
13090 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13091 }
13092
13093 Reason_T m_enmReason;
13094 Utf8Str m_strStateFilePath;
13095
13096 friend class SessionMachine;
13097};
13098
13099/**
13100 * Task thread implementation for SessionMachine::SaveState(), called from
13101 * SessionMachine::taskHandler().
13102 *
13103 * @note Locks this object for writing.
13104 *
13105 * @param task
13106 * @return
13107 */
13108void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13109{
13110 LogFlowThisFuncEnter();
13111
13112 AutoCaller autoCaller(this);
13113 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13114 if (FAILED(autoCaller.rc()))
13115 {
13116 /* we might have been uninitialized because the session was accidentally
13117 * closed by the client, so don't assert */
13118 HRESULT rc = setError(E_FAIL,
13119 tr("The session has been accidentally closed"));
13120 task.m_pProgress->i_notifyComplete(rc);
13121 LogFlowThisFuncLeave();
13122 return;
13123 }
13124
13125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13126
13127 HRESULT rc = S_OK;
13128
13129 try
13130 {
13131 ComPtr<IInternalSessionControl> directControl;
13132 if (mData->mSession.mLockType == LockType_VM)
13133 directControl = mData->mSession.mDirectControl;
13134 if (directControl.isNull())
13135 throw setError(VBOX_E_INVALID_VM_STATE,
13136 tr("Trying to save state without a running VM"));
13137 alock.release();
13138 BOOL fSuspendedBySave;
13139 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13140 Assert(!fSuspendedBySave);
13141 alock.acquire();
13142
13143 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13144 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13145 throw E_FAIL);
13146
13147 if (SUCCEEDED(rc))
13148 {
13149 mSSData->strStateFilePath = task.m_strStateFilePath;
13150
13151 /* save all VM settings */
13152 rc = i_saveSettings(NULL);
13153 // no need to check whether VirtualBox.xml needs saving also since
13154 // we can't have a name change pending at this point
13155 }
13156 else
13157 {
13158 // On failure, set the state to the state we had at the beginning.
13159 i_setMachineState(task.m_machineStateBackup);
13160 i_updateMachineStateOnClient();
13161
13162 // Delete the saved state file (might have been already created).
13163 // No need to check whether this is shared with a snapshot here
13164 // because we certainly created a fresh saved state file here.
13165 RTFileDelete(task.m_strStateFilePath.c_str());
13166 }
13167 }
13168 catch (HRESULT aRC) { rc = aRC; }
13169
13170 task.m_pProgress->i_notifyComplete(rc);
13171
13172 LogFlowThisFuncLeave();
13173}
13174
13175/**
13176 * @note Locks this object for writing.
13177 */
13178HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13179{
13180 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13181}
13182
13183HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13184{
13185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13186
13187 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13188 if (FAILED(rc)) return rc;
13189
13190 if ( mData->mMachineState != MachineState_Running
13191 && mData->mMachineState != MachineState_Paused
13192 )
13193 return setError(VBOX_E_INVALID_VM_STATE,
13194 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13195 Global::stringifyMachineState(mData->mMachineState));
13196
13197 ComObjPtr<Progress> pProgress;
13198 pProgress.createObject();
13199 rc = pProgress->init(i_getVirtualBox(),
13200 static_cast<IMachine *>(this) /* aInitiator */,
13201 tr("Saving the execution state of the virtual machine"),
13202 FALSE /* aCancelable */);
13203 if (FAILED(rc))
13204 return rc;
13205
13206 Utf8Str strStateFilePath;
13207 i_composeSavedStateFilename(strStateFilePath);
13208
13209 /* create and start the task on a separate thread (note that it will not
13210 * start working until we release alock) */
13211 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13212 rc = pTask->createThread();
13213 if (FAILED(rc))
13214 return rc;
13215
13216 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13217 i_setMachineState(MachineState_Saving);
13218 i_updateMachineStateOnClient();
13219
13220 pProgress.queryInterfaceTo(aProgress.asOutParam());
13221
13222 return S_OK;
13223}
13224
13225/**
13226 * @note Locks this object for writing.
13227 */
13228HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13229{
13230 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13231
13232 HRESULT rc = i_checkStateDependency(MutableStateDep);
13233 if (FAILED(rc)) return rc;
13234
13235 if ( mData->mMachineState != MachineState_PoweredOff
13236 && mData->mMachineState != MachineState_Teleported
13237 && mData->mMachineState != MachineState_Aborted
13238 )
13239 return setError(VBOX_E_INVALID_VM_STATE,
13240 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13241 Global::stringifyMachineState(mData->mMachineState));
13242
13243 com::Utf8Str stateFilePathFull;
13244 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13245 if (RT_FAILURE(vrc))
13246 return setError(VBOX_E_FILE_ERROR,
13247 tr("Invalid saved state file path '%s' (%Rrc)"),
13248 aSavedStateFile.c_str(),
13249 vrc);
13250
13251 mSSData->strStateFilePath = stateFilePathFull;
13252
13253 /* The below i_setMachineState() will detect the state transition and will
13254 * update the settings file */
13255
13256 return i_setMachineState(MachineState_Saved);
13257}
13258
13259/**
13260 * @note Locks this object for writing.
13261 */
13262HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13263{
13264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13265
13266 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13267 if (FAILED(rc)) return rc;
13268
13269 if (mData->mMachineState != MachineState_Saved)
13270 return setError(VBOX_E_INVALID_VM_STATE,
13271 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13272 Global::stringifyMachineState(mData->mMachineState));
13273
13274 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13275
13276 /*
13277 * Saved -> PoweredOff transition will be detected in the SessionMachine
13278 * and properly handled.
13279 */
13280 rc = i_setMachineState(MachineState_PoweredOff);
13281 return rc;
13282}
13283
13284
13285/**
13286 * @note Locks the same as #i_setMachineState() does.
13287 */
13288HRESULT SessionMachine::updateState(MachineState_T aState)
13289{
13290 return i_setMachineState(aState);
13291}
13292
13293/**
13294 * @note Locks this object for writing.
13295 */
13296HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13297{
13298 IProgress *pProgress(aProgress);
13299
13300 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13301
13302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13303
13304 if (mData->mSession.mState != SessionState_Locked)
13305 return VBOX_E_INVALID_OBJECT_STATE;
13306
13307 if (!mData->mSession.mProgress.isNull())
13308 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13309
13310 /* If we didn't reference the NAT network service yet, add a reference to
13311 * force a start */
13312 if (miNATNetworksStarted < 1)
13313 {
13314 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13315 {
13316 BOOL enabled;
13317 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13318 if ( FAILED(hrc)
13319 || !enabled)
13320 continue;
13321
13322 NetworkAttachmentType_T type;
13323 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13324 if ( SUCCEEDED(hrc)
13325 && type == NetworkAttachmentType_NATNetwork)
13326 {
13327 Bstr name;
13328 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13329 if (SUCCEEDED(hrc))
13330 {
13331 Utf8Str strName(name);
13332 LogRel(("VM '%s' starts using NAT network '%s'\n",
13333 mUserData->s.strName.c_str(), strName.c_str()));
13334 mPeer->lockHandle()->unlockWrite();
13335 mParent->i_natNetworkRefInc(strName);
13336#ifdef RT_LOCK_STRICT
13337 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13338#else
13339 mPeer->lockHandle()->lockWrite();
13340#endif
13341 }
13342 }
13343 }
13344 miNATNetworksStarted++;
13345 }
13346
13347 LogFlowThisFunc(("returns S_OK.\n"));
13348 return S_OK;
13349}
13350
13351/**
13352 * @note Locks this object for writing.
13353 */
13354HRESULT SessionMachine::endPowerUp(LONG aResult)
13355{
13356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13357
13358 if (mData->mSession.mState != SessionState_Locked)
13359 return VBOX_E_INVALID_OBJECT_STATE;
13360
13361 /* Finalize the LaunchVMProcess progress object. */
13362 if (mData->mSession.mProgress)
13363 {
13364 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13365 mData->mSession.mProgress.setNull();
13366 }
13367
13368 if (SUCCEEDED((HRESULT)aResult))
13369 {
13370#ifdef VBOX_WITH_RESOURCE_USAGE_API
13371 /* The VM has been powered up successfully, so it makes sense
13372 * now to offer the performance metrics for a running machine
13373 * object. Doing it earlier wouldn't be safe. */
13374 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13375 mData->mSession.mPID);
13376#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13377 }
13378
13379 return S_OK;
13380}
13381
13382/**
13383 * @note Locks this object for writing.
13384 */
13385HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13386{
13387 LogFlowThisFuncEnter();
13388
13389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13390
13391 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13392 E_FAIL);
13393
13394 /* create a progress object to track operation completion */
13395 ComObjPtr<Progress> pProgress;
13396 pProgress.createObject();
13397 pProgress->init(i_getVirtualBox(),
13398 static_cast<IMachine *>(this) /* aInitiator */,
13399 tr("Stopping the virtual machine"),
13400 FALSE /* aCancelable */);
13401
13402 /* fill in the console task data */
13403 mConsoleTaskData.mLastState = mData->mMachineState;
13404 mConsoleTaskData.mProgress = pProgress;
13405
13406 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13407 i_setMachineState(MachineState_Stopping);
13408
13409 pProgress.queryInterfaceTo(aProgress.asOutParam());
13410
13411 return S_OK;
13412}
13413
13414/**
13415 * @note Locks this object for writing.
13416 */
13417HRESULT SessionMachine::endPoweringDown(LONG aResult,
13418 const com::Utf8Str &aErrMsg)
13419{
13420 LogFlowThisFuncEnter();
13421
13422 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13423
13424 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13425 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13426 && mConsoleTaskData.mLastState != MachineState_Null,
13427 E_FAIL);
13428
13429 /*
13430 * On failure, set the state to the state we had when BeginPoweringDown()
13431 * was called (this is expected by Console::PowerDown() and the associated
13432 * task). On success the VM process already changed the state to
13433 * MachineState_PoweredOff, so no need to do anything.
13434 */
13435 if (FAILED(aResult))
13436 i_setMachineState(mConsoleTaskData.mLastState);
13437
13438 /* notify the progress object about operation completion */
13439 Assert(mConsoleTaskData.mProgress);
13440 if (SUCCEEDED(aResult))
13441 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13442 else
13443 {
13444 if (aErrMsg.length())
13445 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13446 COM_IIDOF(ISession),
13447 getComponentName(),
13448 aErrMsg.c_str());
13449 else
13450 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13451 }
13452
13453 /* clear out the temporary saved state data */
13454 mConsoleTaskData.mLastState = MachineState_Null;
13455 mConsoleTaskData.mProgress.setNull();
13456
13457 LogFlowThisFuncLeave();
13458 return S_OK;
13459}
13460
13461
13462/**
13463 * Goes through the USB filters of the given machine to see if the given
13464 * device matches any filter or not.
13465 *
13466 * @note Locks the same as USBController::hasMatchingFilter() does.
13467 */
13468HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13469 BOOL *aMatched,
13470 ULONG *aMaskedInterfaces)
13471{
13472 LogFlowThisFunc(("\n"));
13473
13474#ifdef VBOX_WITH_USB
13475 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13476#else
13477 NOREF(aDevice);
13478 NOREF(aMaskedInterfaces);
13479 *aMatched = FALSE;
13480#endif
13481
13482 return S_OK;
13483}
13484
13485/**
13486 * @note Locks the same as Host::captureUSBDevice() does.
13487 */
13488HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13489{
13490 LogFlowThisFunc(("\n"));
13491
13492#ifdef VBOX_WITH_USB
13493 /* if captureDeviceForVM() fails, it must have set extended error info */
13494 clearError();
13495 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13496 if (FAILED(rc)) return rc;
13497
13498 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13499 AssertReturn(service, E_FAIL);
13500 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13501#else
13502 NOREF(aId);
13503 return E_NOTIMPL;
13504#endif
13505}
13506
13507/**
13508 * @note Locks the same as Host::detachUSBDevice() does.
13509 */
13510HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13511 BOOL aDone)
13512{
13513 LogFlowThisFunc(("\n"));
13514
13515#ifdef VBOX_WITH_USB
13516 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13517 AssertReturn(service, E_FAIL);
13518 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13519#else
13520 NOREF(aId);
13521 NOREF(aDone);
13522 return E_NOTIMPL;
13523#endif
13524}
13525
13526/**
13527 * Inserts all machine filters to the USB proxy service and then calls
13528 * Host::autoCaptureUSBDevices().
13529 *
13530 * Called by Console from the VM process upon VM startup.
13531 *
13532 * @note Locks what called methods lock.
13533 */
13534HRESULT SessionMachine::autoCaptureUSBDevices()
13535{
13536 LogFlowThisFunc(("\n"));
13537
13538#ifdef VBOX_WITH_USB
13539 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13540 AssertComRC(rc);
13541 NOREF(rc);
13542
13543 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13544 AssertReturn(service, E_FAIL);
13545 return service->autoCaptureDevicesForVM(this);
13546#else
13547 return S_OK;
13548#endif
13549}
13550
13551/**
13552 * Removes all machine filters from the USB proxy service and then calls
13553 * Host::detachAllUSBDevices().
13554 *
13555 * Called by Console from the VM process upon normal VM termination or by
13556 * SessionMachine::uninit() upon abnormal VM termination (from under the
13557 * Machine/SessionMachine lock).
13558 *
13559 * @note Locks what called methods lock.
13560 */
13561HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13562{
13563 LogFlowThisFunc(("\n"));
13564
13565#ifdef VBOX_WITH_USB
13566 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13567 AssertComRC(rc);
13568 NOREF(rc);
13569
13570 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13571 AssertReturn(service, E_FAIL);
13572 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13573#else
13574 NOREF(aDone);
13575 return S_OK;
13576#endif
13577}
13578
13579/**
13580 * @note Locks this object for writing.
13581 */
13582HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13583 ComPtr<IProgress> &aProgress)
13584{
13585 LogFlowThisFuncEnter();
13586
13587 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13588 /*
13589 * We don't assert below because it might happen that a non-direct session
13590 * informs us it is closed right after we've been uninitialized -- it's ok.
13591 */
13592
13593 /* get IInternalSessionControl interface */
13594 ComPtr<IInternalSessionControl> control(aSession);
13595
13596 ComAssertRet(!control.isNull(), E_INVALIDARG);
13597
13598 /* Creating a Progress object requires the VirtualBox lock, and
13599 * thus locking it here is required by the lock order rules. */
13600 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13601
13602 if (control == mData->mSession.mDirectControl)
13603 {
13604 /* The direct session is being normally closed by the client process
13605 * ----------------------------------------------------------------- */
13606
13607 /* go to the closing state (essential for all open*Session() calls and
13608 * for #i_checkForDeath()) */
13609 Assert(mData->mSession.mState == SessionState_Locked);
13610 mData->mSession.mState = SessionState_Unlocking;
13611
13612 /* set direct control to NULL to release the remote instance */
13613 mData->mSession.mDirectControl.setNull();
13614 LogFlowThisFunc(("Direct control is set to NULL\n"));
13615
13616 if (mData->mSession.mProgress)
13617 {
13618 /* finalize the progress, someone might wait if a frontend
13619 * closes the session before powering on the VM. */
13620 mData->mSession.mProgress->notifyComplete(E_FAIL,
13621 COM_IIDOF(ISession),
13622 getComponentName(),
13623 tr("The VM session was closed before any attempt to power it on"));
13624 mData->mSession.mProgress.setNull();
13625 }
13626
13627 /* Create the progress object the client will use to wait until
13628 * #i_checkForDeath() is called to uninitialize this session object after
13629 * it releases the IPC semaphore.
13630 * Note! Because we're "reusing" mProgress here, this must be a proxy
13631 * object just like for LaunchVMProcess. */
13632 Assert(mData->mSession.mProgress.isNull());
13633 ComObjPtr<ProgressProxy> progress;
13634 progress.createObject();
13635 ComPtr<IUnknown> pPeer(mPeer);
13636 progress->init(mParent, pPeer,
13637 Bstr(tr("Closing session")).raw(),
13638 FALSE /* aCancelable */);
13639 progress.queryInterfaceTo(aProgress.asOutParam());
13640 mData->mSession.mProgress = progress;
13641 }
13642 else
13643 {
13644 /* the remote session is being normally closed */
13645 bool found = false;
13646 for (Data::Session::RemoteControlList::iterator
13647 it = mData->mSession.mRemoteControls.begin();
13648 it != mData->mSession.mRemoteControls.end();
13649 ++it)
13650 {
13651 if (control == *it)
13652 {
13653 found = true;
13654 // This MUST be erase(it), not remove(*it) as the latter
13655 // triggers a very nasty use after free due to the place where
13656 // the value "lives".
13657 mData->mSession.mRemoteControls.erase(it);
13658 break;
13659 }
13660 }
13661 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13662 E_INVALIDARG);
13663 }
13664
13665 /* signal the client watcher thread, because the client is going away */
13666 mParent->i_updateClientWatcher();
13667
13668 LogFlowThisFuncLeave();
13669 return S_OK;
13670}
13671
13672HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13673 std::vector<com::Utf8Str> &aValues,
13674 std::vector<LONG64> &aTimestamps,
13675 std::vector<com::Utf8Str> &aFlags)
13676{
13677 LogFlowThisFunc(("\n"));
13678
13679#ifdef VBOX_WITH_GUEST_PROPS
13680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13681
13682 size_t cEntries = mHWData->mGuestProperties.size();
13683 aNames.resize(cEntries);
13684 aValues.resize(cEntries);
13685 aTimestamps.resize(cEntries);
13686 aFlags.resize(cEntries);
13687
13688 size_t i = 0;
13689 for (HWData::GuestPropertyMap::const_iterator
13690 it = mHWData->mGuestProperties.begin();
13691 it != mHWData->mGuestProperties.end();
13692 ++it, ++i)
13693 {
13694 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13695 aNames[i] = it->first;
13696 aValues[i] = it->second.strValue;
13697 aTimestamps[i] = it->second.mTimestamp;
13698
13699 /* If it is NULL, keep it NULL. */
13700 if (it->second.mFlags)
13701 {
13702 GuestPropWriteFlags(it->second.mFlags, szFlags);
13703 aFlags[i] = szFlags;
13704 }
13705 else
13706 aFlags[i] = "";
13707 }
13708 return S_OK;
13709#else
13710 ReturnComNotImplemented();
13711#endif
13712}
13713
13714HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13715 const com::Utf8Str &aValue,
13716 LONG64 aTimestamp,
13717 const com::Utf8Str &aFlags)
13718{
13719 LogFlowThisFunc(("\n"));
13720
13721#ifdef VBOX_WITH_GUEST_PROPS
13722 try
13723 {
13724 /*
13725 * Convert input up front.
13726 */
13727 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13728 if (aFlags.length())
13729 {
13730 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13731 AssertRCReturn(vrc, E_INVALIDARG);
13732 }
13733
13734 /*
13735 * Now grab the object lock, validate the state and do the update.
13736 */
13737
13738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13739
13740 if (!Global::IsOnline(mData->mMachineState))
13741 {
13742 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13743 VBOX_E_INVALID_VM_STATE);
13744 }
13745
13746 i_setModified(IsModified_MachineData);
13747 mHWData.backup();
13748
13749 bool fDelete = !aValue.length();
13750 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13751 if (it != mHWData->mGuestProperties.end())
13752 {
13753 if (!fDelete)
13754 {
13755 it->second.strValue = aValue;
13756 it->second.mTimestamp = aTimestamp;
13757 it->second.mFlags = fFlags;
13758 }
13759 else
13760 mHWData->mGuestProperties.erase(it);
13761
13762 mData->mGuestPropertiesModified = TRUE;
13763 }
13764 else if (!fDelete)
13765 {
13766 HWData::GuestProperty prop;
13767 prop.strValue = aValue;
13768 prop.mTimestamp = aTimestamp;
13769 prop.mFlags = fFlags;
13770
13771 mHWData->mGuestProperties[aName] = prop;
13772 mData->mGuestPropertiesModified = TRUE;
13773 }
13774
13775 alock.release();
13776
13777 mParent->i_onGuestPropertyChange(mData->mUuid,
13778 Bstr(aName).raw(),
13779 Bstr(aValue).raw(),
13780 Bstr(aFlags).raw());
13781 }
13782 catch (...)
13783 {
13784 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13785 }
13786 return S_OK;
13787#else
13788 ReturnComNotImplemented();
13789#endif
13790}
13791
13792
13793HRESULT SessionMachine::lockMedia()
13794{
13795 AutoMultiWriteLock2 alock(this->lockHandle(),
13796 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13797
13798 AssertReturn( mData->mMachineState == MachineState_Starting
13799 || mData->mMachineState == MachineState_Restoring
13800 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13801
13802 clearError();
13803 alock.release();
13804 return i_lockMedia();
13805}
13806
13807HRESULT SessionMachine::unlockMedia()
13808{
13809 HRESULT hrc = i_unlockMedia();
13810 return hrc;
13811}
13812
13813HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13814 ComPtr<IMediumAttachment> &aNewAttachment)
13815{
13816 // request the host lock first, since might be calling Host methods for getting host drives;
13817 // next, protect the media tree all the while we're in here, as well as our member variables
13818 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13819 this->lockHandle(),
13820 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13821
13822 IMediumAttachment *iAttach = aAttachment;
13823 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13824
13825 Utf8Str ctrlName;
13826 LONG lPort;
13827 LONG lDevice;
13828 bool fTempEject;
13829 {
13830 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13831
13832 /* Need to query the details first, as the IMediumAttachment reference
13833 * might be to the original settings, which we are going to change. */
13834 ctrlName = pAttach->i_getControllerName();
13835 lPort = pAttach->i_getPort();
13836 lDevice = pAttach->i_getDevice();
13837 fTempEject = pAttach->i_getTempEject();
13838 }
13839
13840 if (!fTempEject)
13841 {
13842 /* Remember previously mounted medium. The medium before taking the
13843 * backup is not necessarily the same thing. */
13844 ComObjPtr<Medium> oldmedium;
13845 oldmedium = pAttach->i_getMedium();
13846
13847 i_setModified(IsModified_Storage);
13848 mMediumAttachments.backup();
13849
13850 // The backup operation makes the pAttach reference point to the
13851 // old settings. Re-get the correct reference.
13852 pAttach = i_findAttachment(*mMediumAttachments.data(),
13853 ctrlName,
13854 lPort,
13855 lDevice);
13856
13857 {
13858 AutoCaller autoAttachCaller(this);
13859 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13860
13861 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13862 if (!oldmedium.isNull())
13863 oldmedium->i_removeBackReference(mData->mUuid);
13864
13865 pAttach->i_updateMedium(NULL);
13866 pAttach->i_updateEjected();
13867 }
13868
13869 i_setModified(IsModified_Storage);
13870 }
13871 else
13872 {
13873 {
13874 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13875 pAttach->i_updateEjected();
13876 }
13877 }
13878
13879 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13880
13881 return S_OK;
13882}
13883
13884HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13885 com::Utf8Str &aResult)
13886{
13887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13888
13889 HRESULT hr = S_OK;
13890
13891 if (!mAuthLibCtx.hAuthLibrary)
13892 {
13893 /* Load the external authentication library. */
13894 Bstr authLibrary;
13895 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13896
13897 Utf8Str filename = authLibrary;
13898
13899 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13900 if (RT_FAILURE(rc))
13901 {
13902 hr = setError(E_FAIL,
13903 tr("Could not load the external authentication library '%s' (%Rrc)"),
13904 filename.c_str(), rc);
13905 }
13906 }
13907
13908 /* The auth library might need the machine lock. */
13909 alock.release();
13910
13911 if (FAILED(hr))
13912 return hr;
13913
13914 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13915 {
13916 enum VRDEAuthParams
13917 {
13918 parmUuid = 1,
13919 parmGuestJudgement,
13920 parmUser,
13921 parmPassword,
13922 parmDomain,
13923 parmClientId
13924 };
13925
13926 AuthResult result = AuthResultAccessDenied;
13927
13928 Guid uuid(aAuthParams[parmUuid]);
13929 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13930 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13931
13932 result = AuthLibAuthenticate(&mAuthLibCtx,
13933 uuid.raw(), guestJudgement,
13934 aAuthParams[parmUser].c_str(),
13935 aAuthParams[parmPassword].c_str(),
13936 aAuthParams[parmDomain].c_str(),
13937 u32ClientId);
13938
13939 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13940 size_t cbPassword = aAuthParams[parmPassword].length();
13941 if (cbPassword)
13942 {
13943 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13944 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13945 }
13946
13947 if (result == AuthResultAccessGranted)
13948 aResult = "granted";
13949 else
13950 aResult = "denied";
13951
13952 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13953 aAuthParams[parmUser].c_str(), aResult.c_str()));
13954 }
13955 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13956 {
13957 enum VRDEAuthDisconnectParams
13958 {
13959 parmUuid = 1,
13960 parmClientId
13961 };
13962
13963 Guid uuid(aAuthParams[parmUuid]);
13964 uint32_t u32ClientId = 0;
13965 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13966 }
13967 else
13968 {
13969 hr = E_INVALIDARG;
13970 }
13971
13972 return hr;
13973}
13974
13975// public methods only for internal purposes
13976/////////////////////////////////////////////////////////////////////////////
13977
13978#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13979/**
13980 * Called from the client watcher thread to check for expected or unexpected
13981 * death of the client process that has a direct session to this machine.
13982 *
13983 * On Win32 and on OS/2, this method is called only when we've got the
13984 * mutex (i.e. the client has either died or terminated normally) so it always
13985 * returns @c true (the client is terminated, the session machine is
13986 * uninitialized).
13987 *
13988 * On other platforms, the method returns @c true if the client process has
13989 * terminated normally or abnormally and the session machine was uninitialized,
13990 * and @c false if the client process is still alive.
13991 *
13992 * @note Locks this object for writing.
13993 */
13994bool SessionMachine::i_checkForDeath()
13995{
13996 Uninit::Reason reason;
13997 bool terminated = false;
13998
13999 /* Enclose autoCaller with a block because calling uninit() from under it
14000 * will deadlock. */
14001 {
14002 AutoCaller autoCaller(this);
14003 if (!autoCaller.isOk())
14004 {
14005 /* return true if not ready, to cause the client watcher to exclude
14006 * the corresponding session from watching */
14007 LogFlowThisFunc(("Already uninitialized!\n"));
14008 return true;
14009 }
14010
14011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14012
14013 /* Determine the reason of death: if the session state is Closing here,
14014 * everything is fine. Otherwise it means that the client did not call
14015 * OnSessionEnd() before it released the IPC semaphore. This may happen
14016 * either because the client process has abnormally terminated, or
14017 * because it simply forgot to call ISession::Close() before exiting. We
14018 * threat the latter also as an abnormal termination (see
14019 * Session::uninit() for details). */
14020 reason = mData->mSession.mState == SessionState_Unlocking ?
14021 Uninit::Normal :
14022 Uninit::Abnormal;
14023
14024 if (mClientToken)
14025 terminated = mClientToken->release();
14026 } /* AutoCaller block */
14027
14028 if (terminated)
14029 uninit(reason);
14030
14031 return terminated;
14032}
14033
14034void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14035{
14036 LogFlowThisFunc(("\n"));
14037
14038 strTokenId.setNull();
14039
14040 AutoCaller autoCaller(this);
14041 AssertComRCReturnVoid(autoCaller.rc());
14042
14043 Assert(mClientToken);
14044 if (mClientToken)
14045 mClientToken->getId(strTokenId);
14046}
14047#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14048IToken *SessionMachine::i_getToken()
14049{
14050 LogFlowThisFunc(("\n"));
14051
14052 AutoCaller autoCaller(this);
14053 AssertComRCReturn(autoCaller.rc(), NULL);
14054
14055 Assert(mClientToken);
14056 if (mClientToken)
14057 return mClientToken->getToken();
14058 else
14059 return NULL;
14060}
14061#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14062
14063Machine::ClientToken *SessionMachine::i_getClientToken()
14064{
14065 LogFlowThisFunc(("\n"));
14066
14067 AutoCaller autoCaller(this);
14068 AssertComRCReturn(autoCaller.rc(), NULL);
14069
14070 return mClientToken;
14071}
14072
14073
14074/**
14075 * @note Locks this object for reading.
14076 */
14077HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14078{
14079 LogFlowThisFunc(("\n"));
14080
14081 AutoCaller autoCaller(this);
14082 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14083
14084 ComPtr<IInternalSessionControl> directControl;
14085 {
14086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14087 if (mData->mSession.mLockType == LockType_VM)
14088 directControl = mData->mSession.mDirectControl;
14089 }
14090
14091 /* ignore notifications sent after #OnSessionEnd() is called */
14092 if (!directControl)
14093 return S_OK;
14094
14095 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14096}
14097
14098/**
14099 * @note Locks this object for reading.
14100 */
14101HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14102 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14103 IN_BSTR aGuestIp, LONG aGuestPort)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120 /*
14121 * instead acting like callback we ask IVirtualBox deliver corresponding event
14122 */
14123
14124 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14125 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14126 return S_OK;
14127}
14128
14129/**
14130 * @note Locks this object for reading.
14131 */
14132HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14133{
14134 LogFlowThisFunc(("\n"));
14135
14136 AutoCaller autoCaller(this);
14137 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14138
14139 ComPtr<IInternalSessionControl> directControl;
14140 {
14141 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14142 if (mData->mSession.mLockType == LockType_VM)
14143 directControl = mData->mSession.mDirectControl;
14144 }
14145
14146 /* ignore notifications sent after #OnSessionEnd() is called */
14147 if (!directControl)
14148 return S_OK;
14149
14150 return directControl->OnAudioAdapterChange(audioAdapter);
14151}
14152
14153/**
14154 * @note Locks this object for reading.
14155 */
14156HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14157{
14158 LogFlowThisFunc(("\n"));
14159
14160 AutoCaller autoCaller(this);
14161 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14162
14163 ComPtr<IInternalSessionControl> directControl;
14164 {
14165 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14166 if (mData->mSession.mLockType == LockType_VM)
14167 directControl = mData->mSession.mDirectControl;
14168 }
14169
14170 /* ignore notifications sent after #OnSessionEnd() is called */
14171 if (!directControl)
14172 return S_OK;
14173
14174 return directControl->OnSerialPortChange(serialPort);
14175}
14176
14177/**
14178 * @note Locks this object for reading.
14179 */
14180HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14181{
14182 LogFlowThisFunc(("\n"));
14183
14184 AutoCaller autoCaller(this);
14185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14186
14187 ComPtr<IInternalSessionControl> directControl;
14188 {
14189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14190 if (mData->mSession.mLockType == LockType_VM)
14191 directControl = mData->mSession.mDirectControl;
14192 }
14193
14194 /* ignore notifications sent after #OnSessionEnd() is called */
14195 if (!directControl)
14196 return S_OK;
14197
14198 return directControl->OnParallelPortChange(parallelPort);
14199}
14200
14201/**
14202 * @note Locks this object for reading.
14203 */
14204HRESULT SessionMachine::i_onStorageControllerChange()
14205{
14206 LogFlowThisFunc(("\n"));
14207
14208 AutoCaller autoCaller(this);
14209 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14210
14211 ComPtr<IInternalSessionControl> directControl;
14212 {
14213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14214 if (mData->mSession.mLockType == LockType_VM)
14215 directControl = mData->mSession.mDirectControl;
14216 }
14217
14218 /* ignore notifications sent after #OnSessionEnd() is called */
14219 if (!directControl)
14220 return S_OK;
14221
14222 return directControl->OnStorageControllerChange();
14223}
14224
14225/**
14226 * @note Locks this object for reading.
14227 */
14228HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14229{
14230 LogFlowThisFunc(("\n"));
14231
14232 AutoCaller autoCaller(this);
14233 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14234
14235 ComPtr<IInternalSessionControl> directControl;
14236 {
14237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14238 if (mData->mSession.mLockType == LockType_VM)
14239 directControl = mData->mSession.mDirectControl;
14240 }
14241
14242 /* ignore notifications sent after #OnSessionEnd() is called */
14243 if (!directControl)
14244 return S_OK;
14245
14246 return directControl->OnMediumChange(aAttachment, aForce);
14247}
14248
14249/**
14250 * @note Locks this object for reading.
14251 */
14252HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14253{
14254 LogFlowThisFunc(("\n"));
14255
14256 AutoCaller autoCaller(this);
14257 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14258
14259 ComPtr<IInternalSessionControl> directControl;
14260 {
14261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14262 if (mData->mSession.mLockType == LockType_VM)
14263 directControl = mData->mSession.mDirectControl;
14264 }
14265
14266 /* ignore notifications sent after #OnSessionEnd() is called */
14267 if (!directControl)
14268 return S_OK;
14269
14270 return directControl->OnCPUChange(aCPU, aRemove);
14271}
14272
14273HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14274{
14275 LogFlowThisFunc(("\n"));
14276
14277 AutoCaller autoCaller(this);
14278 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14279
14280 ComPtr<IInternalSessionControl> directControl;
14281 {
14282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14283 if (mData->mSession.mLockType == LockType_VM)
14284 directControl = mData->mSession.mDirectControl;
14285 }
14286
14287 /* ignore notifications sent after #OnSessionEnd() is called */
14288 if (!directControl)
14289 return S_OK;
14290
14291 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14292}
14293
14294/**
14295 * @note Locks this object for reading.
14296 */
14297HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14298{
14299 LogFlowThisFunc(("\n"));
14300
14301 AutoCaller autoCaller(this);
14302 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14303
14304 ComPtr<IInternalSessionControl> directControl;
14305 {
14306 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14307 if (mData->mSession.mLockType == LockType_VM)
14308 directControl = mData->mSession.mDirectControl;
14309 }
14310
14311 /* ignore notifications sent after #OnSessionEnd() is called */
14312 if (!directControl)
14313 return S_OK;
14314
14315 return directControl->OnVRDEServerChange(aRestart);
14316}
14317
14318/**
14319 * @note Locks this object for reading.
14320 */
14321HRESULT SessionMachine::i_onVideoCaptureChange()
14322{
14323 LogFlowThisFunc(("\n"));
14324
14325 AutoCaller autoCaller(this);
14326 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14327
14328 ComPtr<IInternalSessionControl> directControl;
14329 {
14330 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14331 if (mData->mSession.mLockType == LockType_VM)
14332 directControl = mData->mSession.mDirectControl;
14333 }
14334
14335 /* ignore notifications sent after #OnSessionEnd() is called */
14336 if (!directControl)
14337 return S_OK;
14338
14339 return directControl->OnVideoCaptureChange();
14340}
14341
14342/**
14343 * @note Locks this object for reading.
14344 */
14345HRESULT SessionMachine::i_onUSBControllerChange()
14346{
14347 LogFlowThisFunc(("\n"));
14348
14349 AutoCaller autoCaller(this);
14350 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14351
14352 ComPtr<IInternalSessionControl> directControl;
14353 {
14354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14355 if (mData->mSession.mLockType == LockType_VM)
14356 directControl = mData->mSession.mDirectControl;
14357 }
14358
14359 /* ignore notifications sent after #OnSessionEnd() is called */
14360 if (!directControl)
14361 return S_OK;
14362
14363 return directControl->OnUSBControllerChange();
14364}
14365
14366/**
14367 * @note Locks this object for reading.
14368 */
14369HRESULT SessionMachine::i_onSharedFolderChange()
14370{
14371 LogFlowThisFunc(("\n"));
14372
14373 AutoCaller autoCaller(this);
14374 AssertComRCReturnRC(autoCaller.rc());
14375
14376 ComPtr<IInternalSessionControl> directControl;
14377 {
14378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14379 if (mData->mSession.mLockType == LockType_VM)
14380 directControl = mData->mSession.mDirectControl;
14381 }
14382
14383 /* ignore notifications sent after #OnSessionEnd() is called */
14384 if (!directControl)
14385 return S_OK;
14386
14387 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14388}
14389
14390/**
14391 * @note Locks this object for reading.
14392 */
14393HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14394{
14395 LogFlowThisFunc(("\n"));
14396
14397 AutoCaller autoCaller(this);
14398 AssertComRCReturnRC(autoCaller.rc());
14399
14400 ComPtr<IInternalSessionControl> directControl;
14401 {
14402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14403 if (mData->mSession.mLockType == LockType_VM)
14404 directControl = mData->mSession.mDirectControl;
14405 }
14406
14407 /* ignore notifications sent after #OnSessionEnd() is called */
14408 if (!directControl)
14409 return S_OK;
14410
14411 return directControl->OnClipboardModeChange(aClipboardMode);
14412}
14413
14414/**
14415 * @note Locks this object for reading.
14416 */
14417HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14418{
14419 LogFlowThisFunc(("\n"));
14420
14421 AutoCaller autoCaller(this);
14422 AssertComRCReturnRC(autoCaller.rc());
14423
14424 ComPtr<IInternalSessionControl> directControl;
14425 {
14426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14427 if (mData->mSession.mLockType == LockType_VM)
14428 directControl = mData->mSession.mDirectControl;
14429 }
14430
14431 /* ignore notifications sent after #OnSessionEnd() is called */
14432 if (!directControl)
14433 return S_OK;
14434
14435 return directControl->OnDnDModeChange(aDnDMode);
14436}
14437
14438/**
14439 * @note Locks this object for reading.
14440 */
14441HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14442{
14443 LogFlowThisFunc(("\n"));
14444
14445 AutoCaller autoCaller(this);
14446 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14447
14448 ComPtr<IInternalSessionControl> directControl;
14449 {
14450 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14451 if (mData->mSession.mLockType == LockType_VM)
14452 directControl = mData->mSession.mDirectControl;
14453 }
14454
14455 /* ignore notifications sent after #OnSessionEnd() is called */
14456 if (!directControl)
14457 return S_OK;
14458
14459 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14460}
14461
14462/**
14463 * @note Locks this object for reading.
14464 */
14465HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14466{
14467 LogFlowThisFunc(("\n"));
14468
14469 AutoCaller autoCaller(this);
14470 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14471
14472 ComPtr<IInternalSessionControl> directControl;
14473 {
14474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14475 if (mData->mSession.mLockType == LockType_VM)
14476 directControl = mData->mSession.mDirectControl;
14477 }
14478
14479 /* ignore notifications sent after #OnSessionEnd() is called */
14480 if (!directControl)
14481 return S_OK;
14482
14483 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14484}
14485
14486/**
14487 * Returns @c true if this machine's USB controller reports it has a matching
14488 * filter for the given USB device and @c false otherwise.
14489 *
14490 * @note locks this object for reading.
14491 */
14492bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14493{
14494 AutoCaller autoCaller(this);
14495 /* silently return if not ready -- this method may be called after the
14496 * direct machine session has been called */
14497 if (!autoCaller.isOk())
14498 return false;
14499
14500#ifdef VBOX_WITH_USB
14501 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14502
14503 switch (mData->mMachineState)
14504 {
14505 case MachineState_Starting:
14506 case MachineState_Restoring:
14507 case MachineState_TeleportingIn:
14508 case MachineState_Paused:
14509 case MachineState_Running:
14510 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14511 * elsewhere... */
14512 alock.release();
14513 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14514 default: break;
14515 }
14516#else
14517 NOREF(aDevice);
14518 NOREF(aMaskedIfs);
14519#endif
14520 return false;
14521}
14522
14523/**
14524 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14525 */
14526HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14527 IVirtualBoxErrorInfo *aError,
14528 ULONG aMaskedIfs,
14529 const com::Utf8Str &aCaptureFilename)
14530{
14531 LogFlowThisFunc(("\n"));
14532
14533 AutoCaller autoCaller(this);
14534
14535 /* This notification may happen after the machine object has been
14536 * uninitialized (the session was closed), so don't assert. */
14537 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14538
14539 ComPtr<IInternalSessionControl> directControl;
14540 {
14541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14542 if (mData->mSession.mLockType == LockType_VM)
14543 directControl = mData->mSession.mDirectControl;
14544 }
14545
14546 /* fail on notifications sent after #OnSessionEnd() is called, it is
14547 * expected by the caller */
14548 if (!directControl)
14549 return E_FAIL;
14550
14551 /* No locks should be held at this point. */
14552 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14553 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14554
14555 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14556}
14557
14558/**
14559 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14560 */
14561HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14562 IVirtualBoxErrorInfo *aError)
14563{
14564 LogFlowThisFunc(("\n"));
14565
14566 AutoCaller autoCaller(this);
14567
14568 /* This notification may happen after the machine object has been
14569 * uninitialized (the session was closed), so don't assert. */
14570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14571
14572 ComPtr<IInternalSessionControl> directControl;
14573 {
14574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14575 if (mData->mSession.mLockType == LockType_VM)
14576 directControl = mData->mSession.mDirectControl;
14577 }
14578
14579 /* fail on notifications sent after #OnSessionEnd() is called, it is
14580 * expected by the caller */
14581 if (!directControl)
14582 return E_FAIL;
14583
14584 /* No locks should be held at this point. */
14585 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14586 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14587
14588 return directControl->OnUSBDeviceDetach(aId, aError);
14589}
14590
14591// protected methods
14592/////////////////////////////////////////////////////////////////////////////
14593
14594/**
14595 * Deletes the given file if it is no longer in use by either the current machine state
14596 * (if the machine is "saved") or any of the machine's snapshots.
14597 *
14598 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14599 * but is different for each SnapshotMachine. When calling this, the order of calling this
14600 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14601 * is therefore critical. I know, it's all rather messy.
14602 *
14603 * @param strStateFile
14604 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14605 * the test for whether the saved state file is in use.
14606 */
14607void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14608 Snapshot *pSnapshotToIgnore)
14609{
14610 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14611 if ( (strStateFile.isNotEmpty())
14612 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14613 )
14614 // ... and it must also not be shared with other snapshots
14615 if ( !mData->mFirstSnapshot
14616 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14617 // this checks the SnapshotMachine's state file paths
14618 )
14619 RTFileDelete(strStateFile.c_str());
14620}
14621
14622/**
14623 * Locks the attached media.
14624 *
14625 * All attached hard disks are locked for writing and DVD/floppy are locked for
14626 * reading. Parents of attached hard disks (if any) are locked for reading.
14627 *
14628 * This method also performs accessibility check of all media it locks: if some
14629 * media is inaccessible, the method will return a failure and a bunch of
14630 * extended error info objects per each inaccessible medium.
14631 *
14632 * Note that this method is atomic: if it returns a success, all media are
14633 * locked as described above; on failure no media is locked at all (all
14634 * succeeded individual locks will be undone).
14635 *
14636 * The caller is responsible for doing the necessary state sanity checks.
14637 *
14638 * The locks made by this method must be undone by calling #unlockMedia() when
14639 * no more needed.
14640 */
14641HRESULT SessionMachine::i_lockMedia()
14642{
14643 AutoCaller autoCaller(this);
14644 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14645
14646 AutoMultiWriteLock2 alock(this->lockHandle(),
14647 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14648
14649 /* bail out if trying to lock things with already set up locking */
14650 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14651
14652 MultiResult mrc(S_OK);
14653
14654 /* Collect locking information for all medium objects attached to the VM. */
14655 for (MediumAttachmentList::const_iterator
14656 it = mMediumAttachments->begin();
14657 it != mMediumAttachments->end();
14658 ++it)
14659 {
14660 MediumAttachment *pAtt = *it;
14661 DeviceType_T devType = pAtt->i_getType();
14662 Medium *pMedium = pAtt->i_getMedium();
14663
14664 MediumLockList *pMediumLockList(new MediumLockList());
14665 // There can be attachments without a medium (floppy/dvd), and thus
14666 // it's impossible to create a medium lock list. It still makes sense
14667 // to have the empty medium lock list in the map in case a medium is
14668 // attached later.
14669 if (pMedium != NULL)
14670 {
14671 MediumType_T mediumType = pMedium->i_getType();
14672 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14673 || mediumType == MediumType_Shareable;
14674 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14675
14676 alock.release();
14677 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14678 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14679 false /* fMediumLockWriteAll */,
14680 NULL,
14681 *pMediumLockList);
14682 alock.acquire();
14683 if (FAILED(mrc))
14684 {
14685 delete pMediumLockList;
14686 mData->mSession.mLockedMedia.Clear();
14687 break;
14688 }
14689 }
14690
14691 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14692 if (FAILED(rc))
14693 {
14694 mData->mSession.mLockedMedia.Clear();
14695 mrc = setError(rc,
14696 tr("Collecting locking information for all attached media failed"));
14697 break;
14698 }
14699 }
14700
14701 if (SUCCEEDED(mrc))
14702 {
14703 /* Now lock all media. If this fails, nothing is locked. */
14704 alock.release();
14705 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14706 alock.acquire();
14707 if (FAILED(rc))
14708 {
14709 mrc = setError(rc,
14710 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14711 }
14712 }
14713
14714 return mrc;
14715}
14716
14717/**
14718 * Undoes the locks made by by #lockMedia().
14719 */
14720HRESULT SessionMachine::i_unlockMedia()
14721{
14722 AutoCaller autoCaller(this);
14723 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14724
14725 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14726
14727 /* we may be holding important error info on the current thread;
14728 * preserve it */
14729 ErrorInfoKeeper eik;
14730
14731 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14732 AssertComRC(rc);
14733 return rc;
14734}
14735
14736/**
14737 * Helper to change the machine state (reimplementation).
14738 *
14739 * @note Locks this object for writing.
14740 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14741 * it can cause crashes in random places due to unexpectedly committing
14742 * the current settings. The caller is responsible for that. The call
14743 * to saveStateSettings is fine, because this method does not commit.
14744 */
14745HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14746{
14747 LogFlowThisFuncEnter();
14748 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14749
14750 AutoCaller autoCaller(this);
14751 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14752
14753 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14754
14755 MachineState_T oldMachineState = mData->mMachineState;
14756
14757 AssertMsgReturn(oldMachineState != aMachineState,
14758 ("oldMachineState=%s, aMachineState=%s\n",
14759 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14760 E_FAIL);
14761
14762 HRESULT rc = S_OK;
14763
14764 int stsFlags = 0;
14765 bool deleteSavedState = false;
14766
14767 /* detect some state transitions */
14768
14769 if ( ( oldMachineState == MachineState_Saved
14770 && aMachineState == MachineState_Restoring)
14771 || ( ( oldMachineState == MachineState_PoweredOff
14772 || oldMachineState == MachineState_Teleported
14773 || oldMachineState == MachineState_Aborted
14774 )
14775 && ( aMachineState == MachineState_TeleportingIn
14776 || aMachineState == MachineState_Starting
14777 )
14778 )
14779 )
14780 {
14781 /* The EMT thread is about to start */
14782
14783 /* Nothing to do here for now... */
14784
14785 /// @todo NEWMEDIA don't let mDVDDrive and other children
14786 /// change anything when in the Starting/Restoring state
14787 }
14788 else if ( ( oldMachineState == MachineState_Running
14789 || oldMachineState == MachineState_Paused
14790 || oldMachineState == MachineState_Teleporting
14791 || oldMachineState == MachineState_OnlineSnapshotting
14792 || oldMachineState == MachineState_LiveSnapshotting
14793 || oldMachineState == MachineState_Stuck
14794 || oldMachineState == MachineState_Starting
14795 || oldMachineState == MachineState_Stopping
14796 || oldMachineState == MachineState_Saving
14797 || oldMachineState == MachineState_Restoring
14798 || oldMachineState == MachineState_TeleportingPausedVM
14799 || oldMachineState == MachineState_TeleportingIn
14800 )
14801 && ( aMachineState == MachineState_PoweredOff
14802 || aMachineState == MachineState_Saved
14803 || aMachineState == MachineState_Teleported
14804 || aMachineState == MachineState_Aborted
14805 )
14806 )
14807 {
14808 /* The EMT thread has just stopped, unlock attached media. Note that as
14809 * opposed to locking that is done from Console, we do unlocking here
14810 * because the VM process may have aborted before having a chance to
14811 * properly unlock all media it locked. */
14812
14813 unlockMedia();
14814 }
14815
14816 if (oldMachineState == MachineState_Restoring)
14817 {
14818 if (aMachineState != MachineState_Saved)
14819 {
14820 /*
14821 * delete the saved state file once the machine has finished
14822 * restoring from it (note that Console sets the state from
14823 * Restoring to Saved if the VM couldn't restore successfully,
14824 * to give the user an ability to fix an error and retry --
14825 * we keep the saved state file in this case)
14826 */
14827 deleteSavedState = true;
14828 }
14829 }
14830 else if ( oldMachineState == MachineState_Saved
14831 && ( aMachineState == MachineState_PoweredOff
14832 || aMachineState == MachineState_Aborted
14833 || aMachineState == MachineState_Teleported
14834 )
14835 )
14836 {
14837 /*
14838 * delete the saved state after SessionMachine::ForgetSavedState() is called
14839 * or if the VM process (owning a direct VM session) crashed while the
14840 * VM was Saved
14841 */
14842
14843 /// @todo (dmik)
14844 // Not sure that deleting the saved state file just because of the
14845 // client death before it attempted to restore the VM is a good
14846 // thing. But when it crashes we need to go to the Aborted state
14847 // which cannot have the saved state file associated... The only
14848 // way to fix this is to make the Aborted condition not a VM state
14849 // but a bool flag: i.e., when a crash occurs, set it to true and
14850 // change the state to PoweredOff or Saved depending on the
14851 // saved state presence.
14852
14853 deleteSavedState = true;
14854 mData->mCurrentStateModified = TRUE;
14855 stsFlags |= SaveSTS_CurStateModified;
14856 }
14857
14858 if ( aMachineState == MachineState_Starting
14859 || aMachineState == MachineState_Restoring
14860 || aMachineState == MachineState_TeleportingIn
14861 )
14862 {
14863 /* set the current state modified flag to indicate that the current
14864 * state is no more identical to the state in the
14865 * current snapshot */
14866 if (!mData->mCurrentSnapshot.isNull())
14867 {
14868 mData->mCurrentStateModified = TRUE;
14869 stsFlags |= SaveSTS_CurStateModified;
14870 }
14871 }
14872
14873 if (deleteSavedState)
14874 {
14875 if (mRemoveSavedState)
14876 {
14877 Assert(!mSSData->strStateFilePath.isEmpty());
14878
14879 // it is safe to delete the saved state file if ...
14880 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14881 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14882 // ... none of the snapshots share the saved state file
14883 )
14884 RTFileDelete(mSSData->strStateFilePath.c_str());
14885 }
14886
14887 mSSData->strStateFilePath.setNull();
14888 stsFlags |= SaveSTS_StateFilePath;
14889 }
14890
14891 /* redirect to the underlying peer machine */
14892 mPeer->i_setMachineState(aMachineState);
14893
14894 if ( oldMachineState != MachineState_RestoringSnapshot
14895 && ( aMachineState == MachineState_PoweredOff
14896 || aMachineState == MachineState_Teleported
14897 || aMachineState == MachineState_Aborted
14898 || aMachineState == MachineState_Saved))
14899 {
14900 /* the machine has stopped execution
14901 * (or the saved state file was adopted) */
14902 stsFlags |= SaveSTS_StateTimeStamp;
14903 }
14904
14905 if ( ( oldMachineState == MachineState_PoweredOff
14906 || oldMachineState == MachineState_Aborted
14907 || oldMachineState == MachineState_Teleported
14908 )
14909 && aMachineState == MachineState_Saved)
14910 {
14911 /* the saved state file was adopted */
14912 Assert(!mSSData->strStateFilePath.isEmpty());
14913 stsFlags |= SaveSTS_StateFilePath;
14914 }
14915
14916#ifdef VBOX_WITH_GUEST_PROPS
14917 if ( aMachineState == MachineState_PoweredOff
14918 || aMachineState == MachineState_Aborted
14919 || aMachineState == MachineState_Teleported)
14920 {
14921 /* Make sure any transient guest properties get removed from the
14922 * property store on shutdown. */
14923 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14924
14925 /* remove it from the settings representation */
14926 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14927 for (settings::GuestPropertiesList::iterator
14928 it = llGuestProperties.begin();
14929 it != llGuestProperties.end();
14930 /*nothing*/)
14931 {
14932 const settings::GuestProperty &prop = *it;
14933 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14934 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14935 {
14936 it = llGuestProperties.erase(it);
14937 fNeedsSaving = true;
14938 }
14939 else
14940 {
14941 ++it;
14942 }
14943 }
14944
14945 /* Additionally remove it from the HWData representation. Required to
14946 * keep everything in sync, as this is what the API keeps using. */
14947 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14948 for (HWData::GuestPropertyMap::iterator
14949 it = llHWGuestProperties.begin();
14950 it != llHWGuestProperties.end();
14951 /*nothing*/)
14952 {
14953 uint32_t fFlags = it->second.mFlags;
14954 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14955 {
14956 /* iterator where we need to continue after the erase call
14957 * (C++03 is a fact still, and it doesn't return the iterator
14958 * which would allow continuing) */
14959 HWData::GuestPropertyMap::iterator it2 = it;
14960 ++it2;
14961 llHWGuestProperties.erase(it);
14962 it = it2;
14963 fNeedsSaving = true;
14964 }
14965 else
14966 {
14967 ++it;
14968 }
14969 }
14970
14971 if (fNeedsSaving)
14972 {
14973 mData->mCurrentStateModified = TRUE;
14974 stsFlags |= SaveSTS_CurStateModified;
14975 }
14976 }
14977#endif /* VBOX_WITH_GUEST_PROPS */
14978
14979 rc = i_saveStateSettings(stsFlags);
14980
14981 if ( ( oldMachineState != MachineState_PoweredOff
14982 && oldMachineState != MachineState_Aborted
14983 && oldMachineState != MachineState_Teleported
14984 )
14985 && ( aMachineState == MachineState_PoweredOff
14986 || aMachineState == MachineState_Aborted
14987 || aMachineState == MachineState_Teleported
14988 )
14989 )
14990 {
14991 /* we've been shut down for any reason */
14992 /* no special action so far */
14993 }
14994
14995 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14996 LogFlowThisFuncLeave();
14997 return rc;
14998}
14999
15000/**
15001 * Sends the current machine state value to the VM process.
15002 *
15003 * @note Locks this object for reading, then calls a client process.
15004 */
15005HRESULT SessionMachine::i_updateMachineStateOnClient()
15006{
15007 AutoCaller autoCaller(this);
15008 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15009
15010 ComPtr<IInternalSessionControl> directControl;
15011 {
15012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15013 AssertReturn(!!mData, E_FAIL);
15014 if (mData->mSession.mLockType == LockType_VM)
15015 directControl = mData->mSession.mDirectControl;
15016
15017 /* directControl may be already set to NULL here in #OnSessionEnd()
15018 * called too early by the direct session process while there is still
15019 * some operation (like deleting the snapshot) in progress. The client
15020 * process in this case is waiting inside Session::close() for the
15021 * "end session" process object to complete, while #uninit() called by
15022 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15023 * operation to complete. For now, we accept this inconsistent behavior
15024 * and simply do nothing here. */
15025
15026 if (mData->mSession.mState == SessionState_Unlocking)
15027 return S_OK;
15028 }
15029
15030 /* ignore notifications sent after #OnSessionEnd() is called */
15031 if (!directControl)
15032 return S_OK;
15033
15034 return directControl->UpdateMachineState(mData->mMachineState);
15035}
15036
15037
15038/*static*/
15039HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15040{
15041 va_list args;
15042 va_start(args, pcszMsg);
15043 HRESULT rc = setErrorInternal(aResultCode,
15044 getStaticClassIID(),
15045 getStaticComponentName(),
15046 Utf8Str(pcszMsg, args),
15047 false /* aWarning */,
15048 true /* aLogIt */);
15049 va_end(args);
15050 return rc;
15051}
15052
15053
15054HRESULT Machine::updateState(MachineState_T aState)
15055{
15056 NOREF(aState);
15057 ReturnComNotImplemented();
15058}
15059
15060HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15061{
15062 NOREF(aProgress);
15063 ReturnComNotImplemented();
15064}
15065
15066HRESULT Machine::endPowerUp(LONG aResult)
15067{
15068 NOREF(aResult);
15069 ReturnComNotImplemented();
15070}
15071
15072HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15073{
15074 NOREF(aProgress);
15075 ReturnComNotImplemented();
15076}
15077
15078HRESULT Machine::endPoweringDown(LONG aResult,
15079 const com::Utf8Str &aErrMsg)
15080{
15081 NOREF(aResult);
15082 NOREF(aErrMsg);
15083 ReturnComNotImplemented();
15084}
15085
15086HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15087 BOOL *aMatched,
15088 ULONG *aMaskedInterfaces)
15089{
15090 NOREF(aDevice);
15091 NOREF(aMatched);
15092 NOREF(aMaskedInterfaces);
15093 ReturnComNotImplemented();
15094
15095}
15096
15097HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15098{
15099 NOREF(aId); NOREF(aCaptureFilename);
15100 ReturnComNotImplemented();
15101}
15102
15103HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15104 BOOL aDone)
15105{
15106 NOREF(aId);
15107 NOREF(aDone);
15108 ReturnComNotImplemented();
15109}
15110
15111HRESULT Machine::autoCaptureUSBDevices()
15112{
15113 ReturnComNotImplemented();
15114}
15115
15116HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15117{
15118 NOREF(aDone);
15119 ReturnComNotImplemented();
15120}
15121
15122HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15123 ComPtr<IProgress> &aProgress)
15124{
15125 NOREF(aSession);
15126 NOREF(aProgress);
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::finishOnlineMergeMedium()
15131{
15132 ReturnComNotImplemented();
15133}
15134
15135HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15136 std::vector<com::Utf8Str> &aValues,
15137 std::vector<LONG64> &aTimestamps,
15138 std::vector<com::Utf8Str> &aFlags)
15139{
15140 NOREF(aNames);
15141 NOREF(aValues);
15142 NOREF(aTimestamps);
15143 NOREF(aFlags);
15144 ReturnComNotImplemented();
15145}
15146
15147HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15148 const com::Utf8Str &aValue,
15149 LONG64 aTimestamp,
15150 const com::Utf8Str &aFlags)
15151{
15152 NOREF(aName);
15153 NOREF(aValue);
15154 NOREF(aTimestamp);
15155 NOREF(aFlags);
15156 ReturnComNotImplemented();
15157}
15158
15159HRESULT Machine::lockMedia()
15160{
15161 ReturnComNotImplemented();
15162}
15163
15164HRESULT Machine::unlockMedia()
15165{
15166 ReturnComNotImplemented();
15167}
15168
15169HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15170 ComPtr<IMediumAttachment> &aNewAttachment)
15171{
15172 NOREF(aAttachment);
15173 NOREF(aNewAttachment);
15174 ReturnComNotImplemented();
15175}
15176
15177HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15178 ULONG aCpuUser,
15179 ULONG aCpuKernel,
15180 ULONG aCpuIdle,
15181 ULONG aMemTotal,
15182 ULONG aMemFree,
15183 ULONG aMemBalloon,
15184 ULONG aMemShared,
15185 ULONG aMemCache,
15186 ULONG aPagedTotal,
15187 ULONG aMemAllocTotal,
15188 ULONG aMemFreeTotal,
15189 ULONG aMemBalloonTotal,
15190 ULONG aMemSharedTotal,
15191 ULONG aVmNetRx,
15192 ULONG aVmNetTx)
15193{
15194 NOREF(aValidStats);
15195 NOREF(aCpuUser);
15196 NOREF(aCpuKernel);
15197 NOREF(aCpuIdle);
15198 NOREF(aMemTotal);
15199 NOREF(aMemFree);
15200 NOREF(aMemBalloon);
15201 NOREF(aMemShared);
15202 NOREF(aMemCache);
15203 NOREF(aPagedTotal);
15204 NOREF(aMemAllocTotal);
15205 NOREF(aMemFreeTotal);
15206 NOREF(aMemBalloonTotal);
15207 NOREF(aMemSharedTotal);
15208 NOREF(aVmNetRx);
15209 NOREF(aVmNetTx);
15210 ReturnComNotImplemented();
15211}
15212
15213HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15214 com::Utf8Str &aResult)
15215{
15216 NOREF(aAuthParams);
15217 NOREF(aResult);
15218 ReturnComNotImplemented();
15219}
15220
15221HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15222{
15223 NOREF(aFlags);
15224 ReturnComNotImplemented();
15225}
15226
15227/* This isn't handled entirely by the wrapper generator yet. */
15228#ifdef VBOX_WITH_XPCOM
15229NS_DECL_CLASSINFO(SessionMachine)
15230NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15231
15232NS_DECL_CLASSINFO(SnapshotMachine)
15233NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15234#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