VirtualBox

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

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

scm: fix trailing whitespace

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 524.8 KB
Line 
1/* $Id: MachineImpl.cpp 70650 2018-01-19 14:59:09Z 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 mHPETEnabled = false;
201 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
202 mCpuIdPortabilityLevel = 0;
203 mCpuProfile = "host";
204
205 /* default boot order: floppy - DVD - HDD */
206 mBootOrder[0] = DeviceType_Floppy;
207 mBootOrder[1] = DeviceType_DVD;
208 mBootOrder[2] = DeviceType_HardDisk;
209 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
210 mBootOrder[i] = DeviceType_Null;
211
212 mClipboardMode = ClipboardMode_Disabled;
213 mDnDMode = DnDMode_Disabled;
214
215 mFirmwareType = FirmwareType_BIOS;
216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
217 mPointingHIDType = PointingHIDType_PS2Mouse;
218 mChipsetType = ChipsetType_PIIX3;
219 mParavirtProvider = ParavirtProvider_Default;
220 mEmulatedUSBCardReaderEnabled = FALSE;
221
222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
223 mCPUAttached[i] = false;
224
225 mIOCacheEnabled = true;
226 mIOCacheSize = 5; /* 5MB */
227}
228
229Machine::HWData::~HWData()
230{
231}
232
233/////////////////////////////////////////////////////////////////////////////
234// Machine class
235/////////////////////////////////////////////////////////////////////////////
236
237// constructor / destructor
238/////////////////////////////////////////////////////////////////////////////
239
240Machine::Machine() :
241#ifdef VBOX_WITH_RESOURCE_USAGE_API
242 mCollectorGuest(NULL),
243#endif
244 mPeer(NULL),
245 mParent(NULL),
246 mSerialPorts(),
247 mParallelPorts(),
248 uRegistryNeedsSaving(0)
249{}
250
251Machine::~Machine()
252{}
253
254HRESULT Machine::FinalConstruct()
255{
256 LogFlowThisFunc(("\n"));
257 return BaseFinalConstruct();
258}
259
260void Machine::FinalRelease()
261{
262 LogFlowThisFunc(("\n"));
263 uninit();
264 BaseFinalRelease();
265}
266
267/**
268 * Initializes a new machine instance; this init() variant creates a new, empty machine.
269 * This gets called from VirtualBox::CreateMachine().
270 *
271 * @param aParent Associated parent object
272 * @param strConfigFile Local file system path to the VM settings file (can
273 * be relative to the VirtualBox config directory).
274 * @param strName name for the machine
275 * @param llGroups list of groups for the machine
276 * @param aOsType OS Type of this machine or NULL.
277 * @param aId UUID for the new machine.
278 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
279 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
280 * scheme (includes the UUID).
281 *
282 * @return Success indicator. if not S_OK, the machine object is invalid
283 */
284HRESULT Machine::init(VirtualBox *aParent,
285 const Utf8Str &strConfigFile,
286 const Utf8Str &strName,
287 const StringsList &llGroups,
288 GuestOSType *aOsType,
289 const Guid &aId,
290 bool fForceOverwrite,
291 bool fDirectoryIncludesUUID)
292{
293 LogFlowThisFuncEnter();
294 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
295
296 /* Enclose the state transition NotReady->InInit->Ready */
297 AutoInitSpan autoInitSpan(this);
298 AssertReturn(autoInitSpan.isOk(), E_FAIL);
299
300 HRESULT rc = initImpl(aParent, strConfigFile);
301 if (FAILED(rc)) return rc;
302
303 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
304 if (FAILED(rc)) return rc;
305
306 if (SUCCEEDED(rc))
307 {
308 // create an empty machine config
309 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
310
311 rc = initDataAndChildObjects();
312 }
313
314 if (SUCCEEDED(rc))
315 {
316 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
317 mData->mAccessible = TRUE;
318
319 unconst(mData->mUuid) = aId;
320
321 mUserData->s.strName = strName;
322
323 mUserData->s.llGroups = llGroups;
324
325 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
326 // the "name sync" flag determines whether the machine directory gets renamed along
327 // with the machine file; say so if the settings file name is the same as the
328 // settings file parent directory (machine directory)
329 mUserData->s.fNameSync = i_isInOwnDir();
330
331 // initialize the default snapshots folder
332 rc = COMSETTER(SnapshotFolder)(NULL);
333 AssertComRC(rc);
334
335 if (aOsType)
336 {
337 /* Store OS type */
338 mUserData->s.strOsType = aOsType->i_id();
339
340 /* Apply BIOS defaults */
341 mBIOSSettings->i_applyDefaults(aOsType);
342
343 /* Let the OS type select 64-bit ness. */
344 mHWData->mLongMode = aOsType->i_is64Bit()
345 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
346
347 /* Let the OS type enable the X2APIC */
348 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
349 }
350
351 /* Apply network adapters defaults */
352 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
353 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
354
355 /* Apply serial port defaults */
356 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
357 mSerialPorts[slot]->i_applyDefaults(aOsType);
358
359 /* Apply parallel port defaults */
360 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
361 mParallelPorts[slot]->i_applyDefaults();
362
363 /* At this point the changing of the current state modification
364 * flag is allowed. */
365 i_allowStateModification();
366
367 /* commit all changes made during the initialization */
368 i_commit();
369 }
370
371 /* Confirm a successful initialization when it's the case */
372 if (SUCCEEDED(rc))
373 {
374 if (mData->mAccessible)
375 autoInitSpan.setSucceeded();
376 else
377 autoInitSpan.setLimited();
378 }
379
380 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
381 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
382 mData->mRegistered,
383 mData->mAccessible,
384 rc));
385
386 LogFlowThisFuncLeave();
387
388 return rc;
389}
390
391/**
392 * Initializes a new instance with data from machine XML (formerly Init_Registered).
393 * Gets called in two modes:
394 *
395 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
396 * UUID is specified and we mark the machine as "registered";
397 *
398 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
399 * and the machine remains unregistered until RegisterMachine() is called.
400 *
401 * @param aParent Associated parent object
402 * @param strConfigFile Local file system path to the VM settings file (can
403 * be relative to the VirtualBox config directory).
404 * @param aId UUID of the machine or NULL (see above).
405 *
406 * @return Success indicator. if not S_OK, the machine object is invalid
407 */
408HRESULT Machine::initFromSettings(VirtualBox *aParent,
409 const Utf8Str &strConfigFile,
410 const Guid *aId)
411{
412 LogFlowThisFuncEnter();
413 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
414
415 /* Enclose the state transition NotReady->InInit->Ready */
416 AutoInitSpan autoInitSpan(this);
417 AssertReturn(autoInitSpan.isOk(), E_FAIL);
418
419 HRESULT rc = initImpl(aParent, strConfigFile);
420 if (FAILED(rc)) return rc;
421
422 if (aId)
423 {
424 // loading a registered VM:
425 unconst(mData->mUuid) = *aId;
426 mData->mRegistered = TRUE;
427 // now load the settings from XML:
428 rc = i_registeredInit();
429 // this calls initDataAndChildObjects() and loadSettings()
430 }
431 else
432 {
433 // opening an unregistered VM (VirtualBox::OpenMachine()):
434 rc = initDataAndChildObjects();
435
436 if (SUCCEEDED(rc))
437 {
438 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
439 mData->mAccessible = TRUE;
440
441 try
442 {
443 // load and parse machine XML; this will throw on XML or logic errors
444 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
445
446 // reject VM UUID duplicates, they can happen if someone
447 // tries to register an already known VM config again
448 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
449 true /* fPermitInaccessible */,
450 false /* aDoSetError */,
451 NULL) != VBOX_E_OBJECT_NOT_FOUND)
452 {
453 throw setError(E_FAIL,
454 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
455 mData->m_strConfigFile.c_str());
456 }
457
458 // use UUID from machine config
459 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
460
461 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
462 NULL /* puuidRegistry */);
463 if (FAILED(rc)) throw rc;
464
465 /* At this point the changing of the current state modification
466 * flag is allowed. */
467 i_allowStateModification();
468
469 i_commit();
470 }
471 catch (HRESULT err)
472 {
473 /* we assume that error info is set by the thrower */
474 rc = err;
475 }
476 catch (...)
477 {
478 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
479 }
480 }
481 }
482
483 /* Confirm a successful initialization when it's the case */
484 if (SUCCEEDED(rc))
485 {
486 if (mData->mAccessible)
487 autoInitSpan.setSucceeded();
488 else
489 {
490 autoInitSpan.setLimited();
491
492 // uninit media from this machine's media registry, or else
493 // reloading the settings will fail
494 mParent->i_unregisterMachineMedia(i_getId());
495 }
496 }
497
498 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
499 "rc=%08X\n",
500 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
501 mData->mRegistered, mData->mAccessible, rc));
502
503 LogFlowThisFuncLeave();
504
505 return rc;
506}
507
508/**
509 * Initializes a new instance from a machine config that is already in memory
510 * (import OVF case). Since we are importing, the UUID in the machine
511 * config is ignored and we always generate a fresh one.
512 *
513 * @param aParent Associated parent object.
514 * @param strName Name for the new machine; this overrides what is specified in config and is used
515 * for the settings file as well.
516 * @param config Machine configuration loaded and parsed from XML.
517 *
518 * @return Success indicator. if not S_OK, the machine object is invalid
519 */
520HRESULT Machine::init(VirtualBox *aParent,
521 const Utf8Str &strName,
522 const settings::MachineConfigFile &config)
523{
524 LogFlowThisFuncEnter();
525
526 /* Enclose the state transition NotReady->InInit->Ready */
527 AutoInitSpan autoInitSpan(this);
528 AssertReturn(autoInitSpan.isOk(), E_FAIL);
529
530 Utf8Str strConfigFile;
531 aParent->i_getDefaultMachineFolder(strConfigFile);
532 strConfigFile.append(RTPATH_DELIMITER);
533 strConfigFile.append(strName);
534 strConfigFile.append(RTPATH_DELIMITER);
535 strConfigFile.append(strName);
536 strConfigFile.append(".vbox");
537
538 HRESULT rc = initImpl(aParent, strConfigFile);
539 if (FAILED(rc)) return rc;
540
541 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
542 if (FAILED(rc)) return rc;
543
544 rc = initDataAndChildObjects();
545
546 if (SUCCEEDED(rc))
547 {
548 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
549 mData->mAccessible = TRUE;
550
551 // create empty machine config for instance data
552 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
553
554 // generate fresh UUID, ignore machine config
555 unconst(mData->mUuid).create();
556
557 rc = i_loadMachineDataFromSettings(config,
558 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
559
560 // override VM name as well, it may be different
561 mUserData->s.strName = strName;
562
563 if (SUCCEEDED(rc))
564 {
565 /* At this point the changing of the current state modification
566 * flag is allowed. */
567 i_allowStateModification();
568
569 /* commit all changes made during the initialization */
570 i_commit();
571 }
572 }
573
574 /* Confirm a successful initialization when it's the case */
575 if (SUCCEEDED(rc))
576 {
577 if (mData->mAccessible)
578 autoInitSpan.setSucceeded();
579 else
580 {
581 /* Ignore all errors from unregistering, they would destroy
582- * the more interesting error information we already have,
583- * pinpointing the issue with the VM config. */
584 ErrorInfoKeeper eik;
585
586 autoInitSpan.setLimited();
587
588 // uninit media from this machine's media registry, or else
589 // reloading the settings will fail
590 mParent->i_unregisterMachineMedia(i_getId());
591 }
592 }
593
594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
595 "rc=%08X\n",
596 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
597 mData->mRegistered, mData->mAccessible, rc));
598
599 LogFlowThisFuncLeave();
600
601 return rc;
602}
603
604/**
605 * Shared code between the various init() implementations.
606 * @param aParent The VirtualBox object.
607 * @param strConfigFile Settings file.
608 * @return
609 */
610HRESULT Machine::initImpl(VirtualBox *aParent,
611 const Utf8Str &strConfigFile)
612{
613 LogFlowThisFuncEnter();
614
615 AssertReturn(aParent, E_INVALIDARG);
616 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
617
618 HRESULT rc = S_OK;
619
620 /* share the parent weakly */
621 unconst(mParent) = aParent;
622
623 /* allocate the essential machine data structure (the rest will be
624 * allocated later by initDataAndChildObjects() */
625 mData.allocate();
626
627 /* memorize the config file name (as provided) */
628 mData->m_strConfigFile = strConfigFile;
629
630 /* get the full file name */
631 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
632 if (RT_FAILURE(vrc1))
633 return setError(VBOX_E_FILE_ERROR,
634 tr("Invalid machine settings file name '%s' (%Rrc)"),
635 strConfigFile.c_str(),
636 vrc1);
637
638 LogFlowThisFuncLeave();
639
640 return rc;
641}
642
643/**
644 * Tries to create a machine settings file in the path stored in the machine
645 * instance data. Used when a new machine is created to fail gracefully if
646 * the settings file could not be written (e.g. because machine dir is read-only).
647 * @return
648 */
649HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
650{
651 HRESULT rc = S_OK;
652
653 // when we create a new machine, we must be able to create the settings file
654 RTFILE f = NIL_RTFILE;
655 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
656 if ( RT_SUCCESS(vrc)
657 || vrc == VERR_SHARING_VIOLATION
658 )
659 {
660 if (RT_SUCCESS(vrc))
661 RTFileClose(f);
662 if (!fForceOverwrite)
663 rc = setError(VBOX_E_FILE_ERROR,
664 tr("Machine settings file '%s' already exists"),
665 mData->m_strConfigFileFull.c_str());
666 else
667 {
668 /* try to delete the config file, as otherwise the creation
669 * of a new settings file will fail. */
670 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
671 if (RT_FAILURE(vrc2))
672 rc = setError(VBOX_E_FILE_ERROR,
673 tr("Could not delete the existing settings file '%s' (%Rrc)"),
674 mData->m_strConfigFileFull.c_str(), vrc2);
675 }
676 }
677 else if ( vrc != VERR_FILE_NOT_FOUND
678 && vrc != VERR_PATH_NOT_FOUND
679 )
680 rc = setError(VBOX_E_FILE_ERROR,
681 tr("Invalid machine settings file name '%s' (%Rrc)"),
682 mData->m_strConfigFileFull.c_str(),
683 vrc);
684 return rc;
685}
686
687/**
688 * Initializes the registered machine by loading the settings file.
689 * This method is separated from #init() in order to make it possible to
690 * retry the operation after VirtualBox startup instead of refusing to
691 * startup the whole VirtualBox server in case if the settings file of some
692 * registered VM is invalid or inaccessible.
693 *
694 * @note Must be always called from this object's write lock
695 * (unless called from #init() that doesn't need any locking).
696 * @note Locks the mUSBController method for writing.
697 * @note Subclasses must not call this method.
698 */
699HRESULT Machine::i_registeredInit()
700{
701 AssertReturn(!i_isSessionMachine(), E_FAIL);
702 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
703 AssertReturn(mData->mUuid.isValid(), E_FAIL);
704 AssertReturn(!mData->mAccessible, E_FAIL);
705
706 HRESULT rc = initDataAndChildObjects();
707
708 if (SUCCEEDED(rc))
709 {
710 /* Temporarily reset the registered flag in order to let setters
711 * potentially called from loadSettings() succeed (isMutable() used in
712 * all setters will return FALSE for a Machine instance if mRegistered
713 * is TRUE). */
714 mData->mRegistered = FALSE;
715
716 try
717 {
718 // load and parse machine XML; this will throw on XML or logic errors
719 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
720
721 if (mData->mUuid != mData->pMachineConfigFile->uuid)
722 throw setError(E_FAIL,
723 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
724 mData->pMachineConfigFile->uuid.raw(),
725 mData->m_strConfigFileFull.c_str(),
726 mData->mUuid.toString().c_str(),
727 mParent->i_settingsFilePath().c_str());
728
729 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
730 NULL /* const Guid *puuidRegistry */);
731 if (FAILED(rc)) throw rc;
732 }
733 catch (HRESULT err)
734 {
735 /* we assume that error info is set by the thrower */
736 rc = err;
737 }
738 catch (...)
739 {
740 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
741 }
742
743 /* Restore the registered flag (even on failure) */
744 mData->mRegistered = TRUE;
745 }
746
747 if (SUCCEEDED(rc))
748 {
749 /* Set mAccessible to TRUE only if we successfully locked and loaded
750 * the settings file */
751 mData->mAccessible = TRUE;
752
753 /* commit all changes made during loading the settings file */
754 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
755 /// @todo r=klaus for some reason the settings loading logic backs up
756 // the settings, and therefore a commit is needed. Should probably be changed.
757 }
758 else
759 {
760 /* If the machine is registered, then, instead of returning a
761 * failure, we mark it as inaccessible and set the result to
762 * success to give it a try later */
763
764 /* fetch the current error info */
765 mData->mAccessError = com::ErrorInfo();
766 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
767
768 /* rollback all changes */
769 i_rollback(false /* aNotify */);
770
771 // uninit media from this machine's media registry, or else
772 // reloading the settings will fail
773 mParent->i_unregisterMachineMedia(i_getId());
774
775 /* uninitialize the common part to make sure all data is reset to
776 * default (null) values */
777 uninitDataAndChildObjects();
778
779 rc = S_OK;
780 }
781
782 return rc;
783}
784
785/**
786 * Uninitializes the instance.
787 * Called either from FinalRelease() or by the parent when it gets destroyed.
788 *
789 * @note The caller of this method must make sure that this object
790 * a) doesn't have active callers on the current thread and b) is not locked
791 * by the current thread; otherwise uninit() will hang either a) due to
792 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
793 * a dead-lock caused by this thread waiting for all callers on the other
794 * threads are done but preventing them from doing so by holding a lock.
795 */
796void Machine::uninit()
797{
798 LogFlowThisFuncEnter();
799
800 Assert(!isWriteLockOnCurrentThread());
801
802 Assert(!uRegistryNeedsSaving);
803 if (uRegistryNeedsSaving)
804 {
805 AutoCaller autoCaller(this);
806 if (SUCCEEDED(autoCaller.rc()))
807 {
808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
809 i_saveSettings(NULL, Machine::SaveS_Force);
810 }
811 }
812
813 /* Enclose the state transition Ready->InUninit->NotReady */
814 AutoUninitSpan autoUninitSpan(this);
815 if (autoUninitSpan.uninitDone())
816 return;
817
818 Assert(!i_isSnapshotMachine());
819 Assert(!i_isSessionMachine());
820 Assert(!!mData);
821
822 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
823 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
824
825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
826
827 if (!mData->mSession.mMachine.isNull())
828 {
829 /* Theoretically, this can only happen if the VirtualBox server has been
830 * terminated while there were clients running that owned open direct
831 * sessions. Since in this case we are definitely called by
832 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
833 * won't happen on the client watcher thread (because it has a
834 * VirtualBox caller for the duration of the
835 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
836 * cannot happen until the VirtualBox caller is released). This is
837 * important, because SessionMachine::uninit() cannot correctly operate
838 * after we return from this method (it expects the Machine instance is
839 * still valid). We'll call it ourselves below.
840 */
841 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
842 (SessionMachine*)mData->mSession.mMachine));
843
844 if (Global::IsOnlineOrTransient(mData->mMachineState))
845 {
846 Log1WarningThisFunc(("Setting state to Aborted!\n"));
847 /* set machine state using SessionMachine reimplementation */
848 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
849 }
850
851 /*
852 * Uninitialize SessionMachine using public uninit() to indicate
853 * an unexpected uninitialization.
854 */
855 mData->mSession.mMachine->uninit();
856 /* SessionMachine::uninit() must set mSession.mMachine to null */
857 Assert(mData->mSession.mMachine.isNull());
858 }
859
860 // uninit media from this machine's media registry, if they're still there
861 Guid uuidMachine(i_getId());
862
863 /* the lock is no more necessary (SessionMachine is uninitialized) */
864 alock.release();
865
866 /* XXX This will fail with
867 * "cannot be closed because it is still attached to 1 virtual machines"
868 * because at this point we did not call uninitDataAndChildObjects() yet
869 * and therefore also removeBackReference() for all these mediums was not called! */
870
871 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
872 mParent->i_unregisterMachineMedia(uuidMachine);
873
874 // has machine been modified?
875 if (mData->flModifications)
876 {
877 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
878 i_rollback(false /* aNotify */);
879 }
880
881 if (mData->mAccessible)
882 uninitDataAndChildObjects();
883
884 /* free the essential data structure last */
885 mData.free();
886
887 LogFlowThisFuncLeave();
888}
889
890// Wrapped IMachine properties
891/////////////////////////////////////////////////////////////////////////////
892HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
893{
894 /* mParent is constant during life time, no need to lock */
895 ComObjPtr<VirtualBox> pVirtualBox(mParent);
896 aParent = pVirtualBox;
897
898 return S_OK;
899}
900
901
902HRESULT Machine::getAccessible(BOOL *aAccessible)
903{
904 /* In some cases (medium registry related), it is necessary to be able to
905 * go through the list of all machines. Happens when an inaccessible VM
906 * has a sensible medium registry. */
907 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
908 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
909
910 HRESULT rc = S_OK;
911
912 if (!mData->mAccessible)
913 {
914 /* try to initialize the VM once more if not accessible */
915
916 AutoReinitSpan autoReinitSpan(this);
917 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
918
919#ifdef DEBUG
920 LogFlowThisFunc(("Dumping media backreferences\n"));
921 mParent->i_dumpAllBackRefs();
922#endif
923
924 if (mData->pMachineConfigFile)
925 {
926 // reset the XML file to force loadSettings() (called from i_registeredInit())
927 // to parse it again; the file might have changed
928 delete mData->pMachineConfigFile;
929 mData->pMachineConfigFile = NULL;
930 }
931
932 rc = i_registeredInit();
933
934 if (SUCCEEDED(rc) && mData->mAccessible)
935 {
936 autoReinitSpan.setSucceeded();
937
938 /* make sure interesting parties will notice the accessibility
939 * state change */
940 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
941 mParent->i_onMachineDataChange(mData->mUuid);
942 }
943 }
944
945 if (SUCCEEDED(rc))
946 *aAccessible = mData->mAccessible;
947
948 LogFlowThisFuncLeave();
949
950 return rc;
951}
952
953HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
954{
955 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
956
957 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
958 {
959 /* return shortly */
960 aAccessError = NULL;
961 return S_OK;
962 }
963
964 HRESULT rc = S_OK;
965
966 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
967 rc = errorInfo.createObject();
968 if (SUCCEEDED(rc))
969 {
970 errorInfo->init(mData->mAccessError.getResultCode(),
971 mData->mAccessError.getInterfaceID().ref(),
972 Utf8Str(mData->mAccessError.getComponent()).c_str(),
973 Utf8Str(mData->mAccessError.getText()));
974 aAccessError = errorInfo;
975 }
976
977 return rc;
978}
979
980HRESULT Machine::getName(com::Utf8Str &aName)
981{
982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
983
984 aName = mUserData->s.strName;
985
986 return S_OK;
987}
988
989HRESULT Machine::setName(const com::Utf8Str &aName)
990{
991 // prohibit setting a UUID only as the machine name, or else it can
992 // never be found by findMachine()
993 Guid test(aName);
994
995 if (test.isValid())
996 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
997
998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 HRESULT rc = i_checkStateDependency(MutableStateDep);
1001 if (FAILED(rc)) return rc;
1002
1003 i_setModified(IsModified_MachineData);
1004 mUserData.backup();
1005 mUserData->s.strName = aName;
1006
1007 return S_OK;
1008}
1009
1010HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1011{
1012 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 aDescription = mUserData->s.strDescription;
1015
1016 return S_OK;
1017}
1018
1019HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1020{
1021 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1022
1023 // this can be done in principle in any state as it doesn't affect the VM
1024 // significantly, but play safe by not messing around while complex
1025 // activities are going on
1026 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1027 if (FAILED(rc)) return rc;
1028
1029 i_setModified(IsModified_MachineData);
1030 mUserData.backup();
1031 mUserData->s.strDescription = aDescription;
1032
1033 return S_OK;
1034}
1035
1036HRESULT Machine::getId(com::Guid &aId)
1037{
1038 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1039
1040 aId = mData->mUuid;
1041
1042 return S_OK;
1043}
1044
1045HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1046{
1047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1048 aGroups.resize(mUserData->s.llGroups.size());
1049 size_t i = 0;
1050 for (StringsList::const_iterator
1051 it = mUserData->s.llGroups.begin();
1052 it != mUserData->s.llGroups.end();
1053 ++it, ++i)
1054 aGroups[i] = (*it);
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1060{
1061 StringsList llGroups;
1062 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1063 if (FAILED(rc))
1064 return rc;
1065
1066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1067
1068 rc = i_checkStateDependency(MutableOrSavedStateDep);
1069 if (FAILED(rc)) return rc;
1070
1071 i_setModified(IsModified_MachineData);
1072 mUserData.backup();
1073 mUserData->s.llGroups = llGroups;
1074
1075 return S_OK;
1076}
1077
1078HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1079{
1080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 aOSTypeId = mUserData->s.strOsType;
1083
1084 return S_OK;
1085}
1086
1087HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1088{
1089 /* look up the object by Id to check it is valid */
1090 ComObjPtr<GuestOSType> pGuestOSType;
1091 HRESULT rc = mParent->i_findGuestOSType(aOSTypeId,
1092 pGuestOSType);
1093 if (FAILED(rc)) return rc;
1094
1095 /* when setting, always use the "etalon" value for consistency -- lookup
1096 * by ID is case-insensitive and the input value may have different case */
1097 Utf8Str osTypeId = pGuestOSType->i_id();
1098
1099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1100
1101 rc = i_checkStateDependency(MutableStateDep);
1102 if (FAILED(rc)) return rc;
1103
1104 i_setModified(IsModified_MachineData);
1105 mUserData.backup();
1106 mUserData->s.strOsType = osTypeId;
1107
1108 return S_OK;
1109}
1110
1111HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1112{
1113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 *aFirmwareType = mHWData->mFirmwareType;
1116
1117 return S_OK;
1118}
1119
1120HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1121{
1122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1123
1124 HRESULT rc = i_checkStateDependency(MutableStateDep);
1125 if (FAILED(rc)) return rc;
1126
1127 i_setModified(IsModified_MachineData);
1128 mHWData.backup();
1129 mHWData->mFirmwareType = aFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1135{
1136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1139
1140 return S_OK;
1141}
1142
1143HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1144{
1145 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1146
1147 HRESULT rc = i_checkStateDependency(MutableStateDep);
1148 if (FAILED(rc)) return rc;
1149
1150 i_setModified(IsModified_MachineData);
1151 mHWData.backup();
1152 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1153
1154 return S_OK;
1155}
1156
1157HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1158{
1159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1160
1161 *aPointingHIDType = mHWData->mPointingHIDType;
1162
1163 return S_OK;
1164}
1165
1166HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1167{
1168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1169
1170 HRESULT rc = i_checkStateDependency(MutableStateDep);
1171 if (FAILED(rc)) return rc;
1172
1173 i_setModified(IsModified_MachineData);
1174 mHWData.backup();
1175 mHWData->mPointingHIDType = aPointingHIDType;
1176
1177 return S_OK;
1178}
1179
1180HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1181{
1182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1183
1184 *aChipsetType = mHWData->mChipsetType;
1185
1186 return S_OK;
1187}
1188
1189HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1190{
1191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1192
1193 HRESULT rc = i_checkStateDependency(MutableStateDep);
1194 if (FAILED(rc)) return rc;
1195
1196 if (aChipsetType != mHWData->mChipsetType)
1197 {
1198 i_setModified(IsModified_MachineData);
1199 mHWData.backup();
1200 mHWData->mChipsetType = aChipsetType;
1201
1202 // Resize network adapter array, to be finalized on commit/rollback.
1203 // We must not throw away entries yet, otherwise settings are lost
1204 // without a way to roll back.
1205 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1206 size_t oldCount = mNetworkAdapters.size();
1207 if (newCount > oldCount)
1208 {
1209 mNetworkAdapters.resize(newCount);
1210 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1211 {
1212 unconst(mNetworkAdapters[slot]).createObject();
1213 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1214 }
1215 }
1216 }
1217
1218 return S_OK;
1219}
1220
1221HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1222{
1223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1224
1225 aParavirtDebug = mHWData->mParavirtDebug;
1226 return S_OK;
1227}
1228
1229HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1230{
1231 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 HRESULT rc = i_checkStateDependency(MutableStateDep);
1234 if (FAILED(rc)) return rc;
1235
1236 /** @todo Parse/validate options? */
1237 if (aParavirtDebug != mHWData->mParavirtDebug)
1238 {
1239 i_setModified(IsModified_MachineData);
1240 mHWData.backup();
1241 mHWData->mParavirtDebug = aParavirtDebug;
1242 }
1243
1244 return S_OK;
1245}
1246
1247HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1248{
1249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1250
1251 *aParavirtProvider = mHWData->mParavirtProvider;
1252
1253 return S_OK;
1254}
1255
1256HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1257{
1258 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1259
1260 HRESULT rc = i_checkStateDependency(MutableStateDep);
1261 if (FAILED(rc)) return rc;
1262
1263 if (aParavirtProvider != mHWData->mParavirtProvider)
1264 {
1265 i_setModified(IsModified_MachineData);
1266 mHWData.backup();
1267 mHWData->mParavirtProvider = aParavirtProvider;
1268 }
1269
1270 return S_OK;
1271}
1272
1273HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1274{
1275 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 *aParavirtProvider = mHWData->mParavirtProvider;
1278 switch (mHWData->mParavirtProvider)
1279 {
1280 case ParavirtProvider_None:
1281 case ParavirtProvider_HyperV:
1282 case ParavirtProvider_KVM:
1283 case ParavirtProvider_Minimal:
1284 break;
1285
1286 /* Resolve dynamic provider types to the effective types. */
1287 default:
1288 {
1289 ComObjPtr<GuestOSType> pGuestOSType;
1290 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1291 pGuestOSType);
1292 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1293
1294 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1295 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1296
1297 switch (mHWData->mParavirtProvider)
1298 {
1299 case ParavirtProvider_Legacy:
1300 {
1301 if (fOsXGuest)
1302 *aParavirtProvider = ParavirtProvider_Minimal;
1303 else
1304 *aParavirtProvider = ParavirtProvider_None;
1305 break;
1306 }
1307
1308 case ParavirtProvider_Default:
1309 {
1310 if (fOsXGuest)
1311 *aParavirtProvider = ParavirtProvider_Minimal;
1312 else if ( mUserData->s.strOsType == "Windows10"
1313 || mUserData->s.strOsType == "Windows10_64"
1314 || mUserData->s.strOsType == "Windows81"
1315 || mUserData->s.strOsType == "Windows81_64"
1316 || mUserData->s.strOsType == "Windows8"
1317 || mUserData->s.strOsType == "Windows8_64"
1318 || mUserData->s.strOsType == "Windows7"
1319 || mUserData->s.strOsType == "Windows7_64"
1320 || mUserData->s.strOsType == "WindowsVista"
1321 || mUserData->s.strOsType == "WindowsVista_64"
1322 || mUserData->s.strOsType == "Windows2012"
1323 || mUserData->s.strOsType == "Windows2012_64"
1324 || mUserData->s.strOsType == "Windows2008"
1325 || mUserData->s.strOsType == "Windows2008_64")
1326 {
1327 *aParavirtProvider = ParavirtProvider_HyperV;
1328 }
1329 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1330 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1331 || mUserData->s.strOsType == "Linux"
1332 || mUserData->s.strOsType == "Linux_64"
1333 || mUserData->s.strOsType == "ArchLinux"
1334 || mUserData->s.strOsType == "ArchLinux_64"
1335 || mUserData->s.strOsType == "Debian"
1336 || mUserData->s.strOsType == "Debian_64"
1337 || mUserData->s.strOsType == "Fedora"
1338 || mUserData->s.strOsType == "Fedora_64"
1339 || mUserData->s.strOsType == "Gentoo"
1340 || mUserData->s.strOsType == "Gentoo_64"
1341 || mUserData->s.strOsType == "Mandriva"
1342 || mUserData->s.strOsType == "Mandriva_64"
1343 || mUserData->s.strOsType == "OpenSUSE"
1344 || mUserData->s.strOsType == "OpenSUSE_64"
1345 || mUserData->s.strOsType == "Oracle"
1346 || mUserData->s.strOsType == "Oracle_64"
1347 || mUserData->s.strOsType == "RedHat"
1348 || mUserData->s.strOsType == "RedHat_64"
1349 || mUserData->s.strOsType == "Turbolinux"
1350 || mUserData->s.strOsType == "Turbolinux_64"
1351 || mUserData->s.strOsType == "Ubuntu"
1352 || mUserData->s.strOsType == "Ubuntu_64"
1353 || mUserData->s.strOsType == "Xandros"
1354 || mUserData->s.strOsType == "Xandros_64")
1355 {
1356 *aParavirtProvider = ParavirtProvider_KVM;
1357 }
1358 else
1359 *aParavirtProvider = ParavirtProvider_None;
1360 break;
1361 }
1362
1363 default: AssertFailedBreak(); /* Shut up MSC. */
1364 }
1365 break;
1366 }
1367 }
1368
1369 Assert( *aParavirtProvider == ParavirtProvider_None
1370 || *aParavirtProvider == ParavirtProvider_Minimal
1371 || *aParavirtProvider == ParavirtProvider_HyperV
1372 || *aParavirtProvider == ParavirtProvider_KVM);
1373 return S_OK;
1374}
1375
1376HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1377{
1378 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1379
1380 aHardwareVersion = mHWData->mHWVersion;
1381
1382 return S_OK;
1383}
1384
1385HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1386{
1387 /* check known version */
1388 Utf8Str hwVersion = aHardwareVersion;
1389 if ( hwVersion.compare("1") != 0
1390 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1391 return setError(E_INVALIDARG,
1392 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1393
1394 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1395
1396 HRESULT rc = i_checkStateDependency(MutableStateDep);
1397 if (FAILED(rc)) return rc;
1398
1399 i_setModified(IsModified_MachineData);
1400 mHWData.backup();
1401 mHWData->mHWVersion = aHardwareVersion;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 if (!mHWData->mHardwareUUID.isZero())
1411 aHardwareUUID = mHWData->mHardwareUUID;
1412 else
1413 aHardwareUUID = mData->mUuid;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1419{
1420 if (!aHardwareUUID.isValid())
1421 return E_INVALIDARG;
1422
1423 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 HRESULT rc = i_checkStateDependency(MutableStateDep);
1426 if (FAILED(rc)) return rc;
1427
1428 i_setModified(IsModified_MachineData);
1429 mHWData.backup();
1430 if (aHardwareUUID == mData->mUuid)
1431 mHWData->mHardwareUUID.clear();
1432 else
1433 mHWData->mHardwareUUID = aHardwareUUID;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1439{
1440 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1441
1442 *aMemorySize = mHWData->mMemorySize;
1443
1444 return S_OK;
1445}
1446
1447HRESULT Machine::setMemorySize(ULONG aMemorySize)
1448{
1449 /* check RAM limits */
1450 if ( aMemorySize < MM_RAM_MIN_IN_MB
1451 || aMemorySize > MM_RAM_MAX_IN_MB
1452 )
1453 return setError(E_INVALIDARG,
1454 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1455 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1456
1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1458
1459 HRESULT rc = i_checkStateDependency(MutableStateDep);
1460 if (FAILED(rc)) return rc;
1461
1462 i_setModified(IsModified_MachineData);
1463 mHWData.backup();
1464 mHWData->mMemorySize = aMemorySize;
1465
1466 return S_OK;
1467}
1468
1469HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1470{
1471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 *aCPUCount = mHWData->mCPUCount;
1474
1475 return S_OK;
1476}
1477
1478HRESULT Machine::setCPUCount(ULONG aCPUCount)
1479{
1480 /* check CPU limits */
1481 if ( aCPUCount < SchemaDefs::MinCPUCount
1482 || aCPUCount > SchemaDefs::MaxCPUCount
1483 )
1484 return setError(E_INVALIDARG,
1485 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1486 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1487
1488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1491 if (mHWData->mCPUHotPlugEnabled)
1492 {
1493 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1494 {
1495 if (mHWData->mCPUAttached[idx])
1496 return setError(E_INVALIDARG,
1497 tr("There is still a CPU attached to socket %lu."
1498 "Detach the CPU before removing the socket"),
1499 aCPUCount, idx+1);
1500 }
1501 }
1502
1503 HRESULT rc = i_checkStateDependency(MutableStateDep);
1504 if (FAILED(rc)) return rc;
1505
1506 i_setModified(IsModified_MachineData);
1507 mHWData.backup();
1508 mHWData->mCPUCount = aCPUCount;
1509
1510 return S_OK;
1511}
1512
1513HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1514{
1515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1516
1517 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1518
1519 return S_OK;
1520}
1521
1522HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1523{
1524 HRESULT rc = S_OK;
1525
1526 /* check throttle limits */
1527 if ( aCPUExecutionCap < 1
1528 || aCPUExecutionCap > 100
1529 )
1530 return setError(E_INVALIDARG,
1531 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1532 aCPUExecutionCap, 1, 100);
1533
1534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1535
1536 alock.release();
1537 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1538 alock.acquire();
1539 if (FAILED(rc)) return rc;
1540
1541 i_setModified(IsModified_MachineData);
1542 mHWData.backup();
1543 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1544
1545 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1546 if (Global::IsOnline(mData->mMachineState))
1547 i_saveSettings(NULL);
1548
1549 return S_OK;
1550}
1551
1552HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1553{
1554 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1562{
1563 HRESULT rc = S_OK;
1564
1565 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1566
1567 rc = i_checkStateDependency(MutableStateDep);
1568 if (FAILED(rc)) return rc;
1569
1570 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1571 {
1572 if (aCPUHotPlugEnabled)
1573 {
1574 i_setModified(IsModified_MachineData);
1575 mHWData.backup();
1576
1577 /* Add the amount of CPUs currently attached */
1578 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1579 mHWData->mCPUAttached[i] = true;
1580 }
1581 else
1582 {
1583 /*
1584 * We can disable hotplug only if the amount of maximum CPUs is equal
1585 * to the amount of attached CPUs
1586 */
1587 unsigned cCpusAttached = 0;
1588 unsigned iHighestId = 0;
1589
1590 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1591 {
1592 if (mHWData->mCPUAttached[i])
1593 {
1594 cCpusAttached++;
1595 iHighestId = i;
1596 }
1597 }
1598
1599 if ( (cCpusAttached != mHWData->mCPUCount)
1600 || (iHighestId >= mHWData->mCPUCount))
1601 return setError(E_INVALIDARG,
1602 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1603
1604 i_setModified(IsModified_MachineData);
1605 mHWData.backup();
1606 }
1607 }
1608
1609 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1610
1611 return rc;
1612}
1613
1614HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1615{
1616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1617
1618 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1619
1620 return S_OK;
1621}
1622
1623HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1624{
1625 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1626
1627 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1628 if (SUCCEEDED(hrc))
1629 {
1630 i_setModified(IsModified_MachineData);
1631 mHWData.backup();
1632 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1633 }
1634 return hrc;
1635}
1636
1637HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1638{
1639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1640 aCPUProfile = mHWData->mCpuProfile;
1641 return S_OK;
1642}
1643
1644HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1645{
1646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 /* Empty equals 'host'. */
1653 if (aCPUProfile.isNotEmpty())
1654 mHWData->mCpuProfile = aCPUProfile;
1655 else
1656 mHWData->mCpuProfile = "host";
1657 }
1658 return hrc;
1659}
1660
1661HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1662{
1663#ifdef VBOX_WITH_USB_CARDREADER
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1667
1668 return S_OK;
1669#else
1670 NOREF(aEmulatedUSBCardReaderEnabled);
1671 return E_NOTIMPL;
1672#endif
1673}
1674
1675HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1676{
1677#ifdef VBOX_WITH_USB_CARDREADER
1678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1679
1680 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1681 if (FAILED(rc)) return rc;
1682
1683 i_setModified(IsModified_MachineData);
1684 mHWData.backup();
1685 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1686
1687 return S_OK;
1688#else
1689 NOREF(aEmulatedUSBCardReaderEnabled);
1690 return E_NOTIMPL;
1691#endif
1692}
1693
1694HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1695{
1696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1697
1698 *aHPETEnabled = mHWData->mHPETEnabled;
1699
1700 return S_OK;
1701}
1702
1703HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1704{
1705 HRESULT rc = S_OK;
1706
1707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1708
1709 rc = i_checkStateDependency(MutableStateDep);
1710 if (FAILED(rc)) return rc;
1711
1712 i_setModified(IsModified_MachineData);
1713 mHWData.backup();
1714
1715 mHWData->mHPETEnabled = aHPETEnabled;
1716
1717 return rc;
1718}
1719
1720HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1721{
1722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1725 return S_OK;
1726}
1727
1728HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1729{
1730 HRESULT rc = S_OK;
1731
1732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1733
1734 i_setModified(IsModified_MachineData);
1735 mHWData.backup();
1736 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1737
1738 alock.release();
1739 rc = i_onVideoCaptureChange();
1740 alock.acquire();
1741 if (FAILED(rc))
1742 {
1743 /*
1744 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1745 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1746 * determine if it should start or stop capturing. Therefore we need to manually
1747 * undo change.
1748 */
1749 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1750 return rc;
1751 }
1752
1753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1754 if (Global::IsOnline(mData->mMachineState))
1755 i_saveSettings(NULL);
1756
1757 return rc;
1758}
1759
1760HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1761{
1762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1763 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1764 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1765 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1766 return S_OK;
1767}
1768
1769HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1770{
1771 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1772 bool fChanged = false;
1773
1774 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1775
1776 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1777 {
1778 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1779 {
1780 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1781 fChanged = true;
1782 }
1783 }
1784 if (fChanged)
1785 {
1786 alock.release();
1787 HRESULT rc = i_onVideoCaptureChange();
1788 alock.acquire();
1789 if (FAILED(rc)) return rc;
1790 i_setModified(IsModified_MachineData);
1791
1792 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1793 if (Global::IsOnline(mData->mMachineState))
1794 i_saveSettings(NULL);
1795 }
1796
1797 return S_OK;
1798}
1799
1800HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1801{
1802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1803 if (mHWData->mVideoCaptureFile.isEmpty())
1804 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1805 else
1806 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1811{
1812 Utf8Str strFile(aVideoCaptureFile);
1813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 if ( Global::IsOnline(mData->mMachineState)
1816 && mHWData->mVideoCaptureEnabled)
1817 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1818
1819 if (!RTPathStartsWithRoot(strFile.c_str()))
1820 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1821
1822 if (!strFile.isEmpty())
1823 {
1824 Utf8Str defaultFile;
1825 i_getDefaultVideoCaptureFile(defaultFile);
1826 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1827 strFile.setNull();
1828 }
1829
1830 i_setModified(IsModified_MachineData);
1831 mHWData.backup();
1832 mHWData->mVideoCaptureFile = strFile;
1833
1834 return S_OK;
1835}
1836
1837HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1838{
1839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1840 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1845{
1846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1847
1848 if ( Global::IsOnline(mData->mMachineState)
1849 && mHWData->mVideoCaptureEnabled)
1850 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1851
1852 i_setModified(IsModified_MachineData);
1853 mHWData.backup();
1854 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1855
1856 return S_OK;
1857}
1858
1859HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1860{
1861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1862 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1863 return S_OK;
1864}
1865
1866HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1867{
1868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1869
1870 if ( Global::IsOnline(mData->mMachineState)
1871 && mHWData->mVideoCaptureEnabled)
1872 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1873
1874 i_setModified(IsModified_MachineData);
1875 mHWData.backup();
1876 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1877
1878 return S_OK;
1879}
1880
1881HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1882{
1883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1884 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1885 return S_OK;
1886}
1887
1888HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1889{
1890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1891
1892 if ( Global::IsOnline(mData->mMachineState)
1893 && mHWData->mVideoCaptureEnabled)
1894 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1895
1896 i_setModified(IsModified_MachineData);
1897 mHWData.backup();
1898 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1904{
1905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1906 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1907 return S_OK;
1908}
1909
1910HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1911{
1912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 if ( Global::IsOnline(mData->mMachineState)
1915 && mHWData->mVideoCaptureEnabled)
1916 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1917
1918 i_setModified(IsModified_MachineData);
1919 mHWData.backup();
1920 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1921
1922 return S_OK;
1923}
1924
1925HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1926{
1927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1928 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1929 return S_OK;
1930}
1931
1932HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1933{
1934 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1935
1936 if ( Global::IsOnline(mData->mMachineState)
1937 && mHWData->mVideoCaptureEnabled)
1938 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1948{
1949 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1950 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1951 return S_OK;
1952}
1953
1954HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1955{
1956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1957
1958 if ( Global::IsOnline(mData->mMachineState)
1959 && mHWData->mVideoCaptureEnabled)
1960 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1961
1962 i_setModified(IsModified_MachineData);
1963 mHWData.backup();
1964 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1965
1966 return S_OK;
1967}
1968
1969HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1970{
1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1972
1973 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1974 return S_OK;
1975}
1976
1977HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1978{
1979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1980
1981 if ( Global::IsOnline(mData->mMachineState)
1982 && mHWData->mVideoCaptureEnabled)
1983 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1984
1985 i_setModified(IsModified_MachineData);
1986 mHWData.backup();
1987 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1988
1989 return S_OK;
1990}
1991
1992HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1993{
1994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1995
1996 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1997
1998 return S_OK;
1999}
2000
2001HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2002{
2003 switch (aGraphicsControllerType)
2004 {
2005 case GraphicsControllerType_Null:
2006 case GraphicsControllerType_VBoxVGA:
2007#ifdef VBOX_WITH_VMSVGA
2008 case GraphicsControllerType_VMSVGA:
2009#endif
2010 break;
2011 default:
2012 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2013 }
2014
2015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2016
2017 HRESULT rc = i_checkStateDependency(MutableStateDep);
2018 if (FAILED(rc)) return rc;
2019
2020 i_setModified(IsModified_MachineData);
2021 mHWData.backup();
2022 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2023
2024 return S_OK;
2025}
2026
2027HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2028{
2029 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2030
2031 *aVRAMSize = mHWData->mVRAMSize;
2032
2033 return S_OK;
2034}
2035
2036HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2037{
2038 /* check VRAM limits */
2039 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2040 return setError(E_INVALIDARG,
2041 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2042 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2043
2044 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2045
2046 HRESULT rc = i_checkStateDependency(MutableStateDep);
2047 if (FAILED(rc)) return rc;
2048
2049 i_setModified(IsModified_MachineData);
2050 mHWData.backup();
2051 mHWData->mVRAMSize = aVRAMSize;
2052
2053 return S_OK;
2054}
2055
2056/** @todo this method should not be public */
2057HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060
2061 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2062
2063 return S_OK;
2064}
2065
2066/**
2067 * Set the memory balloon size.
2068 *
2069 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2070 * we have to make sure that we never call IGuest from here.
2071 */
2072HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2073{
2074 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2075#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2076 /* check limits */
2077 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2078 return setError(E_INVALIDARG,
2079 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2080 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2081
2082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2083
2084 i_setModified(IsModified_MachineData);
2085 mHWData.backup();
2086 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2087
2088 return S_OK;
2089#else
2090 NOREF(aMemoryBalloonSize);
2091 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2092#endif
2093}
2094
2095HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2096{
2097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2098
2099 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2100 return S_OK;
2101}
2102
2103HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2104{
2105#ifdef VBOX_WITH_PAGE_SHARING
2106 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2107
2108 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2109 i_setModified(IsModified_MachineData);
2110 mHWData.backup();
2111 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2112 return S_OK;
2113#else
2114 NOREF(aPageFusionEnabled);
2115 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2116#endif
2117}
2118
2119HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2120{
2121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2122
2123 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2124
2125 return S_OK;
2126}
2127
2128HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2129{
2130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2131
2132 HRESULT rc = i_checkStateDependency(MutableStateDep);
2133 if (FAILED(rc)) return rc;
2134
2135 /** @todo check validity! */
2136
2137 i_setModified(IsModified_MachineData);
2138 mHWData.backup();
2139 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2140
2141 return S_OK;
2142}
2143
2144
2145HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2146{
2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2148
2149 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2150
2151 return S_OK;
2152}
2153
2154HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2155{
2156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2157
2158 HRESULT rc = i_checkStateDependency(MutableStateDep);
2159 if (FAILED(rc)) return rc;
2160
2161 /** @todo check validity! */
2162 i_setModified(IsModified_MachineData);
2163 mHWData.backup();
2164 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2165
2166 return S_OK;
2167}
2168
2169HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2170{
2171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2172
2173 *aMonitorCount = mHWData->mMonitorCount;
2174
2175 return S_OK;
2176}
2177
2178HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2179{
2180 /* make sure monitor count is a sensible number */
2181 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2182 return setError(E_INVALIDARG,
2183 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2184 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2185
2186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2187
2188 HRESULT rc = i_checkStateDependency(MutableStateDep);
2189 if (FAILED(rc)) return rc;
2190
2191 i_setModified(IsModified_MachineData);
2192 mHWData.backup();
2193 mHWData->mMonitorCount = aMonitorCount;
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2199{
2200 /* mBIOSSettings is constant during life time, no need to lock */
2201 aBIOSSettings = mBIOSSettings;
2202
2203 return S_OK;
2204}
2205
2206HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2207{
2208 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2209
2210 switch (aProperty)
2211 {
2212 case CPUPropertyType_PAE:
2213 *aValue = mHWData->mPAEEnabled;
2214 break;
2215
2216 case CPUPropertyType_LongMode:
2217 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2218 *aValue = TRUE;
2219 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2220 *aValue = FALSE;
2221#if HC_ARCH_BITS == 64
2222 else
2223 *aValue = TRUE;
2224#else
2225 else
2226 {
2227 *aValue = FALSE;
2228
2229 ComObjPtr<GuestOSType> pGuestOSType;
2230 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2231 pGuestOSType);
2232 if (SUCCEEDED(hrc2))
2233 {
2234 if (pGuestOSType->i_is64Bit())
2235 {
2236 ComObjPtr<Host> pHost = mParent->i_host();
2237 alock.release();
2238
2239 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2240 if (FAILED(hrc2))
2241 *aValue = FALSE;
2242 }
2243 }
2244 }
2245#endif
2246 break;
2247
2248 case CPUPropertyType_TripleFaultReset:
2249 *aValue = mHWData->mTripleFaultReset;
2250 break;
2251
2252 case CPUPropertyType_APIC:
2253 *aValue = mHWData->mAPIC;
2254 break;
2255
2256 case CPUPropertyType_X2APIC:
2257 *aValue = mHWData->mX2APIC;
2258 break;
2259
2260 case CPUPropertyType_IBPBOnVMExit:
2261 *aValue = mHWData->mIBPBOnVMExit;
2262 break;
2263
2264 case CPUPropertyType_IBPBOnVMEntry:
2265 *aValue = mHWData->mIBPBOnVMEntry;
2266 break;
2267
2268 default:
2269 return E_INVALIDARG;
2270 }
2271 return S_OK;
2272}
2273
2274HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2275{
2276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2277
2278 HRESULT rc = i_checkStateDependency(MutableStateDep);
2279 if (FAILED(rc)) return rc;
2280
2281 switch (aProperty)
2282 {
2283 case CPUPropertyType_PAE:
2284 i_setModified(IsModified_MachineData);
2285 mHWData.backup();
2286 mHWData->mPAEEnabled = !!aValue;
2287 break;
2288
2289 case CPUPropertyType_LongMode:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2293 break;
2294
2295 case CPUPropertyType_TripleFaultReset:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mTripleFaultReset = !!aValue;
2299 break;
2300
2301 case CPUPropertyType_APIC:
2302 if (mHWData->mX2APIC)
2303 aValue = TRUE;
2304 i_setModified(IsModified_MachineData);
2305 mHWData.backup();
2306 mHWData->mAPIC = !!aValue;
2307 break;
2308
2309 case CPUPropertyType_X2APIC:
2310 i_setModified(IsModified_MachineData);
2311 mHWData.backup();
2312 mHWData->mX2APIC = !!aValue;
2313 if (aValue)
2314 mHWData->mAPIC = !!aValue;
2315 break;
2316
2317 case CPUPropertyType_IBPBOnVMExit:
2318 i_setModified(IsModified_MachineData);
2319 mHWData.backup();
2320 mHWData->mIBPBOnVMExit = !!aValue;
2321 break;
2322
2323 case CPUPropertyType_IBPBOnVMEntry:
2324 i_setModified(IsModified_MachineData);
2325 mHWData.backup();
2326 mHWData->mIBPBOnVMEntry = !!aValue;
2327 break;
2328
2329 default:
2330 return E_INVALIDARG;
2331 }
2332 return S_OK;
2333}
2334
2335HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2336 ULONG *aValEcx, ULONG *aValEdx)
2337{
2338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2339 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2340 {
2341 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2342 it != mHWData->mCpuIdLeafList.end();
2343 ++it)
2344 {
2345 if (aOrdinal == 0)
2346 {
2347 const settings::CpuIdLeaf &rLeaf= *it;
2348 *aIdx = rLeaf.idx;
2349 *aSubIdx = rLeaf.idxSub;
2350 *aValEax = rLeaf.uEax;
2351 *aValEbx = rLeaf.uEbx;
2352 *aValEcx = rLeaf.uEcx;
2353 *aValEdx = rLeaf.uEdx;
2354 return S_OK;
2355 }
2356 aOrdinal--;
2357 }
2358 }
2359 return E_INVALIDARG;
2360}
2361
2362HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2363{
2364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2365
2366 /*
2367 * Search the list.
2368 */
2369 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2370 {
2371 const settings::CpuIdLeaf &rLeaf= *it;
2372 if ( rLeaf.idx == aIdx
2373 && ( aSubIdx == UINT32_MAX
2374 || rLeaf.idxSub == aSubIdx) )
2375 {
2376 *aValEax = rLeaf.uEax;
2377 *aValEbx = rLeaf.uEbx;
2378 *aValEcx = rLeaf.uEcx;
2379 *aValEdx = rLeaf.uEdx;
2380 return S_OK;
2381 }
2382 }
2383
2384 return E_INVALIDARG;
2385}
2386
2387
2388HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2389{
2390 /*
2391 * Validate input before taking locks and checking state.
2392 */
2393 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2394 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2395 if ( aIdx >= UINT32_C(0x20)
2396 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2397 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2398 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2399
2400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2401 HRESULT rc = i_checkStateDependency(MutableStateDep);
2402 if (FAILED(rc)) return rc;
2403
2404 /*
2405 * Impose a maximum number of leaves.
2406 */
2407 if (mHWData->mCpuIdLeafList.size() > 256)
2408 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2409
2410 /*
2411 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2412 */
2413 i_setModified(IsModified_MachineData);
2414 mHWData.backup();
2415
2416 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2417 {
2418 settings::CpuIdLeaf &rLeaf= *it;
2419 if ( rLeaf.idx == aIdx
2420 && ( aSubIdx == UINT32_MAX
2421 || rLeaf.idxSub == aSubIdx) )
2422 it = mHWData->mCpuIdLeafList.erase(it);
2423 else
2424 ++it;
2425 }
2426
2427 settings::CpuIdLeaf NewLeaf;
2428 NewLeaf.idx = aIdx;
2429 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2430 NewLeaf.uEax = aValEax;
2431 NewLeaf.uEbx = aValEbx;
2432 NewLeaf.uEcx = aValEcx;
2433 NewLeaf.uEdx = aValEdx;
2434 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2435 return S_OK;
2436}
2437
2438HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2439{
2440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2441
2442 HRESULT rc = i_checkStateDependency(MutableStateDep);
2443 if (FAILED(rc)) return rc;
2444
2445 /*
2446 * Do the removal.
2447 */
2448 bool fModified = false;
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 {
2456 if (!fModified)
2457 {
2458 fModified = true;
2459 i_setModified(IsModified_MachineData);
2460 mHWData.backup();
2461 }
2462 it = mHWData->mCpuIdLeafList.erase(it);
2463 }
2464 else
2465 ++it;
2466 }
2467
2468 return S_OK;
2469}
2470
2471HRESULT Machine::removeAllCPUIDLeaves()
2472{
2473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2474
2475 HRESULT rc = i_checkStateDependency(MutableStateDep);
2476 if (FAILED(rc)) return rc;
2477
2478 if (mHWData->mCpuIdLeafList.size() > 0)
2479 {
2480 i_setModified(IsModified_MachineData);
2481 mHWData.backup();
2482
2483 mHWData->mCpuIdLeafList.clear();
2484 }
2485
2486 return S_OK;
2487}
2488HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2489{
2490 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2491
2492 switch(aProperty)
2493 {
2494 case HWVirtExPropertyType_Enabled:
2495 *aValue = mHWData->mHWVirtExEnabled;
2496 break;
2497
2498 case HWVirtExPropertyType_VPID:
2499 *aValue = mHWData->mHWVirtExVPIDEnabled;
2500 break;
2501
2502 case HWVirtExPropertyType_NestedPaging:
2503 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2504 break;
2505
2506 case HWVirtExPropertyType_UnrestrictedExecution:
2507 *aValue = mHWData->mHWVirtExUXEnabled;
2508 break;
2509
2510 case HWVirtExPropertyType_LargePages:
2511 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2512#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2513 *aValue = FALSE;
2514#endif
2515 break;
2516
2517 case HWVirtExPropertyType_Force:
2518 *aValue = mHWData->mHWVirtExForceEnabled;
2519 break;
2520
2521 default:
2522 return E_INVALIDARG;
2523 }
2524 return S_OK;
2525}
2526
2527HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2528{
2529 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2530
2531 HRESULT rc = i_checkStateDependency(MutableStateDep);
2532 if (FAILED(rc)) return rc;
2533
2534 switch(aProperty)
2535 {
2536 case HWVirtExPropertyType_Enabled:
2537 i_setModified(IsModified_MachineData);
2538 mHWData.backup();
2539 mHWData->mHWVirtExEnabled = !!aValue;
2540 break;
2541
2542 case HWVirtExPropertyType_VPID:
2543 i_setModified(IsModified_MachineData);
2544 mHWData.backup();
2545 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2546 break;
2547
2548 case HWVirtExPropertyType_NestedPaging:
2549 i_setModified(IsModified_MachineData);
2550 mHWData.backup();
2551 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2552 break;
2553
2554 case HWVirtExPropertyType_UnrestrictedExecution:
2555 i_setModified(IsModified_MachineData);
2556 mHWData.backup();
2557 mHWData->mHWVirtExUXEnabled = !!aValue;
2558 break;
2559
2560 case HWVirtExPropertyType_LargePages:
2561 i_setModified(IsModified_MachineData);
2562 mHWData.backup();
2563 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2564 break;
2565
2566 case HWVirtExPropertyType_Force:
2567 i_setModified(IsModified_MachineData);
2568 mHWData.backup();
2569 mHWData->mHWVirtExForceEnabled = !!aValue;
2570 break;
2571
2572 default:
2573 return E_INVALIDARG;
2574 }
2575
2576 return S_OK;
2577}
2578
2579HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2580{
2581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2582
2583 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2584
2585 return S_OK;
2586}
2587
2588HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2589{
2590 /** @todo (r=dmik):
2591 * 1. Allow to change the name of the snapshot folder containing snapshots
2592 * 2. Rename the folder on disk instead of just changing the property
2593 * value (to be smart and not to leave garbage). Note that it cannot be
2594 * done here because the change may be rolled back. Thus, the right
2595 * place is #saveSettings().
2596 */
2597
2598 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2599
2600 HRESULT rc = i_checkStateDependency(MutableStateDep);
2601 if (FAILED(rc)) return rc;
2602
2603 if (!mData->mCurrentSnapshot.isNull())
2604 return setError(E_FAIL,
2605 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2606
2607 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2608
2609 if (strSnapshotFolder.isEmpty())
2610 strSnapshotFolder = "Snapshots";
2611 int vrc = i_calculateFullPath(strSnapshotFolder,
2612 strSnapshotFolder);
2613 if (RT_FAILURE(vrc))
2614 return setError(E_FAIL,
2615 tr("Invalid snapshot folder '%s' (%Rrc)"),
2616 strSnapshotFolder.c_str(), vrc);
2617
2618 i_setModified(IsModified_MachineData);
2619 mUserData.backup();
2620
2621 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2622
2623 return S_OK;
2624}
2625
2626HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2627{
2628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2629
2630 aMediumAttachments.resize(mMediumAttachments->size());
2631 size_t i = 0;
2632 for (MediumAttachmentList::const_iterator
2633 it = mMediumAttachments->begin();
2634 it != mMediumAttachments->end();
2635 ++it, ++i)
2636 aMediumAttachments[i] = *it;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2642{
2643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 Assert(!!mVRDEServer);
2646
2647 aVRDEServer = mVRDEServer;
2648
2649 return S_OK;
2650}
2651
2652HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2653{
2654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2655
2656 aAudioAdapter = mAudioAdapter;
2657
2658 return S_OK;
2659}
2660
2661HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2662{
2663#ifdef VBOX_WITH_VUSB
2664 clearError();
2665 MultiResult rc(S_OK);
2666
2667# ifdef VBOX_WITH_USB
2668 rc = mParent->i_host()->i_checkUSBProxyService();
2669 if (FAILED(rc)) return rc;
2670# endif
2671
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 aUSBControllers.resize(mUSBControllers->size());
2675 size_t i = 0;
2676 for (USBControllerList::const_iterator
2677 it = mUSBControllers->begin();
2678 it != mUSBControllers->end();
2679 ++it, ++i)
2680 aUSBControllers[i] = *it;
2681
2682 return S_OK;
2683#else
2684 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2685 * extended error info to indicate that USB is simply not available
2686 * (w/o treating it as a failure), for example, as in OSE */
2687 NOREF(aUSBControllers);
2688 ReturnComNotImplemented();
2689#endif /* VBOX_WITH_VUSB */
2690}
2691
2692HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2693{
2694#ifdef VBOX_WITH_VUSB
2695 clearError();
2696 MultiResult rc(S_OK);
2697
2698# ifdef VBOX_WITH_USB
2699 rc = mParent->i_host()->i_checkUSBProxyService();
2700 if (FAILED(rc)) return rc;
2701# endif
2702
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 aUSBDeviceFilters = mUSBDeviceFilters;
2706 return rc;
2707#else
2708 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2709 * extended error info to indicate that USB is simply not available
2710 * (w/o treating it as a failure), for example, as in OSE */
2711 NOREF(aUSBDeviceFilters);
2712 ReturnComNotImplemented();
2713#endif /* VBOX_WITH_VUSB */
2714}
2715
2716HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2717{
2718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 aSettingsFilePath = mData->m_strConfigFileFull;
2721
2722 return S_OK;
2723}
2724
2725HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2726{
2727 RT_NOREF(aSettingsFilePath);
2728 ReturnComNotImplemented();
2729}
2730
2731HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2732{
2733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2736 if (FAILED(rc)) return rc;
2737
2738 if (!mData->pMachineConfigFile->fileExists())
2739 // this is a new machine, and no config file exists yet:
2740 *aSettingsModified = TRUE;
2741 else
2742 *aSettingsModified = (mData->flModifications != 0);
2743
2744 return S_OK;
2745}
2746
2747HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2748{
2749 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 *aSessionState = mData->mSession.mState;
2752
2753 return S_OK;
2754}
2755
2756HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2757{
2758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2759
2760 aSessionName = mData->mSession.mName;
2761
2762 return S_OK;
2763}
2764
2765HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2766{
2767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2768
2769 *aSessionPID = mData->mSession.mPID;
2770
2771 return S_OK;
2772}
2773
2774HRESULT Machine::getState(MachineState_T *aState)
2775{
2776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2777
2778 *aState = mData->mMachineState;
2779 Assert(mData->mMachineState != MachineState_Null);
2780
2781 return S_OK;
2782}
2783
2784HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2785{
2786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2787
2788 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2789
2790 return S_OK;
2791}
2792
2793HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2794{
2795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2796
2797 aStateFilePath = mSSData->strStateFilePath;
2798
2799 return S_OK;
2800}
2801
2802HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2803{
2804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2805
2806 i_getLogFolder(aLogFolder);
2807
2808 return S_OK;
2809}
2810
2811HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2812{
2813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2814
2815 aCurrentSnapshot = mData->mCurrentSnapshot;
2816
2817 return S_OK;
2818}
2819
2820HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2821{
2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2823
2824 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2825 ? 0
2826 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2827
2828 return S_OK;
2829}
2830
2831HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2832{
2833 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2834
2835 /* Note: for machines with no snapshots, we always return FALSE
2836 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2837 * reasons :) */
2838
2839 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2840 ? FALSE
2841 : mData->mCurrentStateModified;
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2847{
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 aSharedFolders.resize(mHWData->mSharedFolders.size());
2851 size_t i = 0;
2852 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2853 it = mHWData->mSharedFolders.begin();
2854 it != mHWData->mSharedFolders.end();
2855 ++it, ++i)
2856 aSharedFolders[i] = *it;
2857
2858 return S_OK;
2859}
2860
2861HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2862{
2863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2864
2865 *aClipboardMode = mHWData->mClipboardMode;
2866
2867 return S_OK;
2868}
2869
2870HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2871{
2872 HRESULT rc = S_OK;
2873
2874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 alock.release();
2877 rc = i_onClipboardModeChange(aClipboardMode);
2878 alock.acquire();
2879 if (FAILED(rc)) return rc;
2880
2881 i_setModified(IsModified_MachineData);
2882 mHWData.backup();
2883 mHWData->mClipboardMode = aClipboardMode;
2884
2885 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2886 if (Global::IsOnline(mData->mMachineState))
2887 i_saveSettings(NULL);
2888
2889 return S_OK;
2890}
2891
2892HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2893{
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 *aDnDMode = mHWData->mDnDMode;
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2902{
2903 HRESULT rc = S_OK;
2904
2905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2906
2907 alock.release();
2908 rc = i_onDnDModeChange(aDnDMode);
2909
2910 alock.acquire();
2911 if (FAILED(rc)) return rc;
2912
2913 i_setModified(IsModified_MachineData);
2914 mHWData.backup();
2915 mHWData->mDnDMode = aDnDMode;
2916
2917 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2918 if (Global::IsOnline(mData->mMachineState))
2919 i_saveSettings(NULL);
2920
2921 return S_OK;
2922}
2923
2924HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2925{
2926 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 aStorageControllers.resize(mStorageControllers->size());
2929 size_t i = 0;
2930 for (StorageControllerList::const_iterator
2931 it = mStorageControllers->begin();
2932 it != mStorageControllers->end();
2933 ++it, ++i)
2934 aStorageControllers[i] = *it;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 *aEnabled = mUserData->s.fTeleporterEnabled;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 /* Only allow it to be set to true when PoweredOff or Aborted.
2953 (Clearing it is always permitted.) */
2954 if ( aTeleporterEnabled
2955 && mData->mRegistered
2956 && ( !i_isSessionMachine()
2957 || ( mData->mMachineState != MachineState_PoweredOff
2958 && mData->mMachineState != MachineState_Teleported
2959 && mData->mMachineState != MachineState_Aborted
2960 )
2961 )
2962 )
2963 return setError(VBOX_E_INVALID_VM_STATE,
2964 tr("The machine is not powered off (state is %s)"),
2965 Global::stringifyMachineState(mData->mMachineState));
2966
2967 i_setModified(IsModified_MachineData);
2968 mUserData.backup();
2969 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2970
2971 return S_OK;
2972}
2973
2974HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2975{
2976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2977
2978 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2979
2980 return S_OK;
2981}
2982
2983HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2984{
2985 if (aTeleporterPort >= _64K)
2986 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2987
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2991 if (FAILED(rc)) return rc;
2992
2993 i_setModified(IsModified_MachineData);
2994 mUserData.backup();
2995 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3005
3006 return S_OK;
3007}
3008
3009HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3010{
3011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3012
3013 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3019
3020 return S_OK;
3021}
3022
3023HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3024{
3025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3026 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3027
3028 return S_OK;
3029}
3030
3031HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3032{
3033 /*
3034 * Hash the password first.
3035 */
3036 com::Utf8Str aT = aTeleporterPassword;
3037
3038 if (!aT.isEmpty())
3039 {
3040 if (VBoxIsPasswordHashed(&aT))
3041 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3042 VBoxHashPassword(&aT);
3043 }
3044
3045 /*
3046 * Do the update.
3047 */
3048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3049 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3050 if (SUCCEEDED(hrc))
3051 {
3052 i_setModified(IsModified_MachineData);
3053 mUserData.backup();
3054 mUserData->s.strTeleporterPassword = aT;
3055 }
3056
3057 return hrc;
3058}
3059
3060HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3061{
3062 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3063
3064 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3065 return S_OK;
3066}
3067
3068HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3069{
3070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 /** @todo deal with running state change. */
3073 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3074 if (FAILED(rc)) return rc;
3075
3076 i_setModified(IsModified_MachineData);
3077 mUserData.backup();
3078 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3079 return S_OK;
3080}
3081
3082HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3083{
3084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3085
3086 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3087 return S_OK;
3088}
3089
3090HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3091{
3092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 /** @todo deal with running state change. */
3095 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3096 if (FAILED(rc)) return rc;
3097
3098 i_setModified(IsModified_MachineData);
3099 mUserData.backup();
3100 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3101 return S_OK;
3102}
3103
3104HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3105{
3106 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3107
3108 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3109 return S_OK;
3110}
3111
3112HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3113{
3114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 /** @todo deal with running state change. */
3117 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3118 if (FAILED(rc)) return rc;
3119
3120 i_setModified(IsModified_MachineData);
3121 mUserData.backup();
3122 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3123 return S_OK;
3124}
3125
3126HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3127{
3128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3129
3130 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3131
3132 return S_OK;
3133}
3134
3135HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3136{
3137 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3138
3139 /** @todo deal with running state change. */
3140 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3141 if (FAILED(rc)) return rc;
3142
3143 i_setModified(IsModified_MachineData);
3144 mUserData.backup();
3145 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3146
3147 return S_OK;
3148}
3149
3150HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3151{
3152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3153
3154 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3155 return S_OK;
3156}
3157
3158HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3159{
3160 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3161
3162 /** @todo deal with running state change. */
3163 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3164 if (FAILED(rc)) return rc;
3165
3166 i_setModified(IsModified_MachineData);
3167 mUserData.backup();
3168 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3169 return S_OK;
3170}
3171
3172HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3173{
3174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3175
3176 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3177
3178 return S_OK;
3179}
3180
3181HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3182{
3183 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3184
3185 /* Only allow it to be set to true when PoweredOff or Aborted.
3186 (Clearing it is always permitted.) */
3187 if ( aRTCUseUTC
3188 && mData->mRegistered
3189 && ( !i_isSessionMachine()
3190 || ( mData->mMachineState != MachineState_PoweredOff
3191 && mData->mMachineState != MachineState_Teleported
3192 && mData->mMachineState != MachineState_Aborted
3193 )
3194 )
3195 )
3196 return setError(VBOX_E_INVALID_VM_STATE,
3197 tr("The machine is not powered off (state is %s)"),
3198 Global::stringifyMachineState(mData->mMachineState));
3199
3200 i_setModified(IsModified_MachineData);
3201 mUserData.backup();
3202 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3203
3204 return S_OK;
3205}
3206
3207HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3208{
3209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3210
3211 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3212
3213 return S_OK;
3214}
3215
3216HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3217{
3218 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3219
3220 HRESULT rc = i_checkStateDependency(MutableStateDep);
3221 if (FAILED(rc)) return rc;
3222
3223 i_setModified(IsModified_MachineData);
3224 mHWData.backup();
3225 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3226
3227 return S_OK;
3228}
3229
3230HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3231{
3232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3233
3234 *aIOCacheSize = mHWData->mIOCacheSize;
3235
3236 return S_OK;
3237}
3238
3239HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3240{
3241 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3242
3243 HRESULT rc = i_checkStateDependency(MutableStateDep);
3244 if (FAILED(rc)) return rc;
3245
3246 i_setModified(IsModified_MachineData);
3247 mHWData.backup();
3248 mHWData->mIOCacheSize = aIOCacheSize;
3249
3250 return S_OK;
3251}
3252
3253
3254/**
3255 * @note Locks objects!
3256 */
3257HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3258 LockType_T aLockType)
3259{
3260 /* check the session state */
3261 SessionState_T state;
3262 HRESULT rc = aSession->COMGETTER(State)(&state);
3263 if (FAILED(rc)) return rc;
3264
3265 if (state != SessionState_Unlocked)
3266 return setError(VBOX_E_INVALID_OBJECT_STATE,
3267 tr("The given session is busy"));
3268
3269 // get the client's IInternalSessionControl interface
3270 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3271 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3272 E_INVALIDARG);
3273
3274 // session name (only used in some code paths)
3275 Utf8Str strSessionName;
3276
3277 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3278
3279 if (!mData->mRegistered)
3280 return setError(E_UNEXPECTED,
3281 tr("The machine '%s' is not registered"),
3282 mUserData->s.strName.c_str());
3283
3284 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3285
3286 SessionState_T oldState = mData->mSession.mState;
3287 /* Hack: in case the session is closing and there is a progress object
3288 * which allows waiting for the session to be closed, take the opportunity
3289 * and do a limited wait (max. 1 second). This helps a lot when the system
3290 * is busy and thus session closing can take a little while. */
3291 if ( mData->mSession.mState == SessionState_Unlocking
3292 && mData->mSession.mProgress)
3293 {
3294 alock.release();
3295 mData->mSession.mProgress->WaitForCompletion(1000);
3296 alock.acquire();
3297 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3298 }
3299
3300 // try again now
3301 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3302 // (i.e. session machine exists)
3303 && (aLockType == LockType_Shared) // caller wants a shared link to the
3304 // existing session that holds the write lock:
3305 )
3306 {
3307 // OK, share the session... we are now dealing with three processes:
3308 // 1) VBoxSVC (where this code runs);
3309 // 2) process C: the caller's client process (who wants a shared session);
3310 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3311
3312 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3313 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3314 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3315 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3316 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3317
3318 /*
3319 * Release the lock before calling the client process. It's safe here
3320 * since the only thing to do after we get the lock again is to add
3321 * the remote control to the list (which doesn't directly influence
3322 * anything).
3323 */
3324 alock.release();
3325
3326 // get the console of the session holding the write lock (this is a remote call)
3327 ComPtr<IConsole> pConsoleW;
3328 if (mData->mSession.mLockType == LockType_VM)
3329 {
3330 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3331 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3332 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3333 if (FAILED(rc))
3334 // the failure may occur w/o any error info (from RPC), so provide one
3335 return setError(VBOX_E_VM_ERROR,
3336 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3337 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3338 }
3339
3340 // share the session machine and W's console with the caller's session
3341 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3342 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3343 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3344
3345 if (FAILED(rc))
3346 // the failure may occur w/o any error info (from RPC), so provide one
3347 return setError(VBOX_E_VM_ERROR,
3348 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3349 alock.acquire();
3350
3351 // need to revalidate the state after acquiring the lock again
3352 if (mData->mSession.mState != SessionState_Locked)
3353 {
3354 pSessionControl->Uninitialize();
3355 return setError(VBOX_E_INVALID_SESSION_STATE,
3356 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3357 mUserData->s.strName.c_str());
3358 }
3359
3360 // add the caller's session to the list
3361 mData->mSession.mRemoteControls.push_back(pSessionControl);
3362 }
3363 else if ( mData->mSession.mState == SessionState_Locked
3364 || mData->mSession.mState == SessionState_Unlocking
3365 )
3366 {
3367 // sharing not permitted, or machine still unlocking:
3368 return setError(VBOX_E_INVALID_OBJECT_STATE,
3369 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3370 mUserData->s.strName.c_str());
3371 }
3372 else
3373 {
3374 // machine is not locked: then write-lock the machine (create the session machine)
3375
3376 // must not be busy
3377 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3378
3379 // get the caller's session PID
3380 RTPROCESS pid = NIL_RTPROCESS;
3381 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3382 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3383 Assert(pid != NIL_RTPROCESS);
3384
3385 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3386
3387 if (fLaunchingVMProcess)
3388 {
3389 if (mData->mSession.mPID == NIL_RTPROCESS)
3390 {
3391 // two or more clients racing for a lock, the one which set the
3392 // session state to Spawning will win, the others will get an
3393 // error as we can't decide here if waiting a little would help
3394 // (only for shared locks this would avoid an error)
3395 return setError(VBOX_E_INVALID_OBJECT_STATE,
3396 tr("The machine '%s' already has a lock request pending"),
3397 mUserData->s.strName.c_str());
3398 }
3399
3400 // this machine is awaiting for a spawning session to be opened:
3401 // then the calling process must be the one that got started by
3402 // LaunchVMProcess()
3403
3404 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3405 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3406
3407#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3408 /* Hardened windows builds spawns three processes when a VM is
3409 launched, the 3rd one is the one that will end up here. */
3410 RTPROCESS ppid;
3411 int rc = RTProcQueryParent(pid, &ppid);
3412 if (RT_SUCCESS(rc))
3413 rc = RTProcQueryParent(ppid, &ppid);
3414 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3415 || rc == VERR_ACCESS_DENIED)
3416 {
3417 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3418 mData->mSession.mPID = pid;
3419 }
3420#endif
3421
3422 if (mData->mSession.mPID != pid)
3423 return setError(E_ACCESSDENIED,
3424 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3425 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3426 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3427 }
3428
3429 // create the mutable SessionMachine from the current machine
3430 ComObjPtr<SessionMachine> sessionMachine;
3431 sessionMachine.createObject();
3432 rc = sessionMachine->init(this);
3433 AssertComRC(rc);
3434
3435 /* NOTE: doing return from this function after this point but
3436 * before the end is forbidden since it may call SessionMachine::uninit()
3437 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3438 * lock while still holding the Machine lock in alock so that a deadlock
3439 * is possible due to the wrong lock order. */
3440
3441 if (SUCCEEDED(rc))
3442 {
3443 /*
3444 * Set the session state to Spawning to protect against subsequent
3445 * attempts to open a session and to unregister the machine after
3446 * we release the lock.
3447 */
3448 SessionState_T origState = mData->mSession.mState;
3449 mData->mSession.mState = SessionState_Spawning;
3450
3451#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3452 /* Get the client token ID to be passed to the client process */
3453 Utf8Str strTokenId;
3454 sessionMachine->i_getTokenId(strTokenId);
3455 Assert(!strTokenId.isEmpty());
3456#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3457 /* Get the client token to be passed to the client process */
3458 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3459 /* The token is now "owned" by pToken, fix refcount */
3460 if (!pToken.isNull())
3461 pToken->Release();
3462#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3463
3464 /*
3465 * Release the lock before calling the client process -- it will call
3466 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3467 * because the state is Spawning, so that LaunchVMProcess() and
3468 * LockMachine() calls will fail. This method, called before we
3469 * acquire the lock again, will fail because of the wrong PID.
3470 *
3471 * Note that mData->mSession.mRemoteControls accessed outside
3472 * the lock may not be modified when state is Spawning, so it's safe.
3473 */
3474 alock.release();
3475
3476 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3477#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3478 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3479#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3480 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3481 /* Now the token is owned by the client process. */
3482 pToken.setNull();
3483#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3484 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3485
3486 /* The failure may occur w/o any error info (from RPC), so provide one */
3487 if (FAILED(rc))
3488 setError(VBOX_E_VM_ERROR,
3489 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3490
3491 // get session name, either to remember or to compare against
3492 // the already known session name.
3493 {
3494 Bstr bstrSessionName;
3495 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3496 if (SUCCEEDED(rc2))
3497 strSessionName = bstrSessionName;
3498 }
3499
3500 if ( SUCCEEDED(rc)
3501 && fLaunchingVMProcess
3502 )
3503 {
3504 /* complete the remote session initialization */
3505
3506 /* get the console from the direct session */
3507 ComPtr<IConsole> console;
3508 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3509 ComAssertComRC(rc);
3510
3511 if (SUCCEEDED(rc) && !console)
3512 {
3513 ComAssert(!!console);
3514 rc = E_FAIL;
3515 }
3516
3517 /* assign machine & console to the remote session */
3518 if (SUCCEEDED(rc))
3519 {
3520 /*
3521 * after LaunchVMProcess(), the first and the only
3522 * entry in remoteControls is that remote session
3523 */
3524 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3525 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3526 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3527
3528 /* The failure may occur w/o any error info (from RPC), so provide one */
3529 if (FAILED(rc))
3530 setError(VBOX_E_VM_ERROR,
3531 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3532 }
3533
3534 if (FAILED(rc))
3535 pSessionControl->Uninitialize();
3536 }
3537
3538 /* acquire the lock again */
3539 alock.acquire();
3540
3541 /* Restore the session state */
3542 mData->mSession.mState = origState;
3543 }
3544
3545 // finalize spawning anyway (this is why we don't return on errors above)
3546 if (fLaunchingVMProcess)
3547 {
3548 Assert(mData->mSession.mName == strSessionName);
3549 /* Note that the progress object is finalized later */
3550 /** @todo Consider checking mData->mSession.mProgress for cancellation
3551 * around here. */
3552
3553 /* We don't reset mSession.mPID here because it is necessary for
3554 * SessionMachine::uninit() to reap the child process later. */
3555
3556 if (FAILED(rc))
3557 {
3558 /* Close the remote session, remove the remote control from the list
3559 * and reset session state to Closed (@note keep the code in sync
3560 * with the relevant part in checkForSpawnFailure()). */
3561
3562 Assert(mData->mSession.mRemoteControls.size() == 1);
3563 if (mData->mSession.mRemoteControls.size() == 1)
3564 {
3565 ErrorInfoKeeper eik;
3566 mData->mSession.mRemoteControls.front()->Uninitialize();
3567 }
3568
3569 mData->mSession.mRemoteControls.clear();
3570 mData->mSession.mState = SessionState_Unlocked;
3571 }
3572 }
3573 else
3574 {
3575 /* memorize PID of the directly opened session */
3576 if (SUCCEEDED(rc))
3577 mData->mSession.mPID = pid;
3578 }
3579
3580 if (SUCCEEDED(rc))
3581 {
3582 mData->mSession.mLockType = aLockType;
3583 /* memorize the direct session control and cache IUnknown for it */
3584 mData->mSession.mDirectControl = pSessionControl;
3585 mData->mSession.mState = SessionState_Locked;
3586 if (!fLaunchingVMProcess)
3587 mData->mSession.mName = strSessionName;
3588 /* associate the SessionMachine with this Machine */
3589 mData->mSession.mMachine = sessionMachine;
3590
3591 /* request an IUnknown pointer early from the remote party for later
3592 * identity checks (it will be internally cached within mDirectControl
3593 * at least on XPCOM) */
3594 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3595 NOREF(unk);
3596 }
3597
3598 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3599 * would break the lock order */
3600 alock.release();
3601
3602 /* uninitialize the created session machine on failure */
3603 if (FAILED(rc))
3604 sessionMachine->uninit();
3605 }
3606
3607 if (SUCCEEDED(rc))
3608 {
3609 /*
3610 * tell the client watcher thread to update the set of
3611 * machines that have open sessions
3612 */
3613 mParent->i_updateClientWatcher();
3614
3615 if (oldState != SessionState_Locked)
3616 /* fire an event */
3617 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3618 }
3619
3620 return rc;
3621}
3622
3623/**
3624 * @note Locks objects!
3625 */
3626HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3627 const com::Utf8Str &aName,
3628 const com::Utf8Str &aEnvironment,
3629 ComPtr<IProgress> &aProgress)
3630{
3631 Utf8Str strFrontend(aName);
3632 /* "emergencystop" doesn't need the session, so skip the checks/interface
3633 * retrieval. This code doesn't quite fit in here, but introducing a
3634 * special API method would be even more effort, and would require explicit
3635 * support by every API client. It's better to hide the feature a bit. */
3636 if (strFrontend != "emergencystop")
3637 CheckComArgNotNull(aSession);
3638
3639 HRESULT rc = S_OK;
3640 if (strFrontend.isEmpty())
3641 {
3642 Bstr bstrFrontend;
3643 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3644 if (FAILED(rc))
3645 return rc;
3646 strFrontend = bstrFrontend;
3647 if (strFrontend.isEmpty())
3648 {
3649 ComPtr<ISystemProperties> systemProperties;
3650 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3651 if (FAILED(rc))
3652 return rc;
3653 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3654 if (FAILED(rc))
3655 return rc;
3656 strFrontend = bstrFrontend;
3657 }
3658 /* paranoia - emergencystop is not a valid default */
3659 if (strFrontend == "emergencystop")
3660 strFrontend = Utf8Str::Empty;
3661 }
3662 /* default frontend: Qt GUI */
3663 if (strFrontend.isEmpty())
3664 strFrontend = "GUI/Qt";
3665
3666 if (strFrontend != "emergencystop")
3667 {
3668 /* check the session state */
3669 SessionState_T state;
3670 rc = aSession->COMGETTER(State)(&state);
3671 if (FAILED(rc))
3672 return rc;
3673
3674 if (state != SessionState_Unlocked)
3675 return setError(VBOX_E_INVALID_OBJECT_STATE,
3676 tr("The given session is busy"));
3677
3678 /* get the IInternalSessionControl interface */
3679 ComPtr<IInternalSessionControl> control(aSession);
3680 ComAssertMsgRet(!control.isNull(),
3681 ("No IInternalSessionControl interface"),
3682 E_INVALIDARG);
3683
3684 /* get the teleporter enable state for the progress object init. */
3685 BOOL fTeleporterEnabled;
3686 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3687 if (FAILED(rc))
3688 return rc;
3689
3690 /* create a progress object */
3691 ComObjPtr<ProgressProxy> progress;
3692 progress.createObject();
3693 rc = progress->init(mParent,
3694 static_cast<IMachine*>(this),
3695 Bstr(tr("Starting VM")).raw(),
3696 TRUE /* aCancelable */,
3697 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3698 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3699 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3700 2 /* uFirstOperationWeight */,
3701 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3702
3703 if (SUCCEEDED(rc))
3704 {
3705 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3706 if (SUCCEEDED(rc))
3707 {
3708 aProgress = progress;
3709
3710 /* signal the client watcher thread */
3711 mParent->i_updateClientWatcher();
3712
3713 /* fire an event */
3714 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3715 }
3716 }
3717 }
3718 else
3719 {
3720 /* no progress object - either instant success or failure */
3721 aProgress = NULL;
3722
3723 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3724
3725 if (mData->mSession.mState != SessionState_Locked)
3726 return setError(VBOX_E_INVALID_OBJECT_STATE,
3727 tr("The machine '%s' is not locked by a session"),
3728 mUserData->s.strName.c_str());
3729
3730 /* must have a VM process associated - do not kill normal API clients
3731 * with an open session */
3732 if (!Global::IsOnline(mData->mMachineState))
3733 return setError(VBOX_E_INVALID_OBJECT_STATE,
3734 tr("The machine '%s' does not have a VM process"),
3735 mUserData->s.strName.c_str());
3736
3737 /* forcibly terminate the VM process */
3738 if (mData->mSession.mPID != NIL_RTPROCESS)
3739 RTProcTerminate(mData->mSession.mPID);
3740
3741 /* signal the client watcher thread, as most likely the client has
3742 * been terminated */
3743 mParent->i_updateClientWatcher();
3744 }
3745
3746 return rc;
3747}
3748
3749HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3750{
3751 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3752 return setError(E_INVALIDARG,
3753 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3754 aPosition, SchemaDefs::MaxBootPosition);
3755
3756 if (aDevice == DeviceType_USB)
3757 return setError(E_NOTIMPL,
3758 tr("Booting from USB device is currently not supported"));
3759
3760 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3761
3762 HRESULT rc = i_checkStateDependency(MutableStateDep);
3763 if (FAILED(rc)) return rc;
3764
3765 i_setModified(IsModified_MachineData);
3766 mHWData.backup();
3767 mHWData->mBootOrder[aPosition - 1] = aDevice;
3768
3769 return S_OK;
3770}
3771
3772HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3773{
3774 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3775 return setError(E_INVALIDARG,
3776 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3777 aPosition, SchemaDefs::MaxBootPosition);
3778
3779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3780
3781 *aDevice = mHWData->mBootOrder[aPosition - 1];
3782
3783 return S_OK;
3784}
3785
3786HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3787 LONG aControllerPort,
3788 LONG aDevice,
3789 DeviceType_T aType,
3790 const ComPtr<IMedium> &aMedium)
3791{
3792 IMedium *aM = aMedium;
3793 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3794 aName.c_str(), aControllerPort, aDevice, aType, aM));
3795
3796 // request the host lock first, since might be calling Host methods for getting host drives;
3797 // next, protect the media tree all the while we're in here, as well as our member variables
3798 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3799 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3800
3801 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3802 if (FAILED(rc)) return rc;
3803
3804 /// @todo NEWMEDIA implicit machine registration
3805 if (!mData->mRegistered)
3806 return setError(VBOX_E_INVALID_OBJECT_STATE,
3807 tr("Cannot attach storage devices to an unregistered machine"));
3808
3809 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3810
3811 /* Check for an existing controller. */
3812 ComObjPtr<StorageController> ctl;
3813 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3814 if (FAILED(rc)) return rc;
3815
3816 StorageControllerType_T ctrlType;
3817 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3818 if (FAILED(rc))
3819 return setError(E_FAIL,
3820 tr("Could not get type of controller '%s'"),
3821 aName.c_str());
3822
3823 bool fSilent = false;
3824 Utf8Str strReconfig;
3825
3826 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3827 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3828 if ( mData->mMachineState == MachineState_Paused
3829 && strReconfig == "1")
3830 fSilent = true;
3831
3832 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3833 bool fHotplug = false;
3834 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3835 fHotplug = true;
3836
3837 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3838 return setError(VBOX_E_INVALID_VM_STATE,
3839 tr("Controller '%s' does not support hotplugging"),
3840 aName.c_str());
3841
3842 // check that the port and device are not out of range
3843 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3844 if (FAILED(rc)) return rc;
3845
3846 /* check if the device slot is already busy */
3847 MediumAttachment *pAttachTemp;
3848 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3849 aName,
3850 aControllerPort,
3851 aDevice)))
3852 {
3853 Medium *pMedium = pAttachTemp->i_getMedium();
3854 if (pMedium)
3855 {
3856 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3857 return setError(VBOX_E_OBJECT_IN_USE,
3858 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3859 pMedium->i_getLocationFull().c_str(),
3860 aControllerPort,
3861 aDevice,
3862 aName.c_str());
3863 }
3864 else
3865 return setError(VBOX_E_OBJECT_IN_USE,
3866 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3867 aControllerPort, aDevice, aName.c_str());
3868 }
3869
3870 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3871 if (aMedium && medium.isNull())
3872 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3873
3874 AutoCaller mediumCaller(medium);
3875 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3876
3877 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3878
3879 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3880 && !medium.isNull()
3881 )
3882 return setError(VBOX_E_OBJECT_IN_USE,
3883 tr("Medium '%s' is already attached to this virtual machine"),
3884 medium->i_getLocationFull().c_str());
3885
3886 if (!medium.isNull())
3887 {
3888 MediumType_T mtype = medium->i_getType();
3889 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3890 // For DVDs it's not written to the config file, so needs no global config
3891 // version bump. For floppies it's a new attribute "type", which is ignored
3892 // by older VirtualBox version, so needs no global config version bump either.
3893 // For hard disks this type is not accepted.
3894 if (mtype == MediumType_MultiAttach)
3895 {
3896 // This type is new with VirtualBox 4.0 and therefore requires settings
3897 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3898 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3899 // two reasons: The medium type is a property of the media registry tree, which
3900 // can reside in the global config file (for pre-4.0 media); we would therefore
3901 // possibly need to bump the global config version. We don't want to do that though
3902 // because that might make downgrading to pre-4.0 impossible.
3903 // As a result, we can only use these two new types if the medium is NOT in the
3904 // global registry:
3905 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3906 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3907 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3908 )
3909 return setError(VBOX_E_INVALID_OBJECT_STATE,
3910 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3911 "to machines that were created with VirtualBox 4.0 or later"),
3912 medium->i_getLocationFull().c_str());
3913 }
3914 }
3915
3916 bool fIndirect = false;
3917 if (!medium.isNull())
3918 fIndirect = medium->i_isReadOnly();
3919 bool associate = true;
3920
3921 do
3922 {
3923 if ( aType == DeviceType_HardDisk
3924 && mMediumAttachments.isBackedUp())
3925 {
3926 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3927
3928 /* check if the medium was attached to the VM before we started
3929 * changing attachments in which case the attachment just needs to
3930 * be restored */
3931 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3932 {
3933 AssertReturn(!fIndirect, E_FAIL);
3934
3935 /* see if it's the same bus/channel/device */
3936 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3937 {
3938 /* the simplest case: restore the whole attachment
3939 * and return, nothing else to do */
3940 mMediumAttachments->push_back(pAttachTemp);
3941
3942 /* Reattach the medium to the VM. */
3943 if (fHotplug || fSilent)
3944 {
3945 mediumLock.release();
3946 treeLock.release();
3947 alock.release();
3948
3949 MediumLockList *pMediumLockList(new MediumLockList());
3950
3951 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3952 medium /* pToLockWrite */,
3953 false /* fMediumLockWriteAll */,
3954 NULL,
3955 *pMediumLockList);
3956 alock.acquire();
3957 if (FAILED(rc))
3958 delete pMediumLockList;
3959 else
3960 {
3961 mData->mSession.mLockedMedia.Unlock();
3962 alock.release();
3963 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3964 mData->mSession.mLockedMedia.Lock();
3965 alock.acquire();
3966 }
3967 alock.release();
3968
3969 if (SUCCEEDED(rc))
3970 {
3971 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3972 /* Remove lock list in case of error. */
3973 if (FAILED(rc))
3974 {
3975 mData->mSession.mLockedMedia.Unlock();
3976 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3977 mData->mSession.mLockedMedia.Lock();
3978 }
3979 }
3980 }
3981
3982 return S_OK;
3983 }
3984
3985 /* bus/channel/device differ; we need a new attachment object,
3986 * but don't try to associate it again */
3987 associate = false;
3988 break;
3989 }
3990 }
3991
3992 /* go further only if the attachment is to be indirect */
3993 if (!fIndirect)
3994 break;
3995
3996 /* perform the so called smart attachment logic for indirect
3997 * attachments. Note that smart attachment is only applicable to base
3998 * hard disks. */
3999
4000 if (medium->i_getParent().isNull())
4001 {
4002 /* first, investigate the backup copy of the current hard disk
4003 * attachments to make it possible to re-attach existing diffs to
4004 * another device slot w/o losing their contents */
4005 if (mMediumAttachments.isBackedUp())
4006 {
4007 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4008
4009 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4010 uint32_t foundLevel = 0;
4011
4012 for (MediumAttachmentList::const_iterator
4013 it = oldAtts.begin();
4014 it != oldAtts.end();
4015 ++it)
4016 {
4017 uint32_t level = 0;
4018 MediumAttachment *pAttach = *it;
4019 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4020 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4021 if (pMedium.isNull())
4022 continue;
4023
4024 if (pMedium->i_getBase(&level) == medium)
4025 {
4026 /* skip the hard disk if its currently attached (we
4027 * cannot attach the same hard disk twice) */
4028 if (i_findAttachment(*mMediumAttachments.data(),
4029 pMedium))
4030 continue;
4031
4032 /* matched device, channel and bus (i.e. attached to the
4033 * same place) will win and immediately stop the search;
4034 * otherwise the attachment that has the youngest
4035 * descendant of medium will be used
4036 */
4037 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4038 {
4039 /* the simplest case: restore the whole attachment
4040 * and return, nothing else to do */
4041 mMediumAttachments->push_back(*it);
4042
4043 /* Reattach the medium to the VM. */
4044 if (fHotplug || fSilent)
4045 {
4046 mediumLock.release();
4047 treeLock.release();
4048 alock.release();
4049
4050 MediumLockList *pMediumLockList(new MediumLockList());
4051
4052 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4053 medium /* pToLockWrite */,
4054 false /* fMediumLockWriteAll */,
4055 NULL,
4056 *pMediumLockList);
4057 alock.acquire();
4058 if (FAILED(rc))
4059 delete pMediumLockList;
4060 else
4061 {
4062 mData->mSession.mLockedMedia.Unlock();
4063 alock.release();
4064 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4065 mData->mSession.mLockedMedia.Lock();
4066 alock.acquire();
4067 }
4068 alock.release();
4069
4070 if (SUCCEEDED(rc))
4071 {
4072 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4073 /* Remove lock list in case of error. */
4074 if (FAILED(rc))
4075 {
4076 mData->mSession.mLockedMedia.Unlock();
4077 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4078 mData->mSession.mLockedMedia.Lock();
4079 }
4080 }
4081 }
4082
4083 return S_OK;
4084 }
4085 else if ( foundIt == oldAtts.end()
4086 || level > foundLevel /* prefer younger */
4087 )
4088 {
4089 foundIt = it;
4090 foundLevel = level;
4091 }
4092 }
4093 }
4094
4095 if (foundIt != oldAtts.end())
4096 {
4097 /* use the previously attached hard disk */
4098 medium = (*foundIt)->i_getMedium();
4099 mediumCaller.attach(medium);
4100 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4101 mediumLock.attach(medium);
4102 /* not implicit, doesn't require association with this VM */
4103 fIndirect = false;
4104 associate = false;
4105 /* go right to the MediumAttachment creation */
4106 break;
4107 }
4108 }
4109
4110 /* must give up the medium lock and medium tree lock as below we
4111 * go over snapshots, which needs a lock with higher lock order. */
4112 mediumLock.release();
4113 treeLock.release();
4114
4115 /* then, search through snapshots for the best diff in the given
4116 * hard disk's chain to base the new diff on */
4117
4118 ComObjPtr<Medium> base;
4119 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4120 while (snap)
4121 {
4122 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4123
4124 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4125
4126 MediumAttachment *pAttachFound = NULL;
4127 uint32_t foundLevel = 0;
4128
4129 for (MediumAttachmentList::const_iterator
4130 it = snapAtts.begin();
4131 it != snapAtts.end();
4132 ++it)
4133 {
4134 MediumAttachment *pAttach = *it;
4135 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4136 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4137 if (pMedium.isNull())
4138 continue;
4139
4140 uint32_t level = 0;
4141 if (pMedium->i_getBase(&level) == medium)
4142 {
4143 /* matched device, channel and bus (i.e. attached to the
4144 * same place) will win and immediately stop the search;
4145 * otherwise the attachment that has the youngest
4146 * descendant of medium will be used
4147 */
4148 if ( pAttach->i_getDevice() == aDevice
4149 && pAttach->i_getPort() == aControllerPort
4150 && pAttach->i_getControllerName() == aName
4151 )
4152 {
4153 pAttachFound = pAttach;
4154 break;
4155 }
4156 else if ( !pAttachFound
4157 || level > foundLevel /* prefer younger */
4158 )
4159 {
4160 pAttachFound = pAttach;
4161 foundLevel = level;
4162 }
4163 }
4164 }
4165
4166 if (pAttachFound)
4167 {
4168 base = pAttachFound->i_getMedium();
4169 break;
4170 }
4171
4172 snap = snap->i_getParent();
4173 }
4174
4175 /* re-lock medium tree and the medium, as we need it below */
4176 treeLock.acquire();
4177 mediumLock.acquire();
4178
4179 /* found a suitable diff, use it as a base */
4180 if (!base.isNull())
4181 {
4182 medium = base;
4183 mediumCaller.attach(medium);
4184 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4185 mediumLock.attach(medium);
4186 }
4187 }
4188
4189 Utf8Str strFullSnapshotFolder;
4190 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4191
4192 ComObjPtr<Medium> diff;
4193 diff.createObject();
4194 // store this diff in the same registry as the parent
4195 Guid uuidRegistryParent;
4196 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4197 {
4198 // parent image has no registry: this can happen if we're attaching a new immutable
4199 // image that has not yet been attached (medium then points to the base and we're
4200 // creating the diff image for the immutable, and the parent is not yet registered);
4201 // put the parent in the machine registry then
4202 mediumLock.release();
4203 treeLock.release();
4204 alock.release();
4205 i_addMediumToRegistry(medium);
4206 alock.acquire();
4207 treeLock.acquire();
4208 mediumLock.acquire();
4209 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4210 }
4211 rc = diff->init(mParent,
4212 medium->i_getPreferredDiffFormat(),
4213 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4214 uuidRegistryParent,
4215 DeviceType_HardDisk);
4216 if (FAILED(rc)) return rc;
4217
4218 /* Apply the normal locking logic to the entire chain. */
4219 MediumLockList *pMediumLockList(new MediumLockList());
4220 mediumLock.release();
4221 treeLock.release();
4222 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4223 diff /* pToLockWrite */,
4224 false /* fMediumLockWriteAll */,
4225 medium,
4226 *pMediumLockList);
4227 treeLock.acquire();
4228 mediumLock.acquire();
4229 if (SUCCEEDED(rc))
4230 {
4231 mediumLock.release();
4232 treeLock.release();
4233 rc = pMediumLockList->Lock();
4234 treeLock.acquire();
4235 mediumLock.acquire();
4236 if (FAILED(rc))
4237 setError(rc,
4238 tr("Could not lock medium when creating diff '%s'"),
4239 diff->i_getLocationFull().c_str());
4240 else
4241 {
4242 /* will release the lock before the potentially lengthy
4243 * operation, so protect with the special state */
4244 MachineState_T oldState = mData->mMachineState;
4245 i_setMachineState(MachineState_SettingUp);
4246
4247 mediumLock.release();
4248 treeLock.release();
4249 alock.release();
4250
4251 rc = medium->i_createDiffStorage(diff,
4252 medium->i_getPreferredDiffVariant(),
4253 pMediumLockList,
4254 NULL /* aProgress */,
4255 true /* aWait */);
4256
4257 alock.acquire();
4258 treeLock.acquire();
4259 mediumLock.acquire();
4260
4261 i_setMachineState(oldState);
4262 }
4263 }
4264
4265 /* Unlock the media and free the associated memory. */
4266 delete pMediumLockList;
4267
4268 if (FAILED(rc)) return rc;
4269
4270 /* use the created diff for the actual attachment */
4271 medium = diff;
4272 mediumCaller.attach(medium);
4273 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4274 mediumLock.attach(medium);
4275 }
4276 while (0);
4277
4278 ComObjPtr<MediumAttachment> attachment;
4279 attachment.createObject();
4280 rc = attachment->init(this,
4281 medium,
4282 aName,
4283 aControllerPort,
4284 aDevice,
4285 aType,
4286 fIndirect,
4287 false /* fPassthrough */,
4288 false /* fTempEject */,
4289 false /* fNonRotational */,
4290 false /* fDiscard */,
4291 fHotplug /* fHotPluggable */,
4292 Utf8Str::Empty);
4293 if (FAILED(rc)) return rc;
4294
4295 if (associate && !medium.isNull())
4296 {
4297 // as the last step, associate the medium to the VM
4298 rc = medium->i_addBackReference(mData->mUuid);
4299 // here we can fail because of Deleting, or being in process of creating a Diff
4300 if (FAILED(rc)) return rc;
4301
4302 mediumLock.release();
4303 treeLock.release();
4304 alock.release();
4305 i_addMediumToRegistry(medium);
4306 alock.acquire();
4307 treeLock.acquire();
4308 mediumLock.acquire();
4309 }
4310
4311 /* success: finally remember the attachment */
4312 i_setModified(IsModified_Storage);
4313 mMediumAttachments.backup();
4314 mMediumAttachments->push_back(attachment);
4315
4316 mediumLock.release();
4317 treeLock.release();
4318 alock.release();
4319
4320 if (fHotplug || fSilent)
4321 {
4322 if (!medium.isNull())
4323 {
4324 MediumLockList *pMediumLockList(new MediumLockList());
4325
4326 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4327 medium /* pToLockWrite */,
4328 false /* fMediumLockWriteAll */,
4329 NULL,
4330 *pMediumLockList);
4331 alock.acquire();
4332 if (FAILED(rc))
4333 delete pMediumLockList;
4334 else
4335 {
4336 mData->mSession.mLockedMedia.Unlock();
4337 alock.release();
4338 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4339 mData->mSession.mLockedMedia.Lock();
4340 alock.acquire();
4341 }
4342 alock.release();
4343 }
4344
4345 if (SUCCEEDED(rc))
4346 {
4347 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4348 /* Remove lock list in case of error. */
4349 if (FAILED(rc))
4350 {
4351 mData->mSession.mLockedMedia.Unlock();
4352 mData->mSession.mLockedMedia.Remove(attachment);
4353 mData->mSession.mLockedMedia.Lock();
4354 }
4355 }
4356 }
4357
4358 /* Save modified registries, but skip this machine as it's the caller's
4359 * job to save its settings like all other settings changes. */
4360 mParent->i_unmarkRegistryModified(i_getId());
4361 mParent->i_saveModifiedRegistries();
4362
4363 return rc;
4364}
4365
4366HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4367 LONG aDevice)
4368{
4369 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4370 aName.c_str(), aControllerPort, aDevice));
4371
4372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4373
4374 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4375 if (FAILED(rc)) return rc;
4376
4377 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4378
4379 /* Check for an existing controller. */
4380 ComObjPtr<StorageController> ctl;
4381 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4382 if (FAILED(rc)) return rc;
4383
4384 StorageControllerType_T ctrlType;
4385 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4386 if (FAILED(rc))
4387 return setError(E_FAIL,
4388 tr("Could not get type of controller '%s'"),
4389 aName.c_str());
4390
4391 bool fSilent = false;
4392 Utf8Str strReconfig;
4393
4394 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4395 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4396 if ( mData->mMachineState == MachineState_Paused
4397 && strReconfig == "1")
4398 fSilent = true;
4399
4400 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4401 bool fHotplug = false;
4402 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4403 fHotplug = true;
4404
4405 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4406 return setError(VBOX_E_INVALID_VM_STATE,
4407 tr("Controller '%s' does not support hotplugging"),
4408 aName.c_str());
4409
4410 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4411 aName,
4412 aControllerPort,
4413 aDevice);
4414 if (!pAttach)
4415 return setError(VBOX_E_OBJECT_NOT_FOUND,
4416 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4417 aDevice, aControllerPort, aName.c_str());
4418
4419 if (fHotplug && !pAttach->i_getHotPluggable())
4420 return setError(VBOX_E_NOT_SUPPORTED,
4421 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4422 aDevice, aControllerPort, aName.c_str());
4423
4424 /*
4425 * The VM has to detach the device before we delete any implicit diffs.
4426 * If this fails we can roll back without loosing data.
4427 */
4428 if (fHotplug || fSilent)
4429 {
4430 alock.release();
4431 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4432 alock.acquire();
4433 }
4434 if (FAILED(rc)) return rc;
4435
4436 /* If we are here everything went well and we can delete the implicit now. */
4437 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4438
4439 alock.release();
4440
4441 /* Save modified registries, but skip this machine as it's the caller's
4442 * job to save its settings like all other settings changes. */
4443 mParent->i_unmarkRegistryModified(i_getId());
4444 mParent->i_saveModifiedRegistries();
4445
4446 return rc;
4447}
4448
4449HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4450 LONG aDevice, BOOL aPassthrough)
4451{
4452 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4453 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4454
4455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4456
4457 HRESULT rc = i_checkStateDependency(MutableStateDep);
4458 if (FAILED(rc)) return rc;
4459
4460 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4461
4462 if (Global::IsOnlineOrTransient(mData->mMachineState))
4463 return setError(VBOX_E_INVALID_VM_STATE,
4464 tr("Invalid machine state: %s"),
4465 Global::stringifyMachineState(mData->mMachineState));
4466
4467 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4468 aName,
4469 aControllerPort,
4470 aDevice);
4471 if (!pAttach)
4472 return setError(VBOX_E_OBJECT_NOT_FOUND,
4473 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4474 aDevice, aControllerPort, aName.c_str());
4475
4476
4477 i_setModified(IsModified_Storage);
4478 mMediumAttachments.backup();
4479
4480 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4481
4482 if (pAttach->i_getType() != DeviceType_DVD)
4483 return setError(E_INVALIDARG,
4484 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4485 aDevice, aControllerPort, aName.c_str());
4486 pAttach->i_updatePassthrough(!!aPassthrough);
4487
4488 return S_OK;
4489}
4490
4491HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4492 LONG aDevice, BOOL aTemporaryEject)
4493{
4494
4495 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4496 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4497
4498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4499
4500 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4501 if (FAILED(rc)) return rc;
4502
4503 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4504 aName,
4505 aControllerPort,
4506 aDevice);
4507 if (!pAttach)
4508 return setError(VBOX_E_OBJECT_NOT_FOUND,
4509 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4510 aDevice, aControllerPort, aName.c_str());
4511
4512
4513 i_setModified(IsModified_Storage);
4514 mMediumAttachments.backup();
4515
4516 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4517
4518 if (pAttach->i_getType() != DeviceType_DVD)
4519 return setError(E_INVALIDARG,
4520 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4521 aDevice, aControllerPort, aName.c_str());
4522 pAttach->i_updateTempEject(!!aTemporaryEject);
4523
4524 return S_OK;
4525}
4526
4527HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4528 LONG aDevice, BOOL aNonRotational)
4529{
4530
4531 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4532 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4533
4534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4535
4536 HRESULT rc = i_checkStateDependency(MutableStateDep);
4537 if (FAILED(rc)) return rc;
4538
4539 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4540
4541 if (Global::IsOnlineOrTransient(mData->mMachineState))
4542 return setError(VBOX_E_INVALID_VM_STATE,
4543 tr("Invalid machine state: %s"),
4544 Global::stringifyMachineState(mData->mMachineState));
4545
4546 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4547 aName,
4548 aControllerPort,
4549 aDevice);
4550 if (!pAttach)
4551 return setError(VBOX_E_OBJECT_NOT_FOUND,
4552 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4553 aDevice, aControllerPort, aName.c_str());
4554
4555
4556 i_setModified(IsModified_Storage);
4557 mMediumAttachments.backup();
4558
4559 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4560
4561 if (pAttach->i_getType() != DeviceType_HardDisk)
4562 return setError(E_INVALIDARG,
4563 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"),
4564 aDevice, aControllerPort, aName.c_str());
4565 pAttach->i_updateNonRotational(!!aNonRotational);
4566
4567 return S_OK;
4568}
4569
4570HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4571 LONG aDevice, BOOL aDiscard)
4572{
4573
4574 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4575 aName.c_str(), aControllerPort, aDevice, aDiscard));
4576
4577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4578
4579 HRESULT rc = i_checkStateDependency(MutableStateDep);
4580 if (FAILED(rc)) return rc;
4581
4582 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4583
4584 if (Global::IsOnlineOrTransient(mData->mMachineState))
4585 return setError(VBOX_E_INVALID_VM_STATE,
4586 tr("Invalid machine state: %s"),
4587 Global::stringifyMachineState(mData->mMachineState));
4588
4589 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4590 aName,
4591 aControllerPort,
4592 aDevice);
4593 if (!pAttach)
4594 return setError(VBOX_E_OBJECT_NOT_FOUND,
4595 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4596 aDevice, aControllerPort, aName.c_str());
4597
4598
4599 i_setModified(IsModified_Storage);
4600 mMediumAttachments.backup();
4601
4602 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4603
4604 if (pAttach->i_getType() != DeviceType_HardDisk)
4605 return setError(E_INVALIDARG,
4606 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"),
4607 aDevice, aControllerPort, aName.c_str());
4608 pAttach->i_updateDiscard(!!aDiscard);
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4614 LONG aDevice, BOOL aHotPluggable)
4615{
4616 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4617 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4618
4619 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4620
4621 HRESULT rc = i_checkStateDependency(MutableStateDep);
4622 if (FAILED(rc)) return rc;
4623
4624 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4625
4626 if (Global::IsOnlineOrTransient(mData->mMachineState))
4627 return setError(VBOX_E_INVALID_VM_STATE,
4628 tr("Invalid machine state: %s"),
4629 Global::stringifyMachineState(mData->mMachineState));
4630
4631 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4632 aName,
4633 aControllerPort,
4634 aDevice);
4635 if (!pAttach)
4636 return setError(VBOX_E_OBJECT_NOT_FOUND,
4637 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4638 aDevice, aControllerPort, aName.c_str());
4639
4640 /* Check for an existing controller. */
4641 ComObjPtr<StorageController> ctl;
4642 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4643 if (FAILED(rc)) return rc;
4644
4645 StorageControllerType_T ctrlType;
4646 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4647 if (FAILED(rc))
4648 return setError(E_FAIL,
4649 tr("Could not get type of controller '%s'"),
4650 aName.c_str());
4651
4652 if (!i_isControllerHotplugCapable(ctrlType))
4653 return setError(VBOX_E_NOT_SUPPORTED,
4654 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4655 aName.c_str());
4656
4657 i_setModified(IsModified_Storage);
4658 mMediumAttachments.backup();
4659
4660 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4661
4662 if (pAttach->i_getType() == DeviceType_Floppy)
4663 return setError(E_INVALIDARG,
4664 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"),
4665 aDevice, aControllerPort, aName.c_str());
4666 pAttach->i_updateHotPluggable(!!aHotPluggable);
4667
4668 return S_OK;
4669}
4670
4671HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4672 LONG aDevice)
4673{
4674 int rc = S_OK;
4675 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4676 aName.c_str(), aControllerPort, aDevice));
4677
4678 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4679
4680 return rc;
4681}
4682
4683HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4684 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4685{
4686 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4687 aName.c_str(), aControllerPort, aDevice));
4688
4689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4690
4691 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4692 if (FAILED(rc)) return rc;
4693
4694 if (Global::IsOnlineOrTransient(mData->mMachineState))
4695 return setError(VBOX_E_INVALID_VM_STATE,
4696 tr("Invalid machine state: %s"),
4697 Global::stringifyMachineState(mData->mMachineState));
4698
4699 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4700 aName,
4701 aControllerPort,
4702 aDevice);
4703 if (!pAttach)
4704 return setError(VBOX_E_OBJECT_NOT_FOUND,
4705 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4706 aDevice, aControllerPort, aName.c_str());
4707
4708
4709 i_setModified(IsModified_Storage);
4710 mMediumAttachments.backup();
4711
4712 IBandwidthGroup *iB = aBandwidthGroup;
4713 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4714 if (aBandwidthGroup && group.isNull())
4715 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4716
4717 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4718
4719 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4720 if (strBandwidthGroupOld.isNotEmpty())
4721 {
4722 /* Get the bandwidth group object and release it - this must not fail. */
4723 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4724 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4725 Assert(SUCCEEDED(rc));
4726
4727 pBandwidthGroupOld->i_release();
4728 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4729 }
4730
4731 if (!group.isNull())
4732 {
4733 group->i_reference();
4734 pAttach->i_updateBandwidthGroup(group->i_getName());
4735 }
4736
4737 return S_OK;
4738}
4739
4740HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4741 LONG aControllerPort,
4742 LONG aDevice,
4743 DeviceType_T aType)
4744{
4745 HRESULT rc = S_OK;
4746
4747 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4748 aName.c_str(), aControllerPort, aDevice, aType));
4749
4750 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4751
4752 return rc;
4753}
4754
4755
4756HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4757 LONG aControllerPort,
4758 LONG aDevice,
4759 BOOL aForce)
4760{
4761 int rc = S_OK;
4762 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4763 aName.c_str(), aControllerPort, aForce));
4764
4765 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4766
4767 return rc;
4768}
4769
4770HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4771 LONG aControllerPort,
4772 LONG aDevice,
4773 const ComPtr<IMedium> &aMedium,
4774 BOOL aForce)
4775{
4776 int rc = S_OK;
4777 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4778 aName.c_str(), aControllerPort, aDevice, aForce));
4779
4780 // request the host lock first, since might be calling Host methods for getting host drives;
4781 // next, protect the media tree all the while we're in here, as well as our member variables
4782 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4783 this->lockHandle(),
4784 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4785
4786 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4787 aName,
4788 aControllerPort,
4789 aDevice);
4790 if (pAttach.isNull())
4791 return setError(VBOX_E_OBJECT_NOT_FOUND,
4792 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4793 aDevice, aControllerPort, aName.c_str());
4794
4795 /* Remember previously mounted medium. The medium before taking the
4796 * backup is not necessarily the same thing. */
4797 ComObjPtr<Medium> oldmedium;
4798 oldmedium = pAttach->i_getMedium();
4799
4800 IMedium *iM = aMedium;
4801 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4802 if (aMedium && pMedium.isNull())
4803 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4804
4805 AutoCaller mediumCaller(pMedium);
4806 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4807
4808 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4809 if (pMedium)
4810 {
4811 DeviceType_T mediumType = pAttach->i_getType();
4812 switch (mediumType)
4813 {
4814 case DeviceType_DVD:
4815 case DeviceType_Floppy:
4816 break;
4817
4818 default:
4819 return setError(VBOX_E_INVALID_OBJECT_STATE,
4820 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4821 aControllerPort,
4822 aDevice,
4823 aName.c_str());
4824 }
4825 }
4826
4827 i_setModified(IsModified_Storage);
4828 mMediumAttachments.backup();
4829
4830 {
4831 // The backup operation makes the pAttach reference point to the
4832 // old settings. Re-get the correct reference.
4833 pAttach = i_findAttachment(*mMediumAttachments.data(),
4834 aName,
4835 aControllerPort,
4836 aDevice);
4837 if (!oldmedium.isNull())
4838 oldmedium->i_removeBackReference(mData->mUuid);
4839 if (!pMedium.isNull())
4840 {
4841 pMedium->i_addBackReference(mData->mUuid);
4842
4843 mediumLock.release();
4844 multiLock.release();
4845 i_addMediumToRegistry(pMedium);
4846 multiLock.acquire();
4847 mediumLock.acquire();
4848 }
4849
4850 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4851 pAttach->i_updateMedium(pMedium);
4852 }
4853
4854 i_setModified(IsModified_Storage);
4855
4856 mediumLock.release();
4857 multiLock.release();
4858 rc = i_onMediumChange(pAttach, aForce);
4859 multiLock.acquire();
4860 mediumLock.acquire();
4861
4862 /* On error roll back this change only. */
4863 if (FAILED(rc))
4864 {
4865 if (!pMedium.isNull())
4866 pMedium->i_removeBackReference(mData->mUuid);
4867 pAttach = i_findAttachment(*mMediumAttachments.data(),
4868 aName,
4869 aControllerPort,
4870 aDevice);
4871 /* If the attachment is gone in the meantime, bail out. */
4872 if (pAttach.isNull())
4873 return rc;
4874 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4875 if (!oldmedium.isNull())
4876 oldmedium->i_addBackReference(mData->mUuid);
4877 pAttach->i_updateMedium(oldmedium);
4878 }
4879
4880 mediumLock.release();
4881 multiLock.release();
4882
4883 /* Save modified registries, but skip this machine as it's the caller's
4884 * job to save its settings like all other settings changes. */
4885 mParent->i_unmarkRegistryModified(i_getId());
4886 mParent->i_saveModifiedRegistries();
4887
4888 return rc;
4889}
4890HRESULT Machine::getMedium(const com::Utf8Str &aName,
4891 LONG aControllerPort,
4892 LONG aDevice,
4893 ComPtr<IMedium> &aMedium)
4894{
4895 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4896 aName.c_str(), aControllerPort, aDevice));
4897
4898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4899
4900 aMedium = NULL;
4901
4902 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4903 aName,
4904 aControllerPort,
4905 aDevice);
4906 if (pAttach.isNull())
4907 return setError(VBOX_E_OBJECT_NOT_FOUND,
4908 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4909 aDevice, aControllerPort, aName.c_str());
4910
4911 aMedium = pAttach->i_getMedium();
4912
4913 return S_OK;
4914}
4915
4916HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4917{
4918
4919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4920
4921 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4922
4923 return S_OK;
4924}
4925
4926HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4927{
4928 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4929
4930 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4931
4932 return S_OK;
4933}
4934
4935HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4936{
4937 /* Do not assert if slot is out of range, just return the advertised
4938 status. testdriver/vbox.py triggers this in logVmInfo. */
4939 if (aSlot >= mNetworkAdapters.size())
4940 return setError(E_INVALIDARG,
4941 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4942 aSlot, mNetworkAdapters.size());
4943
4944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4945
4946 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4947
4948 return S_OK;
4949}
4950
4951HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4952{
4953 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4954
4955 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4956 size_t i = 0;
4957 for (settings::StringsMap::const_iterator
4958 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4959 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4960 ++it, ++i)
4961 aKeys[i] = it->first;
4962
4963 return S_OK;
4964}
4965
4966 /**
4967 * @note Locks this object for reading.
4968 */
4969HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4970 com::Utf8Str &aValue)
4971{
4972 /* start with nothing found */
4973 aValue = "";
4974
4975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4976
4977 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4978 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4979 // found:
4980 aValue = it->second; // source is a Utf8Str
4981
4982 /* return the result to caller (may be empty) */
4983 return S_OK;
4984}
4985
4986 /**
4987 * @note Locks mParent for writing + this object for writing.
4988 */
4989HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4990{
4991 Utf8Str strOldValue; // empty
4992
4993 // locking note: we only hold the read lock briefly to look up the old value,
4994 // then release it and call the onExtraCanChange callbacks. There is a small
4995 // chance of a race insofar as the callback might be called twice if two callers
4996 // change the same key at the same time, but that's a much better solution
4997 // than the deadlock we had here before. The actual changing of the extradata
4998 // is then performed under the write lock and race-free.
4999
5000 // look up the old value first; if nothing has changed then we need not do anything
5001 {
5002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5003
5004 // For snapshots don't even think about allowing changes, extradata
5005 // is global for a machine, so there is nothing snapshot specific.
5006 if (i_isSnapshotMachine())
5007 return setError(VBOX_E_INVALID_VM_STATE,
5008 tr("Cannot set extradata for a snapshot"));
5009
5010 // check if the right IMachine instance is used
5011 if (mData->mRegistered && !i_isSessionMachine())
5012 return setError(VBOX_E_INVALID_VM_STATE,
5013 tr("Cannot set extradata for an immutable machine"));
5014
5015 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5016 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5017 strOldValue = it->second;
5018 }
5019
5020 bool fChanged;
5021 if ((fChanged = (strOldValue != aValue)))
5022 {
5023 // ask for permission from all listeners outside the locks;
5024 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5025 // lock to copy the list of callbacks to invoke
5026 Bstr error;
5027 Bstr bstrValue(aValue);
5028
5029 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5030 {
5031 const char *sep = error.isEmpty() ? "" : ": ";
5032 CBSTR err = error.raw();
5033 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5034 return setError(E_ACCESSDENIED,
5035 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5036 aKey.c_str(),
5037 aValue.c_str(),
5038 sep,
5039 err);
5040 }
5041
5042 // data is changing and change not vetoed: then write it out under the lock
5043 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5044
5045 if (aValue.isEmpty())
5046 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5047 else
5048 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5049 // creates a new key if needed
5050
5051 bool fNeedsGlobalSaveSettings = false;
5052 // This saving of settings is tricky: there is no "old state" for the
5053 // extradata items at all (unlike all other settings), so the old/new
5054 // settings comparison would give a wrong result!
5055 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5056
5057 if (fNeedsGlobalSaveSettings)
5058 {
5059 // save the global settings; for that we should hold only the VirtualBox lock
5060 alock.release();
5061 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5062 mParent->i_saveSettings();
5063 }
5064 }
5065
5066 // fire notification outside the lock
5067 if (fChanged)
5068 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5069
5070 return S_OK;
5071}
5072
5073HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5074{
5075 aProgress = NULL;
5076 NOREF(aSettingsFilePath);
5077 ReturnComNotImplemented();
5078}
5079
5080HRESULT Machine::saveSettings()
5081{
5082 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5083
5084 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5085 if (FAILED(rc)) return rc;
5086
5087 /* the settings file path may never be null */
5088 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5089
5090 /* save all VM data excluding snapshots */
5091 bool fNeedsGlobalSaveSettings = false;
5092 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5093 mlock.release();
5094
5095 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5096 {
5097 // save the global settings; for that we should hold only the VirtualBox lock
5098 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5099 rc = mParent->i_saveSettings();
5100 }
5101
5102 return rc;
5103}
5104
5105
5106HRESULT Machine::discardSettings()
5107{
5108 /*
5109 * We need to take the machine list lock here as well as the machine one
5110 * or we'll get into trouble should any media stuff require rolling back.
5111 *
5112 * Details:
5113 *
5114 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5115 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5116 * 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]
5117 * 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
5118 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5119 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5120 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5121 * 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
5122 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5123 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5124 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5125 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5126 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5127 * 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]
5128 * 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] (*)
5129 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5130 * 0:005> k
5131 * # Child-SP RetAddr Call Site
5132 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5133 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5134 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5135 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5136 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5137 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5138 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5139 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5140 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5141 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5142 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5143 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5144 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5145 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5146 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5147 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5148 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5149 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5150 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5151 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5152 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5153 *
5154 */
5155 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5159 if (FAILED(rc)) return rc;
5160
5161 /*
5162 * during this rollback, the session will be notified if data has
5163 * been actually changed
5164 */
5165 i_rollback(true /* aNotify */);
5166
5167 return S_OK;
5168}
5169
5170/** @note Locks objects! */
5171HRESULT Machine::unregister(AutoCaller &autoCaller,
5172 CleanupMode_T aCleanupMode,
5173 std::vector<ComPtr<IMedium> > &aMedia)
5174{
5175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5176
5177 Guid id(i_getId());
5178
5179 if (mData->mSession.mState != SessionState_Unlocked)
5180 return setError(VBOX_E_INVALID_OBJECT_STATE,
5181 tr("Cannot unregister the machine '%s' while it is locked"),
5182 mUserData->s.strName.c_str());
5183
5184 // wait for state dependents to drop to zero
5185 i_ensureNoStateDependencies();
5186
5187 if (!mData->mAccessible)
5188 {
5189 // inaccessible maschines can only be unregistered; uninitialize ourselves
5190 // here because currently there may be no unregistered that are inaccessible
5191 // (this state combination is not supported). Note releasing the caller and
5192 // leaving the lock before calling uninit()
5193 alock.release();
5194 autoCaller.release();
5195
5196 uninit();
5197
5198 mParent->i_unregisterMachine(this, id);
5199 // calls VirtualBox::i_saveSettings()
5200
5201 return S_OK;
5202 }
5203
5204 HRESULT rc = S_OK;
5205
5206 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5207 // discard saved state
5208 if (mData->mMachineState == MachineState_Saved)
5209 {
5210 // add the saved state file to the list of files the caller should delete
5211 Assert(!mSSData->strStateFilePath.isEmpty());
5212 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5213
5214 mSSData->strStateFilePath.setNull();
5215
5216 // unconditionally set the machine state to powered off, we now
5217 // know no session has locked the machine
5218 mData->mMachineState = MachineState_PoweredOff;
5219 }
5220
5221 size_t cSnapshots = 0;
5222 if (mData->mFirstSnapshot)
5223 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5224 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5225 // fail now before we start detaching media
5226 return setError(VBOX_E_INVALID_OBJECT_STATE,
5227 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5228 mUserData->s.strName.c_str(), cSnapshots);
5229
5230 // This list collects the medium objects from all medium attachments
5231 // which we will detach from the machine and its snapshots, in a specific
5232 // order which allows for closing all media without getting "media in use"
5233 // errors, simply by going through the list from the front to the back:
5234 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5235 // and must be closed before the parent media from the snapshots, or closing the parents
5236 // will fail because they still have children);
5237 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5238 // the root ("first") snapshot of the machine.
5239 MediaList llMedia;
5240
5241 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5242 && mMediumAttachments->size()
5243 )
5244 {
5245 // we have media attachments: detach them all and add the Medium objects to our list
5246 if (aCleanupMode != CleanupMode_UnregisterOnly)
5247 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5248 else
5249 return setError(VBOX_E_INVALID_OBJECT_STATE,
5250 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5251 mUserData->s.strName.c_str(), mMediumAttachments->size());
5252 }
5253
5254 if (cSnapshots)
5255 {
5256 // add the media from the medium attachments of the snapshots to llMedia
5257 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5258 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5259 // into the children first
5260
5261 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5262 MachineState_T oldState = mData->mMachineState;
5263 mData->mMachineState = MachineState_DeletingSnapshot;
5264
5265 // make a copy of the first snapshot so the refcount does not drop to 0
5266 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5267 // because of the AutoCaller voodoo)
5268 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5269
5270 // GO!
5271 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5272
5273 mData->mMachineState = oldState;
5274 }
5275
5276 if (FAILED(rc))
5277 {
5278 i_rollbackMedia();
5279 return rc;
5280 }
5281
5282 // commit all the media changes made above
5283 i_commitMedia();
5284
5285 mData->mRegistered = false;
5286
5287 // machine lock no longer needed
5288 alock.release();
5289
5290 // return media to caller
5291 aMedia.resize(llMedia.size());
5292 size_t i = 0;
5293 for (MediaList::const_iterator
5294 it = llMedia.begin();
5295 it != llMedia.end();
5296 ++it, ++i)
5297 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5298
5299 mParent->i_unregisterMachine(this, id);
5300 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5301
5302 return S_OK;
5303}
5304
5305/**
5306 * Task record for deleting a machine config.
5307 */
5308class Machine::DeleteConfigTask
5309 : public Machine::Task
5310{
5311public:
5312 DeleteConfigTask(Machine *m,
5313 Progress *p,
5314 const Utf8Str &t,
5315 const RTCList<ComPtr<IMedium> > &llMediums,
5316 const StringsList &llFilesToDelete)
5317 : Task(m, p, t),
5318 m_llMediums(llMediums),
5319 m_llFilesToDelete(llFilesToDelete)
5320 {}
5321
5322private:
5323 void handler()
5324 {
5325 try
5326 {
5327 m_pMachine->i_deleteConfigHandler(*this);
5328 }
5329 catch (...)
5330 {
5331 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5332 }
5333 }
5334
5335 RTCList<ComPtr<IMedium> > m_llMediums;
5336 StringsList m_llFilesToDelete;
5337
5338 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5339};
5340
5341/**
5342 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5343 * SessionMachine::taskHandler().
5344 *
5345 * @note Locks this object for writing.
5346 *
5347 * @param task
5348 * @return
5349 */
5350void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5351{
5352 LogFlowThisFuncEnter();
5353
5354 AutoCaller autoCaller(this);
5355 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5356 if (FAILED(autoCaller.rc()))
5357 {
5358 /* we might have been uninitialized because the session was accidentally
5359 * closed by the client, so don't assert */
5360 HRESULT rc = setError(E_FAIL,
5361 tr("The session has been accidentally closed"));
5362 task.m_pProgress->i_notifyComplete(rc);
5363 LogFlowThisFuncLeave();
5364 return;
5365 }
5366
5367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5368
5369 HRESULT rc = S_OK;
5370
5371 try
5372 {
5373 ULONG uLogHistoryCount = 3;
5374 ComPtr<ISystemProperties> systemProperties;
5375 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5376 if (FAILED(rc)) throw rc;
5377
5378 if (!systemProperties.isNull())
5379 {
5380 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5381 if (FAILED(rc)) throw rc;
5382 }
5383
5384 MachineState_T oldState = mData->mMachineState;
5385 i_setMachineState(MachineState_SettingUp);
5386 alock.release();
5387 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5388 {
5389 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5390 {
5391 AutoCaller mac(pMedium);
5392 if (FAILED(mac.rc())) throw mac.rc();
5393 Utf8Str strLocation = pMedium->i_getLocationFull();
5394 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5395 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5396 if (FAILED(rc)) throw rc;
5397 }
5398 if (pMedium->i_isMediumFormatFile())
5399 {
5400 ComPtr<IProgress> pProgress2;
5401 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5402 if (FAILED(rc)) throw rc;
5403 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5404 if (FAILED(rc)) throw rc;
5405 /* Check the result of the asynchronous process. */
5406 LONG iRc;
5407 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5408 if (FAILED(rc)) throw rc;
5409 /* If the thread of the progress object has an error, then
5410 * retrieve the error info from there, or it'll be lost. */
5411 if (FAILED(iRc))
5412 throw setError(ProgressErrorInfo(pProgress2));
5413 }
5414
5415 /* Close the medium, deliberately without checking the return
5416 * code, and without leaving any trace in the error info, as
5417 * a failure here is a very minor issue, which shouldn't happen
5418 * as above we even managed to delete the medium. */
5419 {
5420 ErrorInfoKeeper eik;
5421 pMedium->Close();
5422 }
5423 }
5424 i_setMachineState(oldState);
5425 alock.acquire();
5426
5427 // delete the files pushed on the task list by Machine::Delete()
5428 // (this includes saved states of the machine and snapshots and
5429 // medium storage files from the IMedium list passed in, and the
5430 // machine XML file)
5431 for (StringsList::const_iterator
5432 it = task.m_llFilesToDelete.begin();
5433 it != task.m_llFilesToDelete.end();
5434 ++it)
5435 {
5436 const Utf8Str &strFile = *it;
5437 LogFunc(("Deleting file %s\n", strFile.c_str()));
5438 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5439 if (FAILED(rc)) throw rc;
5440
5441 int vrc = RTFileDelete(strFile.c_str());
5442 if (RT_FAILURE(vrc))
5443 throw setError(VBOX_E_IPRT_ERROR,
5444 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5445 }
5446
5447 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5448 if (FAILED(rc)) throw rc;
5449
5450 /* delete the settings only when the file actually exists */
5451 if (mData->pMachineConfigFile->fileExists())
5452 {
5453 /* Delete any backup or uncommitted XML files. Ignore failures.
5454 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5455 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5456 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5457 RTFileDelete(otherXml.c_str());
5458 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5459 RTFileDelete(otherXml.c_str());
5460
5461 /* delete the Logs folder, nothing important should be left
5462 * there (we don't check for errors because the user might have
5463 * some private files there that we don't want to delete) */
5464 Utf8Str logFolder;
5465 getLogFolder(logFolder);
5466 Assert(logFolder.length());
5467 if (RTDirExists(logFolder.c_str()))
5468 {
5469 /* Delete all VBox.log[.N] files from the Logs folder
5470 * (this must be in sync with the rotation logic in
5471 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5472 * files that may have been created by the GUI. */
5473 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5474 logFolder.c_str(), RTPATH_DELIMITER);
5475 RTFileDelete(log.c_str());
5476 log = Utf8StrFmt("%s%cVBox.png",
5477 logFolder.c_str(), RTPATH_DELIMITER);
5478 RTFileDelete(log.c_str());
5479 for (int i = uLogHistoryCount; i > 0; i--)
5480 {
5481 log = Utf8StrFmt("%s%cVBox.log.%d",
5482 logFolder.c_str(), RTPATH_DELIMITER, i);
5483 RTFileDelete(log.c_str());
5484 log = Utf8StrFmt("%s%cVBox.png.%d",
5485 logFolder.c_str(), RTPATH_DELIMITER, i);
5486 RTFileDelete(log.c_str());
5487 }
5488#if defined(RT_OS_WINDOWS)
5489 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5490 RTFileDelete(log.c_str());
5491 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5492 RTFileDelete(log.c_str());
5493#endif
5494
5495 RTDirRemove(logFolder.c_str());
5496 }
5497
5498 /* delete the Snapshots folder, nothing important should be left
5499 * there (we don't check for errors because the user might have
5500 * some private files there that we don't want to delete) */
5501 Utf8Str strFullSnapshotFolder;
5502 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5503 Assert(!strFullSnapshotFolder.isEmpty());
5504 if (RTDirExists(strFullSnapshotFolder.c_str()))
5505 RTDirRemove(strFullSnapshotFolder.c_str());
5506
5507 // delete the directory that contains the settings file, but only
5508 // if it matches the VM name
5509 Utf8Str settingsDir;
5510 if (i_isInOwnDir(&settingsDir))
5511 RTDirRemove(settingsDir.c_str());
5512 }
5513
5514 alock.release();
5515
5516 mParent->i_saveModifiedRegistries();
5517 }
5518 catch (HRESULT aRC) { rc = aRC; }
5519
5520 task.m_pProgress->i_notifyComplete(rc);
5521
5522 LogFlowThisFuncLeave();
5523}
5524
5525HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5526{
5527 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5528
5529 HRESULT rc = i_checkStateDependency(MutableStateDep);
5530 if (FAILED(rc)) return rc;
5531
5532 if (mData->mRegistered)
5533 return setError(VBOX_E_INVALID_VM_STATE,
5534 tr("Cannot delete settings of a registered machine"));
5535
5536 // collect files to delete
5537 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5538 if (mData->pMachineConfigFile->fileExists())
5539 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5540
5541 RTCList<ComPtr<IMedium> > llMediums;
5542 for (size_t i = 0; i < aMedia.size(); ++i)
5543 {
5544 IMedium *pIMedium(aMedia[i]);
5545 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5546 if (pMedium.isNull())
5547 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5548 SafeArray<BSTR> ids;
5549 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5550 if (FAILED(rc)) return rc;
5551 /* At this point the medium should not have any back references
5552 * anymore. If it has it is attached to another VM and *must* not
5553 * deleted. */
5554 if (ids.size() < 1)
5555 llMediums.append(pMedium);
5556 }
5557
5558 ComObjPtr<Progress> pProgress;
5559 pProgress.createObject();
5560 rc = pProgress->init(i_getVirtualBox(),
5561 static_cast<IMachine*>(this) /* aInitiator */,
5562 tr("Deleting files"),
5563 true /* fCancellable */,
5564 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5565 tr("Collecting file inventory"));
5566 if (FAILED(rc))
5567 return rc;
5568
5569 /* create and start the task on a separate thread (note that it will not
5570 * start working until we release alock) */
5571 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5572 rc = pTask->createThread();
5573 if (FAILED(rc))
5574 return rc;
5575
5576 pProgress.queryInterfaceTo(aProgress.asOutParam());
5577
5578 LogFlowFuncLeave();
5579
5580 return S_OK;
5581}
5582
5583HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5584{
5585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5586
5587 ComObjPtr<Snapshot> pSnapshot;
5588 HRESULT rc;
5589
5590 if (aNameOrId.isEmpty())
5591 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5592 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5593 else
5594 {
5595 Guid uuid(aNameOrId);
5596 if (uuid.isValid())
5597 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5598 else
5599 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5600 }
5601 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5602
5603 return rc;
5604}
5605
5606HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5607{
5608 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5609
5610 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5611 if (FAILED(rc)) return rc;
5612
5613 ComObjPtr<SharedFolder> sharedFolder;
5614 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5615 if (SUCCEEDED(rc))
5616 return setError(VBOX_E_OBJECT_IN_USE,
5617 tr("Shared folder named '%s' already exists"),
5618 aName.c_str());
5619
5620 sharedFolder.createObject();
5621 rc = sharedFolder->init(i_getMachine(),
5622 aName,
5623 aHostPath,
5624 !!aWritable,
5625 !!aAutomount,
5626 true /* fFailOnError */);
5627 if (FAILED(rc)) return rc;
5628
5629 i_setModified(IsModified_SharedFolders);
5630 mHWData.backup();
5631 mHWData->mSharedFolders.push_back(sharedFolder);
5632
5633 /* inform the direct session if any */
5634 alock.release();
5635 i_onSharedFolderChange();
5636
5637 return S_OK;
5638}
5639
5640HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5641{
5642 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5643
5644 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5645 if (FAILED(rc)) return rc;
5646
5647 ComObjPtr<SharedFolder> sharedFolder;
5648 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5649 if (FAILED(rc)) return rc;
5650
5651 i_setModified(IsModified_SharedFolders);
5652 mHWData.backup();
5653 mHWData->mSharedFolders.remove(sharedFolder);
5654
5655 /* inform the direct session if any */
5656 alock.release();
5657 i_onSharedFolderChange();
5658
5659 return S_OK;
5660}
5661
5662HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5663{
5664 /* start with No */
5665 *aCanShow = FALSE;
5666
5667 ComPtr<IInternalSessionControl> directControl;
5668 {
5669 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5670
5671 if (mData->mSession.mState != SessionState_Locked)
5672 return setError(VBOX_E_INVALID_VM_STATE,
5673 tr("Machine is not locked for session (session state: %s)"),
5674 Global::stringifySessionState(mData->mSession.mState));
5675
5676 if (mData->mSession.mLockType == LockType_VM)
5677 directControl = mData->mSession.mDirectControl;
5678 }
5679
5680 /* ignore calls made after #OnSessionEnd() is called */
5681 if (!directControl)
5682 return S_OK;
5683
5684 LONG64 dummy;
5685 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5686}
5687
5688HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5689{
5690 ComPtr<IInternalSessionControl> directControl;
5691 {
5692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5693
5694 if (mData->mSession.mState != SessionState_Locked)
5695 return setError(E_FAIL,
5696 tr("Machine is not locked for session (session state: %s)"),
5697 Global::stringifySessionState(mData->mSession.mState));
5698
5699 if (mData->mSession.mLockType == LockType_VM)
5700 directControl = mData->mSession.mDirectControl;
5701 }
5702
5703 /* ignore calls made after #OnSessionEnd() is called */
5704 if (!directControl)
5705 return S_OK;
5706
5707 BOOL dummy;
5708 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5709}
5710
5711#ifdef VBOX_WITH_GUEST_PROPS
5712/**
5713 * Look up a guest property in VBoxSVC's internal structures.
5714 */
5715HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5716 com::Utf8Str &aValue,
5717 LONG64 *aTimestamp,
5718 com::Utf8Str &aFlags) const
5719{
5720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5721
5722 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5723 if (it != mHWData->mGuestProperties.end())
5724 {
5725 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5726 aValue = it->second.strValue;
5727 *aTimestamp = it->second.mTimestamp;
5728 GuestPropWriteFlags(it->second.mFlags, szFlags);
5729 aFlags = Utf8Str(szFlags);
5730 }
5731
5732 return S_OK;
5733}
5734
5735/**
5736 * Query the VM that a guest property belongs to for the property.
5737 * @returns E_ACCESSDENIED if the VM process is not available or not
5738 * currently handling queries and the lookup should then be done in
5739 * VBoxSVC.
5740 */
5741HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5742 com::Utf8Str &aValue,
5743 LONG64 *aTimestamp,
5744 com::Utf8Str &aFlags) const
5745{
5746 HRESULT rc = S_OK;
5747 BSTR bValue = NULL;
5748 BSTR bFlags = NULL;
5749
5750 ComPtr<IInternalSessionControl> directControl;
5751 {
5752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5753 if (mData->mSession.mLockType == LockType_VM)
5754 directControl = mData->mSession.mDirectControl;
5755 }
5756
5757 /* ignore calls made after #OnSessionEnd() is called */
5758 if (!directControl)
5759 rc = E_ACCESSDENIED;
5760 else
5761 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5762 0 /* accessMode */,
5763 &bValue, aTimestamp, &bFlags);
5764
5765 aValue = bValue;
5766 aFlags = bFlags;
5767
5768 return rc;
5769}
5770#endif // VBOX_WITH_GUEST_PROPS
5771
5772HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5773 com::Utf8Str &aValue,
5774 LONG64 *aTimestamp,
5775 com::Utf8Str &aFlags)
5776{
5777#ifndef VBOX_WITH_GUEST_PROPS
5778 ReturnComNotImplemented();
5779#else // VBOX_WITH_GUEST_PROPS
5780
5781 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5782
5783 if (rc == E_ACCESSDENIED)
5784 /* The VM is not running or the service is not (yet) accessible */
5785 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5786 return rc;
5787#endif // VBOX_WITH_GUEST_PROPS
5788}
5789
5790HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5791{
5792 LONG64 dummyTimestamp;
5793 com::Utf8Str dummyFlags;
5794 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5795 return rc;
5796
5797}
5798HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5799{
5800 com::Utf8Str dummyFlags;
5801 com::Utf8Str dummyValue;
5802 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5803 return rc;
5804}
5805
5806#ifdef VBOX_WITH_GUEST_PROPS
5807/**
5808 * Set a guest property in VBoxSVC's internal structures.
5809 */
5810HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5811 const com::Utf8Str &aFlags, bool fDelete)
5812{
5813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5814 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5815 if (FAILED(rc)) return rc;
5816
5817 try
5818 {
5819 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5820 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5821 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5822
5823 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5824 if (it == mHWData->mGuestProperties.end())
5825 {
5826 if (!fDelete)
5827 {
5828 i_setModified(IsModified_MachineData);
5829 mHWData.backupEx();
5830
5831 RTTIMESPEC time;
5832 HWData::GuestProperty prop;
5833 prop.strValue = Bstr(aValue).raw();
5834 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5835 prop.mFlags = fFlags;
5836 mHWData->mGuestProperties[aName] = prop;
5837 }
5838 }
5839 else
5840 {
5841 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5842 {
5843 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5844 }
5845 else
5846 {
5847 i_setModified(IsModified_MachineData);
5848 mHWData.backupEx();
5849
5850 /* The backupEx() operation invalidates our iterator,
5851 * so get a new one. */
5852 it = mHWData->mGuestProperties.find(aName);
5853 Assert(it != mHWData->mGuestProperties.end());
5854
5855 if (!fDelete)
5856 {
5857 RTTIMESPEC time;
5858 it->second.strValue = aValue;
5859 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5860 it->second.mFlags = fFlags;
5861 }
5862 else
5863 mHWData->mGuestProperties.erase(it);
5864 }
5865 }
5866
5867 if (SUCCEEDED(rc))
5868 {
5869 alock.release();
5870
5871 mParent->i_onGuestPropertyChange(mData->mUuid,
5872 Bstr(aName).raw(),
5873 Bstr(aValue).raw(),
5874 Bstr(aFlags).raw());
5875 }
5876 }
5877 catch (std::bad_alloc &)
5878 {
5879 rc = E_OUTOFMEMORY;
5880 }
5881
5882 return rc;
5883}
5884
5885/**
5886 * Set a property on the VM that that property belongs to.
5887 * @returns E_ACCESSDENIED if the VM process is not available or not
5888 * currently handling queries and the setting should then be done in
5889 * VBoxSVC.
5890 */
5891HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5892 const com::Utf8Str &aFlags, bool fDelete)
5893{
5894 HRESULT rc;
5895
5896 try
5897 {
5898 ComPtr<IInternalSessionControl> directControl;
5899 {
5900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5901 if (mData->mSession.mLockType == LockType_VM)
5902 directControl = mData->mSession.mDirectControl;
5903 }
5904
5905 BSTR dummy = NULL; /* will not be changed (setter) */
5906 LONG64 dummy64;
5907 if (!directControl)
5908 rc = E_ACCESSDENIED;
5909 else
5910 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5911 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5912 fDelete? 2: 1 /* accessMode */,
5913 &dummy, &dummy64, &dummy);
5914 }
5915 catch (std::bad_alloc &)
5916 {
5917 rc = E_OUTOFMEMORY;
5918 }
5919
5920 return rc;
5921}
5922#endif // VBOX_WITH_GUEST_PROPS
5923
5924HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5925 const com::Utf8Str &aFlags)
5926{
5927#ifndef VBOX_WITH_GUEST_PROPS
5928 ReturnComNotImplemented();
5929#else // VBOX_WITH_GUEST_PROPS
5930 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5931 if (rc == E_ACCESSDENIED)
5932 /* The VM is not running or the service is not (yet) accessible */
5933 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5934 return rc;
5935#endif // VBOX_WITH_GUEST_PROPS
5936}
5937
5938HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5939{
5940 return setGuestProperty(aProperty, aValue, "");
5941}
5942
5943HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5944{
5945#ifndef VBOX_WITH_GUEST_PROPS
5946 ReturnComNotImplemented();
5947#else // VBOX_WITH_GUEST_PROPS
5948 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5949 if (rc == E_ACCESSDENIED)
5950 /* The VM is not running or the service is not (yet) accessible */
5951 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5952 return rc;
5953#endif // VBOX_WITH_GUEST_PROPS
5954}
5955
5956#ifdef VBOX_WITH_GUEST_PROPS
5957/**
5958 * Enumerate the guest properties in VBoxSVC's internal structures.
5959 */
5960HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5961 std::vector<com::Utf8Str> &aNames,
5962 std::vector<com::Utf8Str> &aValues,
5963 std::vector<LONG64> &aTimestamps,
5964 std::vector<com::Utf8Str> &aFlags)
5965{
5966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5967 Utf8Str strPatterns(aPatterns);
5968
5969 /*
5970 * Look for matching patterns and build up a list.
5971 */
5972 HWData::GuestPropertyMap propMap;
5973 for (HWData::GuestPropertyMap::const_iterator
5974 it = mHWData->mGuestProperties.begin();
5975 it != mHWData->mGuestProperties.end();
5976 ++it)
5977 {
5978 if ( strPatterns.isEmpty()
5979 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5980 RTSTR_MAX,
5981 it->first.c_str(),
5982 RTSTR_MAX,
5983 NULL)
5984 )
5985 propMap.insert(*it);
5986 }
5987
5988 alock.release();
5989
5990 /*
5991 * And build up the arrays for returning the property information.
5992 */
5993 size_t cEntries = propMap.size();
5994
5995 aNames.resize(cEntries);
5996 aValues.resize(cEntries);
5997 aTimestamps.resize(cEntries);
5998 aFlags.resize(cEntries);
5999
6000 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6001 size_t i = 0;
6002 for (HWData::GuestPropertyMap::const_iterator
6003 it = propMap.begin();
6004 it != propMap.end();
6005 ++it, ++i)
6006 {
6007 aNames[i] = it->first;
6008 aValues[i] = it->second.strValue;
6009 aTimestamps[i] = it->second.mTimestamp;
6010 GuestPropWriteFlags(it->second.mFlags, szFlags);
6011 aFlags[i] = Utf8Str(szFlags);
6012 }
6013
6014 return S_OK;
6015}
6016
6017/**
6018 * Enumerate the properties managed by a VM.
6019 * @returns E_ACCESSDENIED if the VM process is not available or not
6020 * currently handling queries and the setting should then be done in
6021 * VBoxSVC.
6022 */
6023HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6024 std::vector<com::Utf8Str> &aNames,
6025 std::vector<com::Utf8Str> &aValues,
6026 std::vector<LONG64> &aTimestamps,
6027 std::vector<com::Utf8Str> &aFlags)
6028{
6029 HRESULT rc;
6030 ComPtr<IInternalSessionControl> directControl;
6031 {
6032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6033 if (mData->mSession.mLockType == LockType_VM)
6034 directControl = mData->mSession.mDirectControl;
6035 }
6036
6037 com::SafeArray<BSTR> bNames;
6038 com::SafeArray<BSTR> bValues;
6039 com::SafeArray<LONG64> bTimestamps;
6040 com::SafeArray<BSTR> bFlags;
6041
6042 if (!directControl)
6043 rc = E_ACCESSDENIED;
6044 else
6045 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6046 ComSafeArrayAsOutParam(bNames),
6047 ComSafeArrayAsOutParam(bValues),
6048 ComSafeArrayAsOutParam(bTimestamps),
6049 ComSafeArrayAsOutParam(bFlags));
6050 size_t i;
6051 aNames.resize(bNames.size());
6052 for (i = 0; i < bNames.size(); ++i)
6053 aNames[i] = Utf8Str(bNames[i]);
6054 aValues.resize(bValues.size());
6055 for (i = 0; i < bValues.size(); ++i)
6056 aValues[i] = Utf8Str(bValues[i]);
6057 aTimestamps.resize(bTimestamps.size());
6058 for (i = 0; i < bTimestamps.size(); ++i)
6059 aTimestamps[i] = bTimestamps[i];
6060 aFlags.resize(bFlags.size());
6061 for (i = 0; i < bFlags.size(); ++i)
6062 aFlags[i] = Utf8Str(bFlags[i]);
6063
6064 return rc;
6065}
6066#endif // VBOX_WITH_GUEST_PROPS
6067HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6068 std::vector<com::Utf8Str> &aNames,
6069 std::vector<com::Utf8Str> &aValues,
6070 std::vector<LONG64> &aTimestamps,
6071 std::vector<com::Utf8Str> &aFlags)
6072{
6073#ifndef VBOX_WITH_GUEST_PROPS
6074 ReturnComNotImplemented();
6075#else // VBOX_WITH_GUEST_PROPS
6076
6077 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6078
6079 if (rc == E_ACCESSDENIED)
6080 /* The VM is not running or the service is not (yet) accessible */
6081 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6082 return rc;
6083#endif // VBOX_WITH_GUEST_PROPS
6084}
6085
6086HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6087 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6088{
6089 MediumAttachmentList atts;
6090
6091 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6092 if (FAILED(rc)) return rc;
6093
6094 aMediumAttachments.resize(atts.size());
6095 size_t i = 0;
6096 for (MediumAttachmentList::const_iterator
6097 it = atts.begin();
6098 it != atts.end();
6099 ++it, ++i)
6100 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6101
6102 return S_OK;
6103}
6104
6105HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6106 LONG aControllerPort,
6107 LONG aDevice,
6108 ComPtr<IMediumAttachment> &aAttachment)
6109{
6110 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6111 aName.c_str(), aControllerPort, aDevice));
6112
6113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6114
6115 aAttachment = NULL;
6116
6117 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6118 aName,
6119 aControllerPort,
6120 aDevice);
6121 if (pAttach.isNull())
6122 return setError(VBOX_E_OBJECT_NOT_FOUND,
6123 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6124 aDevice, aControllerPort, aName.c_str());
6125
6126 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6127
6128 return S_OK;
6129}
6130
6131
6132HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6133 StorageBus_T aConnectionType,
6134 ComPtr<IStorageController> &aController)
6135{
6136 if ( (aConnectionType <= StorageBus_Null)
6137 || (aConnectionType > StorageBus_PCIe))
6138 return setError(E_INVALIDARG,
6139 tr("Invalid connection type: %d"),
6140 aConnectionType);
6141
6142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6143
6144 HRESULT rc = i_checkStateDependency(MutableStateDep);
6145 if (FAILED(rc)) return rc;
6146
6147 /* try to find one with the name first. */
6148 ComObjPtr<StorageController> ctrl;
6149
6150 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6151 if (SUCCEEDED(rc))
6152 return setError(VBOX_E_OBJECT_IN_USE,
6153 tr("Storage controller named '%s' already exists"),
6154 aName.c_str());
6155
6156 ctrl.createObject();
6157
6158 /* get a new instance number for the storage controller */
6159 ULONG ulInstance = 0;
6160 bool fBootable = true;
6161 for (StorageControllerList::const_iterator
6162 it = mStorageControllers->begin();
6163 it != mStorageControllers->end();
6164 ++it)
6165 {
6166 if ((*it)->i_getStorageBus() == aConnectionType)
6167 {
6168 ULONG ulCurInst = (*it)->i_getInstance();
6169
6170 if (ulCurInst >= ulInstance)
6171 ulInstance = ulCurInst + 1;
6172
6173 /* Only one controller of each type can be marked as bootable. */
6174 if ((*it)->i_getBootable())
6175 fBootable = false;
6176 }
6177 }
6178
6179 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6180 if (FAILED(rc)) return rc;
6181
6182 i_setModified(IsModified_Storage);
6183 mStorageControllers.backup();
6184 mStorageControllers->push_back(ctrl);
6185
6186 ctrl.queryInterfaceTo(aController.asOutParam());
6187
6188 /* inform the direct session if any */
6189 alock.release();
6190 i_onStorageControllerChange();
6191
6192 return S_OK;
6193}
6194
6195HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6196 ComPtr<IStorageController> &aStorageController)
6197{
6198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6199
6200 ComObjPtr<StorageController> ctrl;
6201
6202 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6203 if (SUCCEEDED(rc))
6204 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6205
6206 return rc;
6207}
6208
6209HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6210 ULONG aInstance,
6211 ComPtr<IStorageController> &aStorageController)
6212{
6213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6214
6215 for (StorageControllerList::const_iterator
6216 it = mStorageControllers->begin();
6217 it != mStorageControllers->end();
6218 ++it)
6219 {
6220 if ( (*it)->i_getStorageBus() == aConnectionType
6221 && (*it)->i_getInstance() == aInstance)
6222 {
6223 (*it).queryInterfaceTo(aStorageController.asOutParam());
6224 return S_OK;
6225 }
6226 }
6227
6228 return setError(VBOX_E_OBJECT_NOT_FOUND,
6229 tr("Could not find a storage controller with instance number '%lu'"),
6230 aInstance);
6231}
6232
6233HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6234{
6235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6236
6237 HRESULT rc = i_checkStateDependency(MutableStateDep);
6238 if (FAILED(rc)) return rc;
6239
6240 ComObjPtr<StorageController> ctrl;
6241
6242 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6243 if (SUCCEEDED(rc))
6244 {
6245 /* Ensure that only one controller of each type is marked as bootable. */
6246 if (aBootable == TRUE)
6247 {
6248 for (StorageControllerList::const_iterator
6249 it = mStorageControllers->begin();
6250 it != mStorageControllers->end();
6251 ++it)
6252 {
6253 ComObjPtr<StorageController> aCtrl = (*it);
6254
6255 if ( (aCtrl->i_getName() != aName)
6256 && aCtrl->i_getBootable() == TRUE
6257 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6258 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6259 {
6260 aCtrl->i_setBootable(FALSE);
6261 break;
6262 }
6263 }
6264 }
6265
6266 if (SUCCEEDED(rc))
6267 {
6268 ctrl->i_setBootable(aBootable);
6269 i_setModified(IsModified_Storage);
6270 }
6271 }
6272
6273 if (SUCCEEDED(rc))
6274 {
6275 /* inform the direct session if any */
6276 alock.release();
6277 i_onStorageControllerChange();
6278 }
6279
6280 return rc;
6281}
6282
6283HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6284{
6285 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6286
6287 HRESULT rc = i_checkStateDependency(MutableStateDep);
6288 if (FAILED(rc)) return rc;
6289
6290 ComObjPtr<StorageController> ctrl;
6291 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6292 if (FAILED(rc)) return rc;
6293
6294 {
6295 /* find all attached devices to the appropriate storage controller and detach them all */
6296 // make a temporary list because detachDevice invalidates iterators into
6297 // mMediumAttachments
6298 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6299
6300 for (MediumAttachmentList::const_iterator
6301 it = llAttachments2.begin();
6302 it != llAttachments2.end();
6303 ++it)
6304 {
6305 MediumAttachment *pAttachTemp = *it;
6306
6307 AutoCaller localAutoCaller(pAttachTemp);
6308 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6309
6310 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6311
6312 if (pAttachTemp->i_getControllerName() == aName)
6313 {
6314 rc = i_detachDevice(pAttachTemp, alock, NULL);
6315 if (FAILED(rc)) return rc;
6316 }
6317 }
6318 }
6319
6320 /* We can remove it now. */
6321 i_setModified(IsModified_Storage);
6322 mStorageControllers.backup();
6323
6324 ctrl->i_unshare();
6325
6326 mStorageControllers->remove(ctrl);
6327
6328 /* inform the direct session if any */
6329 alock.release();
6330 i_onStorageControllerChange();
6331
6332 return S_OK;
6333}
6334
6335HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6336 ComPtr<IUSBController> &aController)
6337{
6338 if ( (aType <= USBControllerType_Null)
6339 || (aType >= USBControllerType_Last))
6340 return setError(E_INVALIDARG,
6341 tr("Invalid USB controller type: %d"),
6342 aType);
6343
6344 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6345
6346 HRESULT rc = i_checkStateDependency(MutableStateDep);
6347 if (FAILED(rc)) return rc;
6348
6349 /* try to find one with the same type first. */
6350 ComObjPtr<USBController> ctrl;
6351
6352 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6353 if (SUCCEEDED(rc))
6354 return setError(VBOX_E_OBJECT_IN_USE,
6355 tr("USB controller named '%s' already exists"),
6356 aName.c_str());
6357
6358 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6359 ULONG maxInstances;
6360 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6361 if (FAILED(rc))
6362 return rc;
6363
6364 ULONG cInstances = i_getUSBControllerCountByType(aType);
6365 if (cInstances >= maxInstances)
6366 return setError(E_INVALIDARG,
6367 tr("Too many USB controllers of this type"));
6368
6369 ctrl.createObject();
6370
6371 rc = ctrl->init(this, aName, aType);
6372 if (FAILED(rc)) return rc;
6373
6374 i_setModified(IsModified_USB);
6375 mUSBControllers.backup();
6376 mUSBControllers->push_back(ctrl);
6377
6378 ctrl.queryInterfaceTo(aController.asOutParam());
6379
6380 /* inform the direct session if any */
6381 alock.release();
6382 i_onUSBControllerChange();
6383
6384 return S_OK;
6385}
6386
6387HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6388{
6389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6390
6391 ComObjPtr<USBController> ctrl;
6392
6393 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6394 if (SUCCEEDED(rc))
6395 ctrl.queryInterfaceTo(aController.asOutParam());
6396
6397 return rc;
6398}
6399
6400HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6401 ULONG *aControllers)
6402{
6403 if ( (aType <= USBControllerType_Null)
6404 || (aType >= USBControllerType_Last))
6405 return setError(E_INVALIDARG,
6406 tr("Invalid USB controller type: %d"),
6407 aType);
6408
6409 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6410
6411 ComObjPtr<USBController> ctrl;
6412
6413 *aControllers = i_getUSBControllerCountByType(aType);
6414
6415 return S_OK;
6416}
6417
6418HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6419{
6420
6421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6422
6423 HRESULT rc = i_checkStateDependency(MutableStateDep);
6424 if (FAILED(rc)) return rc;
6425
6426 ComObjPtr<USBController> ctrl;
6427 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6428 if (FAILED(rc)) return rc;
6429
6430 i_setModified(IsModified_USB);
6431 mUSBControllers.backup();
6432
6433 ctrl->i_unshare();
6434
6435 mUSBControllers->remove(ctrl);
6436
6437 /* inform the direct session if any */
6438 alock.release();
6439 i_onUSBControllerChange();
6440
6441 return S_OK;
6442}
6443
6444HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6445 ULONG *aOriginX,
6446 ULONG *aOriginY,
6447 ULONG *aWidth,
6448 ULONG *aHeight,
6449 BOOL *aEnabled)
6450{
6451 uint32_t u32OriginX= 0;
6452 uint32_t u32OriginY= 0;
6453 uint32_t u32Width = 0;
6454 uint32_t u32Height = 0;
6455 uint16_t u16Flags = 0;
6456
6457 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6458 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6459 if (RT_FAILURE(vrc))
6460 {
6461#ifdef RT_OS_WINDOWS
6462 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6463 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6464 * So just assign fEnable to TRUE again.
6465 * The right fix would be to change GUI API wrappers to make sure that parameters
6466 * are changed only if API succeeds.
6467 */
6468 *aEnabled = TRUE;
6469#endif
6470 return setError(VBOX_E_IPRT_ERROR,
6471 tr("Saved guest size is not available (%Rrc)"),
6472 vrc);
6473 }
6474
6475 *aOriginX = u32OriginX;
6476 *aOriginY = u32OriginY;
6477 *aWidth = u32Width;
6478 *aHeight = u32Height;
6479 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6480
6481 return S_OK;
6482}
6483
6484HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6485 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6486{
6487 if (aScreenId != 0)
6488 return E_NOTIMPL;
6489
6490 if ( aBitmapFormat != BitmapFormat_BGR0
6491 && aBitmapFormat != BitmapFormat_BGRA
6492 && aBitmapFormat != BitmapFormat_RGBA
6493 && aBitmapFormat != BitmapFormat_PNG)
6494 return setError(E_NOTIMPL,
6495 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6496
6497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6498
6499 uint8_t *pu8Data = NULL;
6500 uint32_t cbData = 0;
6501 uint32_t u32Width = 0;
6502 uint32_t u32Height = 0;
6503
6504 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6505
6506 if (RT_FAILURE(vrc))
6507 return setError(VBOX_E_IPRT_ERROR,
6508 tr("Saved thumbnail data is not available (%Rrc)"),
6509 vrc);
6510
6511 HRESULT hr = S_OK;
6512
6513 *aWidth = u32Width;
6514 *aHeight = u32Height;
6515
6516 if (cbData > 0)
6517 {
6518 /* Convert pixels to the format expected by the API caller. */
6519 if (aBitmapFormat == BitmapFormat_BGR0)
6520 {
6521 /* [0] B, [1] G, [2] R, [3] 0. */
6522 aData.resize(cbData);
6523 memcpy(&aData.front(), pu8Data, cbData);
6524 }
6525 else if (aBitmapFormat == BitmapFormat_BGRA)
6526 {
6527 /* [0] B, [1] G, [2] R, [3] A. */
6528 aData.resize(cbData);
6529 for (uint32_t i = 0; i < cbData; i += 4)
6530 {
6531 aData[i] = pu8Data[i];
6532 aData[i + 1] = pu8Data[i + 1];
6533 aData[i + 2] = pu8Data[i + 2];
6534 aData[i + 3] = 0xff;
6535 }
6536 }
6537 else if (aBitmapFormat == BitmapFormat_RGBA)
6538 {
6539 /* [0] R, [1] G, [2] B, [3] A. */
6540 aData.resize(cbData);
6541 for (uint32_t i = 0; i < cbData; i += 4)
6542 {
6543 aData[i] = pu8Data[i + 2];
6544 aData[i + 1] = pu8Data[i + 1];
6545 aData[i + 2] = pu8Data[i];
6546 aData[i + 3] = 0xff;
6547 }
6548 }
6549 else if (aBitmapFormat == BitmapFormat_PNG)
6550 {
6551 uint8_t *pu8PNG = NULL;
6552 uint32_t cbPNG = 0;
6553 uint32_t cxPNG = 0;
6554 uint32_t cyPNG = 0;
6555
6556 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6557
6558 if (RT_SUCCESS(vrc))
6559 {
6560 aData.resize(cbPNG);
6561 if (cbPNG)
6562 memcpy(&aData.front(), pu8PNG, cbPNG);
6563 }
6564 else
6565 hr = setError(VBOX_E_IPRT_ERROR,
6566 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6567 vrc);
6568
6569 RTMemFree(pu8PNG);
6570 }
6571 }
6572
6573 freeSavedDisplayScreenshot(pu8Data);
6574
6575 return hr;
6576}
6577
6578HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6579 ULONG *aWidth,
6580 ULONG *aHeight,
6581 std::vector<BitmapFormat_T> &aBitmapFormats)
6582{
6583 if (aScreenId != 0)
6584 return E_NOTIMPL;
6585
6586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 uint8_t *pu8Data = NULL;
6589 uint32_t cbData = 0;
6590 uint32_t u32Width = 0;
6591 uint32_t u32Height = 0;
6592
6593 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6594
6595 if (RT_FAILURE(vrc))
6596 return setError(VBOX_E_IPRT_ERROR,
6597 tr("Saved screenshot data is not available (%Rrc)"),
6598 vrc);
6599
6600 *aWidth = u32Width;
6601 *aHeight = u32Height;
6602 aBitmapFormats.resize(1);
6603 aBitmapFormats[0] = BitmapFormat_PNG;
6604
6605 freeSavedDisplayScreenshot(pu8Data);
6606
6607 return S_OK;
6608}
6609
6610HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6611 BitmapFormat_T aBitmapFormat,
6612 ULONG *aWidth,
6613 ULONG *aHeight,
6614 std::vector<BYTE> &aData)
6615{
6616 if (aScreenId != 0)
6617 return E_NOTIMPL;
6618
6619 if (aBitmapFormat != BitmapFormat_PNG)
6620 return E_NOTIMPL;
6621
6622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6623
6624 uint8_t *pu8Data = NULL;
6625 uint32_t cbData = 0;
6626 uint32_t u32Width = 0;
6627 uint32_t u32Height = 0;
6628
6629 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6630
6631 if (RT_FAILURE(vrc))
6632 return setError(VBOX_E_IPRT_ERROR,
6633 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6634 vrc);
6635
6636 *aWidth = u32Width;
6637 *aHeight = u32Height;
6638
6639 aData.resize(cbData);
6640 if (cbData)
6641 memcpy(&aData.front(), pu8Data, cbData);
6642
6643 freeSavedDisplayScreenshot(pu8Data);
6644
6645 return S_OK;
6646}
6647
6648HRESULT Machine::hotPlugCPU(ULONG aCpu)
6649{
6650 HRESULT rc = S_OK;
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 if (!mHWData->mCPUHotPlugEnabled)
6654 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6655
6656 if (aCpu >= mHWData->mCPUCount)
6657 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6658
6659 if (mHWData->mCPUAttached[aCpu])
6660 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6661
6662 alock.release();
6663 rc = i_onCPUChange(aCpu, false);
6664 alock.acquire();
6665 if (FAILED(rc)) return rc;
6666
6667 i_setModified(IsModified_MachineData);
6668 mHWData.backup();
6669 mHWData->mCPUAttached[aCpu] = true;
6670
6671 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6672 if (Global::IsOnline(mData->mMachineState))
6673 i_saveSettings(NULL);
6674
6675 return S_OK;
6676}
6677
6678HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6679{
6680 HRESULT rc = S_OK;
6681
6682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6683
6684 if (!mHWData->mCPUHotPlugEnabled)
6685 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6686
6687 if (aCpu >= SchemaDefs::MaxCPUCount)
6688 return setError(E_INVALIDARG,
6689 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6690 SchemaDefs::MaxCPUCount);
6691
6692 if (!mHWData->mCPUAttached[aCpu])
6693 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6694
6695 /* CPU 0 can't be detached */
6696 if (aCpu == 0)
6697 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6698
6699 alock.release();
6700 rc = i_onCPUChange(aCpu, true);
6701 alock.acquire();
6702 if (FAILED(rc)) return rc;
6703
6704 i_setModified(IsModified_MachineData);
6705 mHWData.backup();
6706 mHWData->mCPUAttached[aCpu] = false;
6707
6708 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6709 if (Global::IsOnline(mData->mMachineState))
6710 i_saveSettings(NULL);
6711
6712 return S_OK;
6713}
6714
6715HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6716{
6717 *aAttached = false;
6718
6719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6720
6721 /* If hotplug is enabled the CPU is always enabled. */
6722 if (!mHWData->mCPUHotPlugEnabled)
6723 {
6724 if (aCpu < mHWData->mCPUCount)
6725 *aAttached = true;
6726 }
6727 else
6728 {
6729 if (aCpu < SchemaDefs::MaxCPUCount)
6730 *aAttached = mHWData->mCPUAttached[aCpu];
6731 }
6732
6733 return S_OK;
6734}
6735
6736HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6737{
6738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6739
6740 Utf8Str log = i_getLogFilename(aIdx);
6741 if (!RTFileExists(log.c_str()))
6742 log.setNull();
6743 aFilename = log;
6744
6745 return S_OK;
6746}
6747
6748HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6749{
6750 if (aSize < 0)
6751 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6752
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 HRESULT rc = S_OK;
6756 Utf8Str log = i_getLogFilename(aIdx);
6757
6758 /* do not unnecessarily hold the lock while doing something which does
6759 * not need the lock and potentially takes a long time. */
6760 alock.release();
6761
6762 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6763 * keeps the SOAP reply size under 1M for the webservice (we're using
6764 * base64 encoded strings for binary data for years now, avoiding the
6765 * expansion of each byte array element to approx. 25 bytes of XML. */
6766 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6767 aData.resize(cbData);
6768
6769 RTFILE LogFile;
6770 int vrc = RTFileOpen(&LogFile, log.c_str(),
6771 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6772 if (RT_SUCCESS(vrc))
6773 {
6774 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6775 if (RT_SUCCESS(vrc))
6776 aData.resize(cbData);
6777 else
6778 rc = setError(VBOX_E_IPRT_ERROR,
6779 tr("Could not read log file '%s' (%Rrc)"),
6780 log.c_str(), vrc);
6781 RTFileClose(LogFile);
6782 }
6783 else
6784 rc = setError(VBOX_E_IPRT_ERROR,
6785 tr("Could not open log file '%s' (%Rrc)"),
6786 log.c_str(), vrc);
6787
6788 if (FAILED(rc))
6789 aData.resize(0);
6790
6791 return rc;
6792}
6793
6794
6795/**
6796 * Currently this method doesn't attach device to the running VM,
6797 * just makes sure it's plugged on next VM start.
6798 */
6799HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6800{
6801 // lock scope
6802 {
6803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6804
6805 HRESULT rc = i_checkStateDependency(MutableStateDep);
6806 if (FAILED(rc)) return rc;
6807
6808 ChipsetType_T aChipset = ChipsetType_PIIX3;
6809 COMGETTER(ChipsetType)(&aChipset);
6810
6811 if (aChipset != ChipsetType_ICH9)
6812 {
6813 return setError(E_INVALIDARG,
6814 tr("Host PCI attachment only supported with ICH9 chipset"));
6815 }
6816
6817 // check if device with this host PCI address already attached
6818 for (HWData::PCIDeviceAssignmentList::const_iterator
6819 it = mHWData->mPCIDeviceAssignments.begin();
6820 it != mHWData->mPCIDeviceAssignments.end();
6821 ++it)
6822 {
6823 LONG iHostAddress = -1;
6824 ComPtr<PCIDeviceAttachment> pAttach;
6825 pAttach = *it;
6826 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6827 if (iHostAddress == aHostAddress)
6828 return setError(E_INVALIDARG,
6829 tr("Device with host PCI address already attached to this VM"));
6830 }
6831
6832 ComObjPtr<PCIDeviceAttachment> pda;
6833 char name[32];
6834
6835 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6836 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6837 pda.createObject();
6838 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6839 i_setModified(IsModified_MachineData);
6840 mHWData.backup();
6841 mHWData->mPCIDeviceAssignments.push_back(pda);
6842 }
6843
6844 return S_OK;
6845}
6846
6847/**
6848 * Currently this method doesn't detach device from the running VM,
6849 * just makes sure it's not plugged on next VM start.
6850 */
6851HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6852{
6853 ComObjPtr<PCIDeviceAttachment> pAttach;
6854 bool fRemoved = false;
6855 HRESULT rc;
6856
6857 // lock scope
6858 {
6859 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6860
6861 rc = i_checkStateDependency(MutableStateDep);
6862 if (FAILED(rc)) return rc;
6863
6864 for (HWData::PCIDeviceAssignmentList::const_iterator
6865 it = mHWData->mPCIDeviceAssignments.begin();
6866 it != mHWData->mPCIDeviceAssignments.end();
6867 ++it)
6868 {
6869 LONG iHostAddress = -1;
6870 pAttach = *it;
6871 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6872 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6873 {
6874 i_setModified(IsModified_MachineData);
6875 mHWData.backup();
6876 mHWData->mPCIDeviceAssignments.remove(pAttach);
6877 fRemoved = true;
6878 break;
6879 }
6880 }
6881 }
6882
6883
6884 /* Fire event outside of the lock */
6885 if (fRemoved)
6886 {
6887 Assert(!pAttach.isNull());
6888 ComPtr<IEventSource> es;
6889 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6890 Assert(SUCCEEDED(rc));
6891 Bstr mid;
6892 rc = this->COMGETTER(Id)(mid.asOutParam());
6893 Assert(SUCCEEDED(rc));
6894 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6895 }
6896
6897 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6898 tr("No host PCI device %08x attached"),
6899 aHostAddress
6900 );
6901}
6902
6903HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6904{
6905 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6906
6907 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6908 size_t i = 0;
6909 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6910 it = mHWData->mPCIDeviceAssignments.begin();
6911 it != mHWData->mPCIDeviceAssignments.end();
6912 ++it, ++i)
6913 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6914
6915 return S_OK;
6916}
6917
6918HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6919{
6920 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6921
6922 return S_OK;
6923}
6924
6925HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6926{
6927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6928
6929 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6930
6931 return S_OK;
6932}
6933
6934HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6935{
6936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6937 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6938 if (SUCCEEDED(hrc))
6939 {
6940 hrc = mHWData.backupEx();
6941 if (SUCCEEDED(hrc))
6942 {
6943 i_setModified(IsModified_MachineData);
6944 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6945 }
6946 }
6947 return hrc;
6948}
6949
6950HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6951{
6952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6953 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6954 return S_OK;
6955}
6956
6957HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6958{
6959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6960 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6961 if (SUCCEEDED(hrc))
6962 {
6963 hrc = mHWData.backupEx();
6964 if (SUCCEEDED(hrc))
6965 {
6966 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6967 if (SUCCEEDED(hrc))
6968 i_setModified(IsModified_MachineData);
6969 }
6970 }
6971 return hrc;
6972}
6973
6974HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6975{
6976 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6977
6978 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6979
6980 return S_OK;
6981}
6982
6983HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6984{
6985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6987 if (SUCCEEDED(hrc))
6988 {
6989 hrc = mHWData.backupEx();
6990 if (SUCCEEDED(hrc))
6991 {
6992 i_setModified(IsModified_MachineData);
6993 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6994 }
6995 }
6996 return hrc;
6997}
6998
6999HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7000{
7001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7002
7003 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7004
7005 return S_OK;
7006}
7007
7008HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7009{
7010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7011
7012 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7013 if ( SUCCEEDED(hrc)
7014 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7015 {
7016 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7017 int vrc;
7018
7019 if (aAutostartEnabled)
7020 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7021 else
7022 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7023
7024 if (RT_SUCCESS(vrc))
7025 {
7026 hrc = mHWData.backupEx();
7027 if (SUCCEEDED(hrc))
7028 {
7029 i_setModified(IsModified_MachineData);
7030 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7031 }
7032 }
7033 else if (vrc == VERR_NOT_SUPPORTED)
7034 hrc = setError(VBOX_E_NOT_SUPPORTED,
7035 tr("The VM autostart feature is not supported on this platform"));
7036 else if (vrc == VERR_PATH_NOT_FOUND)
7037 hrc = setError(E_FAIL,
7038 tr("The path to the autostart database is not set"));
7039 else
7040 hrc = setError(E_UNEXPECTED,
7041 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7042 aAutostartEnabled ? "Adding" : "Removing",
7043 mUserData->s.strName.c_str(), vrc);
7044 }
7045 return hrc;
7046}
7047
7048HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7049{
7050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7051
7052 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7053
7054 return S_OK;
7055}
7056
7057HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7058{
7059 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7060 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7061 if (SUCCEEDED(hrc))
7062 {
7063 hrc = mHWData.backupEx();
7064 if (SUCCEEDED(hrc))
7065 {
7066 i_setModified(IsModified_MachineData);
7067 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7068 }
7069 }
7070 return hrc;
7071}
7072
7073HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7074{
7075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7076
7077 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7078
7079 return S_OK;
7080}
7081
7082HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7083{
7084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7085 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7086 if ( SUCCEEDED(hrc)
7087 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7088 {
7089 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7090 int vrc;
7091
7092 if (aAutostopType != AutostopType_Disabled)
7093 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7094 else
7095 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7096
7097 if (RT_SUCCESS(vrc))
7098 {
7099 hrc = mHWData.backupEx();
7100 if (SUCCEEDED(hrc))
7101 {
7102 i_setModified(IsModified_MachineData);
7103 mHWData->mAutostart.enmAutostopType = aAutostopType;
7104 }
7105 }
7106 else if (vrc == VERR_NOT_SUPPORTED)
7107 hrc = setError(VBOX_E_NOT_SUPPORTED,
7108 tr("The VM autostop feature is not supported on this platform"));
7109 else if (vrc == VERR_PATH_NOT_FOUND)
7110 hrc = setError(E_FAIL,
7111 tr("The path to the autostart database is not set"));
7112 else
7113 hrc = setError(E_UNEXPECTED,
7114 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7115 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7116 mUserData->s.strName.c_str(), vrc);
7117 }
7118 return hrc;
7119}
7120
7121HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7122{
7123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7124
7125 aDefaultFrontend = mHWData->mDefaultFrontend;
7126
7127 return S_OK;
7128}
7129
7130HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7131{
7132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7133 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7134 if (SUCCEEDED(hrc))
7135 {
7136 hrc = mHWData.backupEx();
7137 if (SUCCEEDED(hrc))
7138 {
7139 i_setModified(IsModified_MachineData);
7140 mHWData->mDefaultFrontend = aDefaultFrontend;
7141 }
7142 }
7143 return hrc;
7144}
7145
7146HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7147{
7148 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7149 size_t cbIcon = mUserData->s.ovIcon.size();
7150 aIcon.resize(cbIcon);
7151 if (cbIcon)
7152 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7153 return S_OK;
7154}
7155
7156HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7157{
7158 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7159 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7160 if (SUCCEEDED(hrc))
7161 {
7162 i_setModified(IsModified_MachineData);
7163 mUserData.backup();
7164 size_t cbIcon = aIcon.size();
7165 mUserData->s.ovIcon.resize(cbIcon);
7166 if (cbIcon)
7167 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7168 }
7169 return hrc;
7170}
7171
7172HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7173{
7174#ifdef VBOX_WITH_USB
7175 *aUSBProxyAvailable = true;
7176#else
7177 *aUSBProxyAvailable = false;
7178#endif
7179 return S_OK;
7180}
7181
7182HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7183{
7184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7185
7186 aVMProcessPriority = mUserData->s.strVMPriority;
7187
7188 return S_OK;
7189}
7190
7191HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7192{
7193 RT_NOREF(aVMProcessPriority);
7194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7195 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7196 if (SUCCEEDED(hrc))
7197 {
7198 /** @todo r=klaus: currently this is marked as not implemented, as
7199 * the code for setting the priority of the process is not there
7200 * (neither when starting the VM nor at runtime). */
7201 ReturnComNotImplemented();
7202#if 0
7203 hrc = mUserData.backupEx();
7204 if (SUCCEEDED(hrc))
7205 {
7206 i_setModified(IsModified_MachineData);
7207 mUserData->s.strVMPriority = aVMProcessPriority;
7208 }
7209#endif
7210 }
7211 return hrc;
7212}
7213
7214HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7215 ComPtr<IProgress> &aProgress)
7216{
7217 ComObjPtr<Progress> pP;
7218 Progress *ppP = pP;
7219 IProgress *iP = static_cast<IProgress *>(ppP);
7220 IProgress **pProgress = &iP;
7221
7222 IMachine *pTarget = aTarget;
7223
7224 /* Convert the options. */
7225 RTCList<CloneOptions_T> optList;
7226 if (aOptions.size())
7227 for (size_t i = 0; i < aOptions.size(); ++i)
7228 optList.append(aOptions[i]);
7229
7230 if (optList.contains(CloneOptions_Link))
7231 {
7232 if (!i_isSnapshotMachine())
7233 return setError(E_INVALIDARG,
7234 tr("Linked clone can only be created from a snapshot"));
7235 if (aMode != CloneMode_MachineState)
7236 return setError(E_INVALIDARG,
7237 tr("Linked clone can only be created for a single machine state"));
7238 }
7239 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7240
7241 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7242
7243 HRESULT rc = pWorker->start(pProgress);
7244
7245 pP = static_cast<Progress *>(*pProgress);
7246 pP.queryInterfaceTo(aProgress.asOutParam());
7247
7248 return rc;
7249
7250}
7251
7252HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7253 const com::Utf8Str &aType,
7254 ComPtr<IProgress> &aProgress)
7255{
7256 LogFlowThisFuncEnter();
7257
7258 ComObjPtr<Progress> progress;
7259
7260 progress.createObject();
7261
7262 HRESULT rc = S_OK;
7263 Utf8Str targetPath = aTargetPath;
7264 Utf8Str type = aType;
7265
7266 /* Initialize our worker task */
7267 MachineMoveVM* task = NULL;
7268 try
7269 {
7270 task = new MachineMoveVM(this, targetPath, type, progress);
7271 }
7272 catch(...)
7273 {
7274 delete task;
7275 return rc;
7276 }
7277
7278 /*
7279 * task pointer will be owned by the ThreadTask class.
7280 * There is no need to call operator "delete" in the end.
7281 */
7282 rc = task->init();
7283 if (SUCCEEDED(rc))
7284 {
7285 rc = task->createThread();
7286 if (FAILED(rc))
7287 {
7288 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7289 }
7290
7291 /* Return progress to the caller */
7292 progress.queryInterfaceTo(aProgress.asOutParam());
7293 }
7294
7295 LogFlowThisFuncLeave();
7296 return rc;
7297
7298}
7299
7300HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7301{
7302 NOREF(aProgress);
7303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7304
7305 // This check should always fail.
7306 HRESULT rc = i_checkStateDependency(MutableStateDep);
7307 if (FAILED(rc)) return rc;
7308
7309 AssertFailedReturn(E_NOTIMPL);
7310}
7311
7312HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7313{
7314 NOREF(aSavedStateFile);
7315 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7316
7317 // This check should always fail.
7318 HRESULT rc = i_checkStateDependency(MutableStateDep);
7319 if (FAILED(rc)) return rc;
7320
7321 AssertFailedReturn(E_NOTIMPL);
7322}
7323
7324HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7325{
7326 NOREF(aFRemoveFile);
7327 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7328
7329 // This check should always fail.
7330 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7331 if (FAILED(rc)) return rc;
7332
7333 AssertFailedReturn(E_NOTIMPL);
7334}
7335
7336// public methods for internal purposes
7337/////////////////////////////////////////////////////////////////////////////
7338
7339/**
7340 * Adds the given IsModified_* flag to the dirty flags of the machine.
7341 * This must be called either during i_loadSettings or under the machine write lock.
7342 * @param fl Flag
7343 * @param fAllowStateModification If state modifications are allowed.
7344 */
7345void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7346{
7347 mData->flModifications |= fl;
7348 if (fAllowStateModification && i_isStateModificationAllowed())
7349 mData->mCurrentStateModified = true;
7350}
7351
7352/**
7353 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7354 * care of the write locking.
7355 *
7356 * @param fModification The flag to add.
7357 * @param fAllowStateModification If state modifications are allowed.
7358 */
7359void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7360{
7361 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7362 i_setModified(fModification, fAllowStateModification);
7363}
7364
7365/**
7366 * Saves the registry entry of this machine to the given configuration node.
7367 *
7368 * @param data Machine registry data.
7369 *
7370 * @note locks this object for reading.
7371 */
7372HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7373{
7374 AutoLimitedCaller autoCaller(this);
7375 AssertComRCReturnRC(autoCaller.rc());
7376
7377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7378
7379 data.uuid = mData->mUuid;
7380 data.strSettingsFile = mData->m_strConfigFile;
7381
7382 return S_OK;
7383}
7384
7385/**
7386 * Calculates the absolute path of the given path taking the directory of the
7387 * machine settings file as the current directory.
7388 *
7389 * @param strPath Path to calculate the absolute path for.
7390 * @param aResult Where to put the result (used only on success, can be the
7391 * same Utf8Str instance as passed in @a aPath).
7392 * @return IPRT result.
7393 *
7394 * @note Locks this object for reading.
7395 */
7396int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7397{
7398 AutoCaller autoCaller(this);
7399 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7400
7401 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7402
7403 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7404
7405 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7406
7407 strSettingsDir.stripFilename();
7408 char folder[RTPATH_MAX];
7409 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7410 if (RT_SUCCESS(vrc))
7411 aResult = folder;
7412
7413 return vrc;
7414}
7415
7416/**
7417 * Copies strSource to strTarget, making it relative to the machine folder
7418 * if it is a subdirectory thereof, or simply copying it otherwise.
7419 *
7420 * @param strSource Path to evaluate and copy.
7421 * @param strTarget Buffer to receive target path.
7422 *
7423 * @note Locks this object for reading.
7424 */
7425void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7426 Utf8Str &strTarget)
7427{
7428 AutoCaller autoCaller(this);
7429 AssertComRCReturn(autoCaller.rc(), (void)0);
7430
7431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7432
7433 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7434 // use strTarget as a temporary buffer to hold the machine settings dir
7435 strTarget = mData->m_strConfigFileFull;
7436 strTarget.stripFilename();
7437 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7438 {
7439 // is relative: then append what's left
7440 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7441 // for empty paths (only possible for subdirs) use "." to avoid
7442 // triggering default settings for not present config attributes.
7443 if (strTarget.isEmpty())
7444 strTarget = ".";
7445 }
7446 else
7447 // is not relative: then overwrite
7448 strTarget = strSource;
7449}
7450
7451/**
7452 * Returns the full path to the machine's log folder in the
7453 * \a aLogFolder argument.
7454 */
7455void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7456{
7457 AutoCaller autoCaller(this);
7458 AssertComRCReturnVoid(autoCaller.rc());
7459
7460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7461
7462 char szTmp[RTPATH_MAX];
7463 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7464 if (RT_SUCCESS(vrc))
7465 {
7466 if (szTmp[0] && !mUserData.isNull())
7467 {
7468 char szTmp2[RTPATH_MAX];
7469 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7470 if (RT_SUCCESS(vrc))
7471 aLogFolder = Utf8StrFmt("%s%c%s",
7472 szTmp2,
7473 RTPATH_DELIMITER,
7474 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7475 }
7476 else
7477 vrc = VERR_PATH_IS_RELATIVE;
7478 }
7479
7480 if (RT_FAILURE(vrc))
7481 {
7482 // fallback if VBOX_USER_LOGHOME is not set or invalid
7483 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7484 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7485 aLogFolder.append(RTPATH_DELIMITER);
7486 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7487 }
7488}
7489
7490/**
7491 * Returns the full path to the machine's log file for an given index.
7492 */
7493Utf8Str Machine::i_getLogFilename(ULONG idx)
7494{
7495 Utf8Str logFolder;
7496 getLogFolder(logFolder);
7497 Assert(logFolder.length());
7498
7499 Utf8Str log;
7500 if (idx == 0)
7501 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7502#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7503 else if (idx == 1)
7504 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7505 else
7506 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7507#else
7508 else
7509 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7510#endif
7511 return log;
7512}
7513
7514/**
7515 * Returns the full path to the machine's hardened log file.
7516 */
7517Utf8Str Machine::i_getHardeningLogFilename(void)
7518{
7519 Utf8Str strFilename;
7520 getLogFolder(strFilename);
7521 Assert(strFilename.length());
7522 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7523 return strFilename;
7524}
7525
7526
7527/**
7528 * Composes a unique saved state filename based on the current system time. The filename is
7529 * granular to the second so this will work so long as no more than one snapshot is taken on
7530 * a machine per second.
7531 *
7532 * Before version 4.1, we used this formula for saved state files:
7533 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7534 * which no longer works because saved state files can now be shared between the saved state of the
7535 * "saved" machine and an online snapshot, and the following would cause problems:
7536 * 1) save machine
7537 * 2) create online snapshot from that machine state --> reusing saved state file
7538 * 3) save machine again --> filename would be reused, breaking the online snapshot
7539 *
7540 * So instead we now use a timestamp.
7541 *
7542 * @param strStateFilePath
7543 */
7544
7545void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7546{
7547 AutoCaller autoCaller(this);
7548 AssertComRCReturnVoid(autoCaller.rc());
7549
7550 {
7551 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7552 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7553 }
7554
7555 RTTIMESPEC ts;
7556 RTTimeNow(&ts);
7557 RTTIME time;
7558 RTTimeExplode(&time, &ts);
7559
7560 strStateFilePath += RTPATH_DELIMITER;
7561 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7562 time.i32Year, time.u8Month, time.u8MonthDay,
7563 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7564}
7565
7566/**
7567 * Returns the full path to the default video capture file.
7568 */
7569void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7570{
7571 AutoCaller autoCaller(this);
7572 AssertComRCReturnVoid(autoCaller.rc());
7573
7574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7575
7576 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7577 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7578 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7579}
7580
7581/**
7582 * Returns whether at least one USB controller is present for the VM.
7583 */
7584bool Machine::i_isUSBControllerPresent()
7585{
7586 AutoCaller autoCaller(this);
7587 AssertComRCReturn(autoCaller.rc(), false);
7588
7589 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7590
7591 return (mUSBControllers->size() > 0);
7592}
7593
7594/**
7595 * @note Locks this object for writing, calls the client process
7596 * (inside the lock).
7597 */
7598HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7599 const Utf8Str &strFrontend,
7600 const Utf8Str &strEnvironment,
7601 ProgressProxy *aProgress)
7602{
7603 LogFlowThisFuncEnter();
7604
7605 AssertReturn(aControl, E_FAIL);
7606 AssertReturn(aProgress, E_FAIL);
7607 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7608
7609 AutoCaller autoCaller(this);
7610 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7611
7612 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7613
7614 if (!mData->mRegistered)
7615 return setError(E_UNEXPECTED,
7616 tr("The machine '%s' is not registered"),
7617 mUserData->s.strName.c_str());
7618
7619 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7620
7621 /* The process started when launching a VM with separate UI/VM processes is always
7622 * the UI process, i.e. needs special handling as it won't claim the session. */
7623 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7624
7625 if (fSeparate)
7626 {
7627 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7628 return setError(VBOX_E_INVALID_OBJECT_STATE,
7629 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7630 mUserData->s.strName.c_str());
7631 }
7632 else
7633 {
7634 if ( mData->mSession.mState == SessionState_Locked
7635 || mData->mSession.mState == SessionState_Spawning
7636 || mData->mSession.mState == SessionState_Unlocking)
7637 return setError(VBOX_E_INVALID_OBJECT_STATE,
7638 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7639 mUserData->s.strName.c_str());
7640
7641 /* may not be busy */
7642 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7643 }
7644
7645 /* get the path to the executable */
7646 char szPath[RTPATH_MAX];
7647 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7648 size_t cchBufLeft = strlen(szPath);
7649 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7650 szPath[cchBufLeft] = 0;
7651 char *pszNamePart = szPath + cchBufLeft;
7652 cchBufLeft = sizeof(szPath) - cchBufLeft;
7653
7654 int vrc = VINF_SUCCESS;
7655 RTPROCESS pid = NIL_RTPROCESS;
7656
7657 RTENV env = RTENV_DEFAULT;
7658
7659 if (!strEnvironment.isEmpty())
7660 {
7661 char *newEnvStr = NULL;
7662
7663 do
7664 {
7665 /* clone the current environment */
7666 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7667 AssertRCBreakStmt(vrc2, vrc = vrc2);
7668
7669 newEnvStr = RTStrDup(strEnvironment.c_str());
7670 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7671
7672 /* put new variables to the environment
7673 * (ignore empty variable names here since RTEnv API
7674 * intentionally doesn't do that) */
7675 char *var = newEnvStr;
7676 for (char *p = newEnvStr; *p; ++p)
7677 {
7678 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7679 {
7680 *p = '\0';
7681 if (*var)
7682 {
7683 char *val = strchr(var, '=');
7684 if (val)
7685 {
7686 *val++ = '\0';
7687 vrc2 = RTEnvSetEx(env, var, val);
7688 }
7689 else
7690 vrc2 = RTEnvUnsetEx(env, var);
7691 if (RT_FAILURE(vrc2))
7692 break;
7693 }
7694 var = p + 1;
7695 }
7696 }
7697 if (RT_SUCCESS(vrc2) && *var)
7698 vrc2 = RTEnvPutEx(env, var);
7699
7700 AssertRCBreakStmt(vrc2, vrc = vrc2);
7701 }
7702 while (0);
7703
7704 if (newEnvStr != NULL)
7705 RTStrFree(newEnvStr);
7706 }
7707
7708 /* Hardening logging */
7709#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7710 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7711 {
7712 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7713 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7714 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7715 {
7716 Utf8Str strStartupLogDir = strHardeningLogFile;
7717 strStartupLogDir.stripFilename();
7718 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7719 file without stripping the file. */
7720 }
7721 strSupHardeningLogArg.append(strHardeningLogFile);
7722
7723 /* Remove legacy log filename to avoid confusion. */
7724 Utf8Str strOldStartupLogFile;
7725 getLogFolder(strOldStartupLogFile);
7726 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7727 RTFileDelete(strOldStartupLogFile.c_str());
7728 }
7729 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7730#else
7731 const char *pszSupHardeningLogArg = NULL;
7732#endif
7733
7734 Utf8Str strCanonicalName;
7735
7736#ifdef VBOX_WITH_QTGUI
7737 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7738 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7739 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7740 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7741 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7742 {
7743 strCanonicalName = "GUI/Qt";
7744# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7745 /* Modify the base path so that we don't need to use ".." below. */
7746 RTPathStripTrailingSlash(szPath);
7747 RTPathStripFilename(szPath);
7748 cchBufLeft = strlen(szPath);
7749 pszNamePart = szPath + cchBufLeft;
7750 cchBufLeft = sizeof(szPath) - cchBufLeft;
7751
7752# define OSX_APP_NAME "VirtualBoxVM"
7753# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7754
7755 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7756 if ( strAppOverride.contains(".")
7757 || strAppOverride.contains("/")
7758 || strAppOverride.contains("\\")
7759 || strAppOverride.contains(":"))
7760 strAppOverride.setNull();
7761 Utf8Str strAppPath;
7762 if (!strAppOverride.isEmpty())
7763 {
7764 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7765 Utf8Str strFullPath(szPath);
7766 strFullPath.append(strAppPath);
7767 /* there is a race, but people using this deserve the failure */
7768 if (!RTFileExists(strFullPath.c_str()))
7769 strAppOverride.setNull();
7770 }
7771 if (strAppOverride.isEmpty())
7772 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7773 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7774 strcpy(pszNamePart, strAppPath.c_str());
7775# else
7776 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7777 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7778 strcpy(pszNamePart, s_szVirtualBox_exe);
7779# endif
7780
7781 Utf8Str idStr = mData->mUuid.toString();
7782 const char *apszArgs[] =
7783 {
7784 szPath,
7785 "--comment", mUserData->s.strName.c_str(),
7786 "--startvm", idStr.c_str(),
7787 "--no-startvm-errormsgbox",
7788 NULL, /* For "--separate". */
7789 NULL, /* For "--sup-startup-log". */
7790 NULL
7791 };
7792 unsigned iArg = 6;
7793 if (fSeparate)
7794 apszArgs[iArg++] = "--separate";
7795 apszArgs[iArg++] = pszSupHardeningLogArg;
7796
7797 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7798 }
7799#else /* !VBOX_WITH_QTGUI */
7800 if (0)
7801 ;
7802#endif /* VBOX_WITH_QTGUI */
7803
7804 else
7805
7806#ifdef VBOX_WITH_VBOXSDL
7807 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7808 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7809 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7810 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7811 {
7812 strCanonicalName = "GUI/SDL";
7813 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7814 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7815 strcpy(pszNamePart, s_szVBoxSDL_exe);
7816
7817 Utf8Str idStr = mData->mUuid.toString();
7818 const char *apszArgs[] =
7819 {
7820 szPath,
7821 "--comment", mUserData->s.strName.c_str(),
7822 "--startvm", idStr.c_str(),
7823 NULL, /* For "--separate". */
7824 NULL, /* For "--sup-startup-log". */
7825 NULL
7826 };
7827 unsigned iArg = 5;
7828 if (fSeparate)
7829 apszArgs[iArg++] = "--separate";
7830 apszArgs[iArg++] = pszSupHardeningLogArg;
7831
7832 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7833 }
7834#else /* !VBOX_WITH_VBOXSDL */
7835 if (0)
7836 ;
7837#endif /* !VBOX_WITH_VBOXSDL */
7838
7839 else
7840
7841#ifdef VBOX_WITH_HEADLESS
7842 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7843 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7844 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7845 )
7846 {
7847 strCanonicalName = "headless";
7848 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7849 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7850 * and a VM works even if the server has not been installed.
7851 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7852 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7853 * differently in 4.0 and 3.x.
7854 */
7855 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7856 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7857 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7858
7859 Utf8Str idStr = mData->mUuid.toString();
7860 const char *apszArgs[] =
7861 {
7862 szPath,
7863 "--comment", mUserData->s.strName.c_str(),
7864 "--startvm", idStr.c_str(),
7865 "--vrde", "config",
7866 NULL, /* For "--capture". */
7867 NULL, /* For "--sup-startup-log". */
7868 NULL
7869 };
7870 unsigned iArg = 7;
7871 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7872 apszArgs[iArg++] = "--capture";
7873 apszArgs[iArg++] = pszSupHardeningLogArg;
7874
7875# ifdef RT_OS_WINDOWS
7876 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7877# else
7878 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7879# endif
7880 }
7881#else /* !VBOX_WITH_HEADLESS */
7882 if (0)
7883 ;
7884#endif /* !VBOX_WITH_HEADLESS */
7885 else
7886 {
7887 RTEnvDestroy(env);
7888 return setError(E_INVALIDARG,
7889 tr("Invalid frontend name: '%s'"),
7890 strFrontend.c_str());
7891 }
7892
7893 RTEnvDestroy(env);
7894
7895 if (RT_FAILURE(vrc))
7896 return setError(VBOX_E_IPRT_ERROR,
7897 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7898 mUserData->s.strName.c_str(), vrc);
7899
7900 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7901
7902 if (!fSeparate)
7903 {
7904 /*
7905 * Note that we don't release the lock here before calling the client,
7906 * because it doesn't need to call us back if called with a NULL argument.
7907 * Releasing the lock here is dangerous because we didn't prepare the
7908 * launch data yet, but the client we've just started may happen to be
7909 * too fast and call LockMachine() that will fail (because of PID, etc.),
7910 * so that the Machine will never get out of the Spawning session state.
7911 */
7912
7913 /* inform the session that it will be a remote one */
7914 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7915#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7916 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7917#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7918 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7919#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7920 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7921
7922 if (FAILED(rc))
7923 {
7924 /* restore the session state */
7925 mData->mSession.mState = SessionState_Unlocked;
7926 alock.release();
7927 mParent->i_addProcessToReap(pid);
7928 /* The failure may occur w/o any error info (from RPC), so provide one */
7929 return setError(VBOX_E_VM_ERROR,
7930 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7931 }
7932
7933 /* attach launch data to the machine */
7934 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7935 mData->mSession.mRemoteControls.push_back(aControl);
7936 mData->mSession.mProgress = aProgress;
7937 mData->mSession.mPID = pid;
7938 mData->mSession.mState = SessionState_Spawning;
7939 Assert(strCanonicalName.isNotEmpty());
7940 mData->mSession.mName = strCanonicalName;
7941 }
7942 else
7943 {
7944 /* For separate UI process we declare the launch as completed instantly, as the
7945 * actual headless VM start may or may not come. No point in remembering anything
7946 * yet, as what matters for us is when the headless VM gets started. */
7947 aProgress->i_notifyComplete(S_OK);
7948 }
7949
7950 alock.release();
7951 mParent->i_addProcessToReap(pid);
7952
7953 LogFlowThisFuncLeave();
7954 return S_OK;
7955}
7956
7957/**
7958 * Returns @c true if the given session machine instance has an open direct
7959 * session (and optionally also for direct sessions which are closing) and
7960 * returns the session control machine instance if so.
7961 *
7962 * Note that when the method returns @c false, the arguments remain unchanged.
7963 *
7964 * @param aMachine Session machine object.
7965 * @param aControl Direct session control object (optional).
7966 * @param aRequireVM If true then only allow VM sessions.
7967 * @param aAllowClosing If true then additionally a session which is currently
7968 * being closed will also be allowed.
7969 *
7970 * @note locks this object for reading.
7971 */
7972bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7973 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7974 bool aRequireVM /*= false*/,
7975 bool aAllowClosing /*= false*/)
7976{
7977 AutoLimitedCaller autoCaller(this);
7978 AssertComRCReturn(autoCaller.rc(), false);
7979
7980 /* just return false for inaccessible machines */
7981 if (getObjectState().getState() != ObjectState::Ready)
7982 return false;
7983
7984 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7985
7986 if ( ( mData->mSession.mState == SessionState_Locked
7987 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7988 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7989 )
7990 {
7991 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7992
7993 aMachine = mData->mSession.mMachine;
7994
7995 if (aControl != NULL)
7996 *aControl = mData->mSession.mDirectControl;
7997
7998 return true;
7999 }
8000
8001 return false;
8002}
8003
8004/**
8005 * Returns @c true if the given machine has an spawning direct session.
8006 *
8007 * @note locks this object for reading.
8008 */
8009bool Machine::i_isSessionSpawning()
8010{
8011 AutoLimitedCaller autoCaller(this);
8012 AssertComRCReturn(autoCaller.rc(), false);
8013
8014 /* just return false for inaccessible machines */
8015 if (getObjectState().getState() != ObjectState::Ready)
8016 return false;
8017
8018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8019
8020 if (mData->mSession.mState == SessionState_Spawning)
8021 return true;
8022
8023 return false;
8024}
8025
8026/**
8027 * Called from the client watcher thread to check for unexpected client process
8028 * death during Session_Spawning state (e.g. before it successfully opened a
8029 * direct session).
8030 *
8031 * On Win32 and on OS/2, this method is called only when we've got the
8032 * direct client's process termination notification, so it always returns @c
8033 * true.
8034 *
8035 * On other platforms, this method returns @c true if the client process is
8036 * terminated and @c false if it's still alive.
8037 *
8038 * @note Locks this object for writing.
8039 */
8040bool Machine::i_checkForSpawnFailure()
8041{
8042 AutoCaller autoCaller(this);
8043 if (!autoCaller.isOk())
8044 {
8045 /* nothing to do */
8046 LogFlowThisFunc(("Already uninitialized!\n"));
8047 return true;
8048 }
8049
8050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8051
8052 if (mData->mSession.mState != SessionState_Spawning)
8053 {
8054 /* nothing to do */
8055 LogFlowThisFunc(("Not spawning any more!\n"));
8056 return true;
8057 }
8058
8059 HRESULT rc = S_OK;
8060
8061 /* PID not yet initialized, skip check. */
8062 if (mData->mSession.mPID == NIL_RTPROCESS)
8063 return false;
8064
8065 RTPROCSTATUS status;
8066 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8067
8068 if (vrc != VERR_PROCESS_RUNNING)
8069 {
8070 Utf8Str strExtraInfo;
8071
8072#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8073 /* If the startup logfile exists and is of non-zero length, tell the
8074 user to look there for more details to encourage them to attach it
8075 when reporting startup issues. */
8076 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8077 uint64_t cbStartupLogFile = 0;
8078 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8079 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8080 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8081#endif
8082
8083 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8084 rc = setError(E_FAIL,
8085 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8086 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8087 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8088 rc = setError(E_FAIL,
8089 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8090 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8091 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8092 rc = setError(E_FAIL,
8093 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8094 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8095 else
8096 rc = setError(E_FAIL,
8097 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8098 i_getName().c_str(), vrc, strExtraInfo.c_str());
8099 }
8100
8101 if (FAILED(rc))
8102 {
8103 /* Close the remote session, remove the remote control from the list
8104 * and reset session state to Closed (@note keep the code in sync with
8105 * the relevant part in LockMachine()). */
8106
8107 Assert(mData->mSession.mRemoteControls.size() == 1);
8108 if (mData->mSession.mRemoteControls.size() == 1)
8109 {
8110 ErrorInfoKeeper eik;
8111 mData->mSession.mRemoteControls.front()->Uninitialize();
8112 }
8113
8114 mData->mSession.mRemoteControls.clear();
8115 mData->mSession.mState = SessionState_Unlocked;
8116
8117 /* finalize the progress after setting the state */
8118 if (!mData->mSession.mProgress.isNull())
8119 {
8120 mData->mSession.mProgress->notifyComplete(rc);
8121 mData->mSession.mProgress.setNull();
8122 }
8123
8124 mData->mSession.mPID = NIL_RTPROCESS;
8125
8126 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8127 return true;
8128 }
8129
8130 return false;
8131}
8132
8133/**
8134 * Checks whether the machine can be registered. If so, commits and saves
8135 * all settings.
8136 *
8137 * @note Must be called from mParent's write lock. Locks this object and
8138 * children for writing.
8139 */
8140HRESULT Machine::i_prepareRegister()
8141{
8142 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8143
8144 AutoLimitedCaller autoCaller(this);
8145 AssertComRCReturnRC(autoCaller.rc());
8146
8147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8148
8149 /* wait for state dependents to drop to zero */
8150 i_ensureNoStateDependencies();
8151
8152 if (!mData->mAccessible)
8153 return setError(VBOX_E_INVALID_OBJECT_STATE,
8154 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8155 mUserData->s.strName.c_str(),
8156 mData->mUuid.toString().c_str());
8157
8158 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8159
8160 if (mData->mRegistered)
8161 return setError(VBOX_E_INVALID_OBJECT_STATE,
8162 tr("The machine '%s' with UUID {%s} is already registered"),
8163 mUserData->s.strName.c_str(),
8164 mData->mUuid.toString().c_str());
8165
8166 HRESULT rc = S_OK;
8167
8168 // Ensure the settings are saved. If we are going to be registered and
8169 // no config file exists yet, create it by calling i_saveSettings() too.
8170 if ( (mData->flModifications)
8171 || (!mData->pMachineConfigFile->fileExists())
8172 )
8173 {
8174 rc = i_saveSettings(NULL);
8175 // no need to check whether VirtualBox.xml needs saving too since
8176 // we can't have a machine XML file rename pending
8177 if (FAILED(rc)) return rc;
8178 }
8179
8180 /* more config checking goes here */
8181
8182 if (SUCCEEDED(rc))
8183 {
8184 /* we may have had implicit modifications we want to fix on success */
8185 i_commit();
8186
8187 mData->mRegistered = true;
8188 }
8189 else
8190 {
8191 /* we may have had implicit modifications we want to cancel on failure*/
8192 i_rollback(false /* aNotify */);
8193 }
8194
8195 return rc;
8196}
8197
8198/**
8199 * Increases the number of objects dependent on the machine state or on the
8200 * registered state. Guarantees that these two states will not change at least
8201 * until #i_releaseStateDependency() is called.
8202 *
8203 * Depending on the @a aDepType value, additional state checks may be made.
8204 * These checks will set extended error info on failure. See
8205 * #i_checkStateDependency() for more info.
8206 *
8207 * If this method returns a failure, the dependency is not added and the caller
8208 * is not allowed to rely on any particular machine state or registration state
8209 * value and may return the failed result code to the upper level.
8210 *
8211 * @param aDepType Dependency type to add.
8212 * @param aState Current machine state (NULL if not interested).
8213 * @param aRegistered Current registered state (NULL if not interested).
8214 *
8215 * @note Locks this object for writing.
8216 */
8217HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8218 MachineState_T *aState /* = NULL */,
8219 BOOL *aRegistered /* = NULL */)
8220{
8221 AutoCaller autoCaller(this);
8222 AssertComRCReturnRC(autoCaller.rc());
8223
8224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8225
8226 HRESULT rc = i_checkStateDependency(aDepType);
8227 if (FAILED(rc)) return rc;
8228
8229 {
8230 if (mData->mMachineStateChangePending != 0)
8231 {
8232 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8233 * drop to zero so don't add more. It may make sense to wait a bit
8234 * and retry before reporting an error (since the pending state
8235 * transition should be really quick) but let's just assert for
8236 * now to see if it ever happens on practice. */
8237
8238 AssertFailed();
8239
8240 return setError(E_ACCESSDENIED,
8241 tr("Machine state change is in progress. Please retry the operation later."));
8242 }
8243
8244 ++mData->mMachineStateDeps;
8245 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8246 }
8247
8248 if (aState)
8249 *aState = mData->mMachineState;
8250 if (aRegistered)
8251 *aRegistered = mData->mRegistered;
8252
8253 return S_OK;
8254}
8255
8256/**
8257 * Decreases the number of objects dependent on the machine state.
8258 * Must always complete the #i_addStateDependency() call after the state
8259 * dependency is no more necessary.
8260 */
8261void Machine::i_releaseStateDependency()
8262{
8263 AutoCaller autoCaller(this);
8264 AssertComRCReturnVoid(autoCaller.rc());
8265
8266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8267
8268 /* releaseStateDependency() w/o addStateDependency()? */
8269 AssertReturnVoid(mData->mMachineStateDeps != 0);
8270 -- mData->mMachineStateDeps;
8271
8272 if (mData->mMachineStateDeps == 0)
8273 {
8274 /* inform i_ensureNoStateDependencies() that there are no more deps */
8275 if (mData->mMachineStateChangePending != 0)
8276 {
8277 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8278 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8279 }
8280 }
8281}
8282
8283Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8284{
8285 /* start with nothing found */
8286 Utf8Str strResult("");
8287
8288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8289
8290 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8291 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8292 // found:
8293 strResult = it->second; // source is a Utf8Str
8294
8295 return strResult;
8296}
8297
8298// protected methods
8299/////////////////////////////////////////////////////////////////////////////
8300
8301/**
8302 * Performs machine state checks based on the @a aDepType value. If a check
8303 * fails, this method will set extended error info, otherwise it will return
8304 * S_OK. It is supposed, that on failure, the caller will immediately return
8305 * the return value of this method to the upper level.
8306 *
8307 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8308 *
8309 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8310 * current state of this machine object allows to change settings of the
8311 * machine (i.e. the machine is not registered, or registered but not running
8312 * and not saved). It is useful to call this method from Machine setters
8313 * before performing any change.
8314 *
8315 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8316 * as for MutableStateDep except that if the machine is saved, S_OK is also
8317 * returned. This is useful in setters which allow changing machine
8318 * properties when it is in the saved state.
8319 *
8320 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8321 * if the current state of this machine object allows to change runtime
8322 * changeable settings of the machine (i.e. the machine is not registered, or
8323 * registered but either running or not running and not saved). It is useful
8324 * to call this method from Machine setters before performing any changes to
8325 * runtime changeable settings.
8326 *
8327 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8328 * the same as for MutableOrRunningStateDep except that if the machine is
8329 * saved, S_OK is also returned. This is useful in setters which allow
8330 * changing runtime and saved state changeable machine properties.
8331 *
8332 * @param aDepType Dependency type to check.
8333 *
8334 * @note Non Machine based classes should use #i_addStateDependency() and
8335 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8336 * template.
8337 *
8338 * @note This method must be called from under this object's read or write
8339 * lock.
8340 */
8341HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8342{
8343 switch (aDepType)
8344 {
8345 case AnyStateDep:
8346 {
8347 break;
8348 }
8349 case MutableStateDep:
8350 {
8351 if ( mData->mRegistered
8352 && ( !i_isSessionMachine()
8353 || ( mData->mMachineState != MachineState_Aborted
8354 && mData->mMachineState != MachineState_Teleported
8355 && mData->mMachineState != MachineState_PoweredOff
8356 )
8357 )
8358 )
8359 return setError(VBOX_E_INVALID_VM_STATE,
8360 tr("The machine is not mutable (state is %s)"),
8361 Global::stringifyMachineState(mData->mMachineState));
8362 break;
8363 }
8364 case MutableOrSavedStateDep:
8365 {
8366 if ( mData->mRegistered
8367 && ( !i_isSessionMachine()
8368 || ( mData->mMachineState != MachineState_Aborted
8369 && mData->mMachineState != MachineState_Teleported
8370 && mData->mMachineState != MachineState_Saved
8371 && mData->mMachineState != MachineState_PoweredOff
8372 )
8373 )
8374 )
8375 return setError(VBOX_E_INVALID_VM_STATE,
8376 tr("The machine is not mutable or saved (state is %s)"),
8377 Global::stringifyMachineState(mData->mMachineState));
8378 break;
8379 }
8380 case MutableOrRunningStateDep:
8381 {
8382 if ( mData->mRegistered
8383 && ( !i_isSessionMachine()
8384 || ( mData->mMachineState != MachineState_Aborted
8385 && mData->mMachineState != MachineState_Teleported
8386 && mData->mMachineState != MachineState_PoweredOff
8387 && !Global::IsOnline(mData->mMachineState)
8388 )
8389 )
8390 )
8391 return setError(VBOX_E_INVALID_VM_STATE,
8392 tr("The machine is not mutable or running (state is %s)"),
8393 Global::stringifyMachineState(mData->mMachineState));
8394 break;
8395 }
8396 case MutableOrSavedOrRunningStateDep:
8397 {
8398 if ( mData->mRegistered
8399 && ( !i_isSessionMachine()
8400 || ( mData->mMachineState != MachineState_Aborted
8401 && mData->mMachineState != MachineState_Teleported
8402 && mData->mMachineState != MachineState_Saved
8403 && mData->mMachineState != MachineState_PoweredOff
8404 && !Global::IsOnline(mData->mMachineState)
8405 )
8406 )
8407 )
8408 return setError(VBOX_E_INVALID_VM_STATE,
8409 tr("The machine is not mutable, saved or running (state is %s)"),
8410 Global::stringifyMachineState(mData->mMachineState));
8411 break;
8412 }
8413 }
8414
8415 return S_OK;
8416}
8417
8418/**
8419 * Helper to initialize all associated child objects and allocate data
8420 * structures.
8421 *
8422 * This method must be called as a part of the object's initialization procedure
8423 * (usually done in the #init() method).
8424 *
8425 * @note Must be called only from #init() or from #i_registeredInit().
8426 */
8427HRESULT Machine::initDataAndChildObjects()
8428{
8429 AutoCaller autoCaller(this);
8430 AssertComRCReturnRC(autoCaller.rc());
8431 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8432 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8433
8434 AssertReturn(!mData->mAccessible, E_FAIL);
8435
8436 /* allocate data structures */
8437 mSSData.allocate();
8438 mUserData.allocate();
8439 mHWData.allocate();
8440 mMediumAttachments.allocate();
8441 mStorageControllers.allocate();
8442 mUSBControllers.allocate();
8443
8444 /* initialize mOSTypeId */
8445 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8446
8447/** @todo r=bird: init() methods never fails, right? Why don't we make them
8448 * return void then! */
8449
8450 /* create associated BIOS settings object */
8451 unconst(mBIOSSettings).createObject();
8452 mBIOSSettings->init(this);
8453
8454 /* create an associated VRDE object (default is disabled) */
8455 unconst(mVRDEServer).createObject();
8456 mVRDEServer->init(this);
8457
8458 /* create associated serial port objects */
8459 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8460 {
8461 unconst(mSerialPorts[slot]).createObject();
8462 mSerialPorts[slot]->init(this, slot);
8463 }
8464
8465 /* create associated parallel port objects */
8466 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8467 {
8468 unconst(mParallelPorts[slot]).createObject();
8469 mParallelPorts[slot]->init(this, slot);
8470 }
8471
8472 /* create the audio adapter object (always present, default is disabled) */
8473 unconst(mAudioAdapter).createObject();
8474 mAudioAdapter->init(this);
8475
8476 /* create the USB device filters object (always present) */
8477 unconst(mUSBDeviceFilters).createObject();
8478 mUSBDeviceFilters->init(this);
8479
8480 /* create associated network adapter objects */
8481 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8482 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8483 {
8484 unconst(mNetworkAdapters[slot]).createObject();
8485 mNetworkAdapters[slot]->init(this, slot);
8486 }
8487
8488 /* create the bandwidth control */
8489 unconst(mBandwidthControl).createObject();
8490 mBandwidthControl->init(this);
8491
8492 return S_OK;
8493}
8494
8495/**
8496 * Helper to uninitialize all associated child objects and to free all data
8497 * structures.
8498 *
8499 * This method must be called as a part of the object's uninitialization
8500 * procedure (usually done in the #uninit() method).
8501 *
8502 * @note Must be called only from #uninit() or from #i_registeredInit().
8503 */
8504void Machine::uninitDataAndChildObjects()
8505{
8506 AutoCaller autoCaller(this);
8507 AssertComRCReturnVoid(autoCaller.rc());
8508 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8509 || getObjectState().getState() == ObjectState::Limited);
8510
8511 /* tell all our other child objects we've been uninitialized */
8512 if (mBandwidthControl)
8513 {
8514 mBandwidthControl->uninit();
8515 unconst(mBandwidthControl).setNull();
8516 }
8517
8518 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8519 {
8520 if (mNetworkAdapters[slot])
8521 {
8522 mNetworkAdapters[slot]->uninit();
8523 unconst(mNetworkAdapters[slot]).setNull();
8524 }
8525 }
8526
8527 if (mUSBDeviceFilters)
8528 {
8529 mUSBDeviceFilters->uninit();
8530 unconst(mUSBDeviceFilters).setNull();
8531 }
8532
8533 if (mAudioAdapter)
8534 {
8535 mAudioAdapter->uninit();
8536 unconst(mAudioAdapter).setNull();
8537 }
8538
8539 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8540 {
8541 if (mParallelPorts[slot])
8542 {
8543 mParallelPorts[slot]->uninit();
8544 unconst(mParallelPorts[slot]).setNull();
8545 }
8546 }
8547
8548 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8549 {
8550 if (mSerialPorts[slot])
8551 {
8552 mSerialPorts[slot]->uninit();
8553 unconst(mSerialPorts[slot]).setNull();
8554 }
8555 }
8556
8557 if (mVRDEServer)
8558 {
8559 mVRDEServer->uninit();
8560 unconst(mVRDEServer).setNull();
8561 }
8562
8563 if (mBIOSSettings)
8564 {
8565 mBIOSSettings->uninit();
8566 unconst(mBIOSSettings).setNull();
8567 }
8568
8569 /* Deassociate media (only when a real Machine or a SnapshotMachine
8570 * instance is uninitialized; SessionMachine instances refer to real
8571 * Machine media). This is necessary for a clean re-initialization of
8572 * the VM after successfully re-checking the accessibility state. Note
8573 * that in case of normal Machine or SnapshotMachine uninitialization (as
8574 * a result of unregistering or deleting the snapshot), outdated media
8575 * attachments will already be uninitialized and deleted, so this
8576 * code will not affect them. */
8577 if ( !mMediumAttachments.isNull()
8578 && !i_isSessionMachine()
8579 )
8580 {
8581 for (MediumAttachmentList::const_iterator
8582 it = mMediumAttachments->begin();
8583 it != mMediumAttachments->end();
8584 ++it)
8585 {
8586 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8587 if (pMedium.isNull())
8588 continue;
8589 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8590 AssertComRC(rc);
8591 }
8592 }
8593
8594 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8595 {
8596 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8597 if (mData->mFirstSnapshot)
8598 {
8599 // snapshots tree is protected by machine write lock; strictly
8600 // this isn't necessary here since we're deleting the entire
8601 // machine, but otherwise we assert in Snapshot::uninit()
8602 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8603 mData->mFirstSnapshot->uninit();
8604 mData->mFirstSnapshot.setNull();
8605 }
8606
8607 mData->mCurrentSnapshot.setNull();
8608 }
8609
8610 /* free data structures (the essential mData structure is not freed here
8611 * since it may be still in use) */
8612 mMediumAttachments.free();
8613 mStorageControllers.free();
8614 mUSBControllers.free();
8615 mHWData.free();
8616 mUserData.free();
8617 mSSData.free();
8618}
8619
8620/**
8621 * Returns a pointer to the Machine object for this machine that acts like a
8622 * parent for complex machine data objects such as shared folders, etc.
8623 *
8624 * For primary Machine objects and for SnapshotMachine objects, returns this
8625 * object's pointer itself. For SessionMachine objects, returns the peer
8626 * (primary) machine pointer.
8627 */
8628Machine *Machine::i_getMachine()
8629{
8630 if (i_isSessionMachine())
8631 return (Machine*)mPeer;
8632 return this;
8633}
8634
8635/**
8636 * Makes sure that there are no machine state dependents. If necessary, waits
8637 * for the number of dependents to drop to zero.
8638 *
8639 * Make sure this method is called from under this object's write lock to
8640 * guarantee that no new dependents may be added when this method returns
8641 * control to the caller.
8642 *
8643 * @note Locks this object for writing. The lock will be released while waiting
8644 * (if necessary).
8645 *
8646 * @warning To be used only in methods that change the machine state!
8647 */
8648void Machine::i_ensureNoStateDependencies()
8649{
8650 AssertReturnVoid(isWriteLockOnCurrentThread());
8651
8652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8653
8654 /* Wait for all state dependents if necessary */
8655 if (mData->mMachineStateDeps != 0)
8656 {
8657 /* lazy semaphore creation */
8658 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8659 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8660
8661 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8662 mData->mMachineStateDeps));
8663
8664 ++mData->mMachineStateChangePending;
8665
8666 /* reset the semaphore before waiting, the last dependent will signal
8667 * it */
8668 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8669
8670 alock.release();
8671
8672 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8673
8674 alock.acquire();
8675
8676 -- mData->mMachineStateChangePending;
8677 }
8678}
8679
8680/**
8681 * Changes the machine state and informs callbacks.
8682 *
8683 * This method is not intended to fail so it either returns S_OK or asserts (and
8684 * returns a failure).
8685 *
8686 * @note Locks this object for writing.
8687 */
8688HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8689{
8690 LogFlowThisFuncEnter();
8691 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8692 Assert(aMachineState != MachineState_Null);
8693
8694 AutoCaller autoCaller(this);
8695 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8696
8697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8698
8699 /* wait for state dependents to drop to zero */
8700 i_ensureNoStateDependencies();
8701
8702 MachineState_T const enmOldState = mData->mMachineState;
8703 if (enmOldState != aMachineState)
8704 {
8705 mData->mMachineState = aMachineState;
8706 RTTimeNow(&mData->mLastStateChange);
8707
8708#ifdef VBOX_WITH_DTRACE_R3_MAIN
8709 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8710#endif
8711 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8712 }
8713
8714 LogFlowThisFuncLeave();
8715 return S_OK;
8716}
8717
8718/**
8719 * Searches for a shared folder with the given logical name
8720 * in the collection of shared folders.
8721 *
8722 * @param aName logical name of the shared folder
8723 * @param aSharedFolder where to return the found object
8724 * @param aSetError whether to set the error info if the folder is
8725 * not found
8726 * @return
8727 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8728 *
8729 * @note
8730 * must be called from under the object's lock!
8731 */
8732HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8733 ComObjPtr<SharedFolder> &aSharedFolder,
8734 bool aSetError /* = false */)
8735{
8736 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8737 for (HWData::SharedFolderList::const_iterator
8738 it = mHWData->mSharedFolders.begin();
8739 it != mHWData->mSharedFolders.end();
8740 ++it)
8741 {
8742 SharedFolder *pSF = *it;
8743 AutoCaller autoCaller(pSF);
8744 if (pSF->i_getName() == aName)
8745 {
8746 aSharedFolder = pSF;
8747 rc = S_OK;
8748 break;
8749 }
8750 }
8751
8752 if (aSetError && FAILED(rc))
8753 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8754
8755 return rc;
8756}
8757
8758/**
8759 * Initializes all machine instance data from the given settings structures
8760 * from XML. The exception is the machine UUID which needs special handling
8761 * depending on the caller's use case, so the caller needs to set that herself.
8762 *
8763 * This gets called in several contexts during machine initialization:
8764 *
8765 * -- When machine XML exists on disk already and needs to be loaded into memory,
8766 * for example, from #i_registeredInit() to load all registered machines on
8767 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8768 * attached to the machine should be part of some media registry already.
8769 *
8770 * -- During OVF import, when a machine config has been constructed from an
8771 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8772 * ensure that the media listed as attachments in the config (which have
8773 * been imported from the OVF) receive the correct registry ID.
8774 *
8775 * -- During VM cloning.
8776 *
8777 * @param config Machine settings from XML.
8778 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8779 * for each attached medium in the config.
8780 * @return
8781 */
8782HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8783 const Guid *puuidRegistry)
8784{
8785 // copy name, description, OS type, teleporter, UTC etc.
8786 mUserData->s = config.machineUserData;
8787
8788 // look up the object by Id to check it is valid
8789 ComObjPtr<GuestOSType> pGuestOSType;
8790 HRESULT rc = mParent->i_findGuestOSType(mUserData->s.strOsType,
8791 pGuestOSType);
8792 if (FAILED(rc)) return rc;
8793 mUserData->s.strOsType = pGuestOSType->i_id();
8794
8795 // stateFile (optional)
8796 if (config.strStateFile.isEmpty())
8797 mSSData->strStateFilePath.setNull();
8798 else
8799 {
8800 Utf8Str stateFilePathFull(config.strStateFile);
8801 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8802 if (RT_FAILURE(vrc))
8803 return setError(E_FAIL,
8804 tr("Invalid saved state file path '%s' (%Rrc)"),
8805 config.strStateFile.c_str(),
8806 vrc);
8807 mSSData->strStateFilePath = stateFilePathFull;
8808 }
8809
8810 // snapshot folder needs special processing so set it again
8811 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8812 if (FAILED(rc)) return rc;
8813
8814 /* Copy the extra data items (config may or may not be the same as
8815 * mData->pMachineConfigFile) if necessary. When loading the XML files
8816 * from disk they are the same, but not for OVF import. */
8817 if (mData->pMachineConfigFile != &config)
8818 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8819
8820 /* currentStateModified (optional, default is true) */
8821 mData->mCurrentStateModified = config.fCurrentStateModified;
8822
8823 mData->mLastStateChange = config.timeLastStateChange;
8824
8825 /*
8826 * note: all mUserData members must be assigned prior this point because
8827 * we need to commit changes in order to let mUserData be shared by all
8828 * snapshot machine instances.
8829 */
8830 mUserData.commitCopy();
8831
8832 // machine registry, if present (must be loaded before snapshots)
8833 if (config.canHaveOwnMediaRegistry())
8834 {
8835 // determine machine folder
8836 Utf8Str strMachineFolder = i_getSettingsFileFull();
8837 strMachineFolder.stripFilename();
8838 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8839 config.mediaRegistry,
8840 strMachineFolder);
8841 if (FAILED(rc)) return rc;
8842 }
8843
8844 /* Snapshot node (optional) */
8845 size_t cRootSnapshots;
8846 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8847 {
8848 // there must be only one root snapshot
8849 Assert(cRootSnapshots == 1);
8850
8851 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8852
8853 rc = i_loadSnapshot(snap,
8854 config.uuidCurrentSnapshot,
8855 NULL); // no parent == first snapshot
8856 if (FAILED(rc)) return rc;
8857 }
8858
8859 // hardware data
8860 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8861 if (FAILED(rc)) return rc;
8862
8863 /*
8864 * NOTE: the assignment below must be the last thing to do,
8865 * otherwise it will be not possible to change the settings
8866 * somewhere in the code above because all setters will be
8867 * blocked by i_checkStateDependency(MutableStateDep).
8868 */
8869
8870 /* set the machine state to Aborted or Saved when appropriate */
8871 if (config.fAborted)
8872 {
8873 mSSData->strStateFilePath.setNull();
8874
8875 /* no need to use i_setMachineState() during init() */
8876 mData->mMachineState = MachineState_Aborted;
8877 }
8878 else if (!mSSData->strStateFilePath.isEmpty())
8879 {
8880 /* no need to use i_setMachineState() during init() */
8881 mData->mMachineState = MachineState_Saved;
8882 }
8883
8884 // after loading settings, we are no longer different from the XML on disk
8885 mData->flModifications = 0;
8886
8887 return S_OK;
8888}
8889
8890/**
8891 * Recursively loads all snapshots starting from the given.
8892 *
8893 * @param data snapshot settings.
8894 * @param aCurSnapshotId Current snapshot ID from the settings file.
8895 * @param aParentSnapshot Parent snapshot.
8896 */
8897HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8898 const Guid &aCurSnapshotId,
8899 Snapshot *aParentSnapshot)
8900{
8901 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8902 AssertReturn(!i_isSessionMachine(), E_FAIL);
8903
8904 HRESULT rc = S_OK;
8905
8906 Utf8Str strStateFile;
8907 if (!data.strStateFile.isEmpty())
8908 {
8909 /* optional */
8910 strStateFile = data.strStateFile;
8911 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8912 if (RT_FAILURE(vrc))
8913 return setError(E_FAIL,
8914 tr("Invalid saved state file path '%s' (%Rrc)"),
8915 strStateFile.c_str(),
8916 vrc);
8917 }
8918
8919 /* create a snapshot machine object */
8920 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8921 pSnapshotMachine.createObject();
8922 rc = pSnapshotMachine->initFromSettings(this,
8923 data.hardware,
8924 &data.debugging,
8925 &data.autostart,
8926 data.uuid.ref(),
8927 strStateFile);
8928 if (FAILED(rc)) return rc;
8929
8930 /* create a snapshot object */
8931 ComObjPtr<Snapshot> pSnapshot;
8932 pSnapshot.createObject();
8933 /* initialize the snapshot */
8934 rc = pSnapshot->init(mParent, // VirtualBox object
8935 data.uuid,
8936 data.strName,
8937 data.strDescription,
8938 data.timestamp,
8939 pSnapshotMachine,
8940 aParentSnapshot);
8941 if (FAILED(rc)) return rc;
8942
8943 /* memorize the first snapshot if necessary */
8944 if (!mData->mFirstSnapshot)
8945 mData->mFirstSnapshot = pSnapshot;
8946
8947 /* memorize the current snapshot when appropriate */
8948 if ( !mData->mCurrentSnapshot
8949 && pSnapshot->i_getId() == aCurSnapshotId
8950 )
8951 mData->mCurrentSnapshot = pSnapshot;
8952
8953 // now create the children
8954 for (settings::SnapshotsList::const_iterator
8955 it = data.llChildSnapshots.begin();
8956 it != data.llChildSnapshots.end();
8957 ++it)
8958 {
8959 const settings::Snapshot &childData = *it;
8960 // recurse
8961 rc = i_loadSnapshot(childData,
8962 aCurSnapshotId,
8963 pSnapshot); // parent = the one we created above
8964 if (FAILED(rc)) return rc;
8965 }
8966
8967 return rc;
8968}
8969
8970/**
8971 * Loads settings into mHWData.
8972 *
8973 * @param puuidRegistry Registry ID.
8974 * @param puuidSnapshot Snapshot ID
8975 * @param data Reference to the hardware settings.
8976 * @param pDbg Pointer to the debugging settings.
8977 * @param pAutostart Pointer to the autostart settings.
8978 */
8979HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8980 const Guid *puuidSnapshot,
8981 const settings::Hardware &data,
8982 const settings::Debugging *pDbg,
8983 const settings::Autostart *pAutostart)
8984{
8985 AssertReturn(!i_isSessionMachine(), E_FAIL);
8986
8987 HRESULT rc = S_OK;
8988
8989 try
8990 {
8991 ComObjPtr<GuestOSType> pGuestOSType;
8992 rc = mParent->i_findGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8993 pGuestOSType);
8994 if (FAILED(rc))
8995 return rc;
8996
8997 /* The hardware version attribute (optional). */
8998 mHWData->mHWVersion = data.strVersion;
8999 mHWData->mHardwareUUID = data.uuid;
9000
9001 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9002 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9003 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9004 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9005 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9006 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9007 mHWData->mPAEEnabled = data.fPAE;
9008 mHWData->mLongMode = data.enmLongMode;
9009 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9010 mHWData->mAPIC = data.fAPIC;
9011 mHWData->mX2APIC = data.fX2APIC;
9012 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9013 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9014 mHWData->mCPUCount = data.cCPUs;
9015 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9016 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9017 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9018 mHWData->mCpuProfile = data.strCpuProfile;
9019
9020 // cpu
9021 if (mHWData->mCPUHotPlugEnabled)
9022 {
9023 for (settings::CpuList::const_iterator
9024 it = data.llCpus.begin();
9025 it != data.llCpus.end();
9026 ++it)
9027 {
9028 const settings::Cpu &cpu = *it;
9029
9030 mHWData->mCPUAttached[cpu.ulId] = true;
9031 }
9032 }
9033
9034 // cpuid leafs
9035 for (settings::CpuIdLeafsList::const_iterator
9036 it = data.llCpuIdLeafs.begin();
9037 it != data.llCpuIdLeafs.end();
9038 ++it)
9039 {
9040 const settings::CpuIdLeaf &rLeaf= *it;
9041 if ( rLeaf.idx < UINT32_C(0x20)
9042 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9043 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9044 mHWData->mCpuIdLeafList.push_back(rLeaf);
9045 /* else: just ignore */
9046 }
9047
9048 mHWData->mMemorySize = data.ulMemorySizeMB;
9049 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9050
9051 // boot order
9052 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9053 {
9054 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9055 if (it == data.mapBootOrder.end())
9056 mHWData->mBootOrder[i] = DeviceType_Null;
9057 else
9058 mHWData->mBootOrder[i] = it->second;
9059 }
9060
9061 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9062 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9063 mHWData->mMonitorCount = data.cMonitors;
9064 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9065 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9066 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9067 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9068 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9069 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9070 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9071 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9072 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9073 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9074 if (!data.strVideoCaptureFile.isEmpty())
9075 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9076 else
9077 mHWData->mVideoCaptureFile.setNull();
9078 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9079 mHWData->mFirmwareType = data.firmwareType;
9080 mHWData->mPointingHIDType = data.pointingHIDType;
9081 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9082 mHWData->mChipsetType = data.chipsetType;
9083 mHWData->mParavirtProvider = data.paravirtProvider;
9084 mHWData->mParavirtDebug = data.strParavirtDebug;
9085 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9086 mHWData->mHPETEnabled = data.fHPETEnabled;
9087
9088 /* VRDEServer */
9089 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9090 if (FAILED(rc)) return rc;
9091
9092 /* BIOS */
9093 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9094 if (FAILED(rc)) return rc;
9095
9096 // Bandwidth control (must come before network adapters)
9097 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9098 if (FAILED(rc)) return rc;
9099
9100 /* Shared folders */
9101 for (settings::USBControllerList::const_iterator
9102 it = data.usbSettings.llUSBControllers.begin();
9103 it != data.usbSettings.llUSBControllers.end();
9104 ++it)
9105 {
9106 const settings::USBController &settingsCtrl = *it;
9107 ComObjPtr<USBController> newCtrl;
9108
9109 newCtrl.createObject();
9110 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9111 mUSBControllers->push_back(newCtrl);
9112 }
9113
9114 /* USB device filters */
9115 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9116 if (FAILED(rc)) return rc;
9117
9118 // network adapters (establish array size first and apply defaults, to
9119 // ensure reading the same settings as we saved, since the list skips
9120 // adapters having defaults)
9121 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9122 size_t oldCount = mNetworkAdapters.size();
9123 if (newCount > oldCount)
9124 {
9125 mNetworkAdapters.resize(newCount);
9126 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9127 {
9128 unconst(mNetworkAdapters[slot]).createObject();
9129 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9130 }
9131 }
9132 else if (newCount < oldCount)
9133 mNetworkAdapters.resize(newCount);
9134 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9135 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9136 for (settings::NetworkAdaptersList::const_iterator
9137 it = data.llNetworkAdapters.begin();
9138 it != data.llNetworkAdapters.end();
9139 ++it)
9140 {
9141 const settings::NetworkAdapter &nic = *it;
9142
9143 /* slot uniqueness is guaranteed by XML Schema */
9144 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9145 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9146 if (FAILED(rc)) return rc;
9147 }
9148
9149 // serial ports (establish defaults first, to ensure reading the same
9150 // settings as we saved, since the list skips ports having defaults)
9151 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9152 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9153 for (settings::SerialPortsList::const_iterator
9154 it = data.llSerialPorts.begin();
9155 it != data.llSerialPorts.end();
9156 ++it)
9157 {
9158 const settings::SerialPort &s = *it;
9159
9160 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9161 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9162 if (FAILED(rc)) return rc;
9163 }
9164
9165 // parallel ports (establish defaults first, to ensure reading the same
9166 // settings as we saved, since the list skips ports having defaults)
9167 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9168 mParallelPorts[i]->i_applyDefaults();
9169 for (settings::ParallelPortsList::const_iterator
9170 it = data.llParallelPorts.begin();
9171 it != data.llParallelPorts.end();
9172 ++it)
9173 {
9174 const settings::ParallelPort &p = *it;
9175
9176 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9177 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9178 if (FAILED(rc)) return rc;
9179 }
9180
9181 /* AudioAdapter */
9182 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9183 if (FAILED(rc)) return rc;
9184
9185 /* storage controllers */
9186 rc = i_loadStorageControllers(data.storage,
9187 puuidRegistry,
9188 puuidSnapshot);
9189 if (FAILED(rc)) return rc;
9190
9191 /* Shared folders */
9192 for (settings::SharedFoldersList::const_iterator
9193 it = data.llSharedFolders.begin();
9194 it != data.llSharedFolders.end();
9195 ++it)
9196 {
9197 const settings::SharedFolder &sf = *it;
9198
9199 ComObjPtr<SharedFolder> sharedFolder;
9200 /* Check for double entries. Not allowed! */
9201 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9202 if (SUCCEEDED(rc))
9203 return setError(VBOX_E_OBJECT_IN_USE,
9204 tr("Shared folder named '%s' already exists"),
9205 sf.strName.c_str());
9206
9207 /* Create the new shared folder. Don't break on error. This will be
9208 * reported when the machine starts. */
9209 sharedFolder.createObject();
9210 rc = sharedFolder->init(i_getMachine(),
9211 sf.strName,
9212 sf.strHostPath,
9213 RT_BOOL(sf.fWritable),
9214 RT_BOOL(sf.fAutoMount),
9215 false /* fFailOnError */);
9216 if (FAILED(rc)) return rc;
9217 mHWData->mSharedFolders.push_back(sharedFolder);
9218 }
9219
9220 // Clipboard
9221 mHWData->mClipboardMode = data.clipboardMode;
9222
9223 // drag'n'drop
9224 mHWData->mDnDMode = data.dndMode;
9225
9226 // guest settings
9227 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9228
9229 // IO settings
9230 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9231 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9232
9233 // Host PCI devices
9234 for (settings::HostPCIDeviceAttachmentList::const_iterator
9235 it = data.pciAttachments.begin();
9236 it != data.pciAttachments.end();
9237 ++it)
9238 {
9239 const settings::HostPCIDeviceAttachment &hpda = *it;
9240 ComObjPtr<PCIDeviceAttachment> pda;
9241
9242 pda.createObject();
9243 pda->i_loadSettings(this, hpda);
9244 mHWData->mPCIDeviceAssignments.push_back(pda);
9245 }
9246
9247 /*
9248 * (The following isn't really real hardware, but it lives in HWData
9249 * for reasons of convenience.)
9250 */
9251
9252#ifdef VBOX_WITH_GUEST_PROPS
9253 /* Guest properties (optional) */
9254
9255 /* Only load transient guest properties for configs which have saved
9256 * state, because there shouldn't be any for powered off VMs. The same
9257 * logic applies for snapshots, as offline snapshots shouldn't have
9258 * any such properties. They confuse the code in various places.
9259 * Note: can't rely on the machine state, as it isn't set yet. */
9260 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9261 /* apologies for the hacky unconst() usage, but this needs hacking
9262 * actually inconsistent settings into consistency, otherwise there
9263 * will be some corner cases where the inconsistency survives
9264 * surprisingly long without getting fixed, especially for snapshots
9265 * as there are no config changes. */
9266 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9267 for (settings::GuestPropertiesList::iterator
9268 it = llGuestProperties.begin();
9269 it != llGuestProperties.end();
9270 /*nothing*/)
9271 {
9272 const settings::GuestProperty &prop = *it;
9273 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9274 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9275 if ( fSkipTransientGuestProperties
9276 && ( fFlags & GUEST_PROP_F_TRANSIENT
9277 || fFlags & GUEST_PROP_F_TRANSRESET))
9278 {
9279 it = llGuestProperties.erase(it);
9280 continue;
9281 }
9282 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9283 mHWData->mGuestProperties[prop.strName] = property;
9284 ++it;
9285 }
9286#endif /* VBOX_WITH_GUEST_PROPS defined */
9287
9288 rc = i_loadDebugging(pDbg);
9289 if (FAILED(rc))
9290 return rc;
9291
9292 mHWData->mAutostart = *pAutostart;
9293
9294 /* default frontend */
9295 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9296 }
9297 catch (std::bad_alloc &)
9298 {
9299 return E_OUTOFMEMORY;
9300 }
9301
9302 AssertComRC(rc);
9303 return rc;
9304}
9305
9306/**
9307 * Called from i_loadHardware() to load the debugging settings of the
9308 * machine.
9309 *
9310 * @param pDbg Pointer to the settings.
9311 */
9312HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9313{
9314 mHWData->mDebugging = *pDbg;
9315 /* no more processing currently required, this will probably change. */
9316 return S_OK;
9317}
9318
9319/**
9320 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9321 *
9322 * @param data storage settings.
9323 * @param puuidRegistry media registry ID to set media to or NULL;
9324 * see Machine::i_loadMachineDataFromSettings()
9325 * @param puuidSnapshot snapshot ID
9326 * @return
9327 */
9328HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9329 const Guid *puuidRegistry,
9330 const Guid *puuidSnapshot)
9331{
9332 AssertReturn(!i_isSessionMachine(), E_FAIL);
9333
9334 HRESULT rc = S_OK;
9335
9336 for (settings::StorageControllersList::const_iterator
9337 it = data.llStorageControllers.begin();
9338 it != data.llStorageControllers.end();
9339 ++it)
9340 {
9341 const settings::StorageController &ctlData = *it;
9342
9343 ComObjPtr<StorageController> pCtl;
9344 /* Try to find one with the name first. */
9345 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9346 if (SUCCEEDED(rc))
9347 return setError(VBOX_E_OBJECT_IN_USE,
9348 tr("Storage controller named '%s' already exists"),
9349 ctlData.strName.c_str());
9350
9351 pCtl.createObject();
9352 rc = pCtl->init(this,
9353 ctlData.strName,
9354 ctlData.storageBus,
9355 ctlData.ulInstance,
9356 ctlData.fBootable);
9357 if (FAILED(rc)) return rc;
9358
9359 mStorageControllers->push_back(pCtl);
9360
9361 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9362 if (FAILED(rc)) return rc;
9363
9364 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9365 if (FAILED(rc)) return rc;
9366
9367 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9368 if (FAILED(rc)) return rc;
9369
9370 /* Load the attached devices now. */
9371 rc = i_loadStorageDevices(pCtl,
9372 ctlData,
9373 puuidRegistry,
9374 puuidSnapshot);
9375 if (FAILED(rc)) return rc;
9376 }
9377
9378 return S_OK;
9379}
9380
9381/**
9382 * Called from i_loadStorageControllers for a controller's devices.
9383 *
9384 * @param aStorageController
9385 * @param data
9386 * @param puuidRegistry media registry ID to set media to or NULL; see
9387 * Machine::i_loadMachineDataFromSettings()
9388 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9389 * @return
9390 */
9391HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9392 const settings::StorageController &data,
9393 const Guid *puuidRegistry,
9394 const Guid *puuidSnapshot)
9395{
9396 HRESULT rc = S_OK;
9397
9398 /* paranoia: detect duplicate attachments */
9399 for (settings::AttachedDevicesList::const_iterator
9400 it = data.llAttachedDevices.begin();
9401 it != data.llAttachedDevices.end();
9402 ++it)
9403 {
9404 const settings::AttachedDevice &ad = *it;
9405
9406 for (settings::AttachedDevicesList::const_iterator it2 = it;
9407 it2 != data.llAttachedDevices.end();
9408 ++it2)
9409 {
9410 if (it == it2)
9411 continue;
9412
9413 const settings::AttachedDevice &ad2 = *it2;
9414
9415 if ( ad.lPort == ad2.lPort
9416 && ad.lDevice == ad2.lDevice)
9417 {
9418 return setError(E_FAIL,
9419 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9420 aStorageController->i_getName().c_str(),
9421 ad.lPort,
9422 ad.lDevice,
9423 mUserData->s.strName.c_str());
9424 }
9425 }
9426 }
9427
9428 for (settings::AttachedDevicesList::const_iterator
9429 it = data.llAttachedDevices.begin();
9430 it != data.llAttachedDevices.end();
9431 ++it)
9432 {
9433 const settings::AttachedDevice &dev = *it;
9434 ComObjPtr<Medium> medium;
9435
9436 switch (dev.deviceType)
9437 {
9438 case DeviceType_Floppy:
9439 case DeviceType_DVD:
9440 if (dev.strHostDriveSrc.isNotEmpty())
9441 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9442 false /* fRefresh */, medium);
9443 else
9444 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9445 dev.uuid,
9446 false /* fRefresh */,
9447 false /* aSetError */,
9448 medium);
9449 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9450 // This is not an error. The host drive or UUID might have vanished, so just go
9451 // ahead without this removeable medium attachment
9452 rc = S_OK;
9453 break;
9454
9455 case DeviceType_HardDisk:
9456 {
9457 /* find a hard disk by UUID */
9458 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9459 if (FAILED(rc))
9460 {
9461 if (i_isSnapshotMachine())
9462 {
9463 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9464 // so the user knows that the bad disk is in a snapshot somewhere
9465 com::ErrorInfo info;
9466 return setError(E_FAIL,
9467 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9468 puuidSnapshot->raw(),
9469 info.getText().raw());
9470 }
9471 else
9472 return rc;
9473 }
9474
9475 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9476
9477 if (medium->i_getType() == MediumType_Immutable)
9478 {
9479 if (i_isSnapshotMachine())
9480 return setError(E_FAIL,
9481 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9482 "of the virtual machine '%s' ('%s')"),
9483 medium->i_getLocationFull().c_str(),
9484 dev.uuid.raw(),
9485 puuidSnapshot->raw(),
9486 mUserData->s.strName.c_str(),
9487 mData->m_strConfigFileFull.c_str());
9488
9489 return setError(E_FAIL,
9490 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9491 medium->i_getLocationFull().c_str(),
9492 dev.uuid.raw(),
9493 mUserData->s.strName.c_str(),
9494 mData->m_strConfigFileFull.c_str());
9495 }
9496
9497 if (medium->i_getType() == MediumType_MultiAttach)
9498 {
9499 if (i_isSnapshotMachine())
9500 return setError(E_FAIL,
9501 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9502 "of the virtual machine '%s' ('%s')"),
9503 medium->i_getLocationFull().c_str(),
9504 dev.uuid.raw(),
9505 puuidSnapshot->raw(),
9506 mUserData->s.strName.c_str(),
9507 mData->m_strConfigFileFull.c_str());
9508
9509 return setError(E_FAIL,
9510 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9511 medium->i_getLocationFull().c_str(),
9512 dev.uuid.raw(),
9513 mUserData->s.strName.c_str(),
9514 mData->m_strConfigFileFull.c_str());
9515 }
9516
9517 if ( !i_isSnapshotMachine()
9518 && medium->i_getChildren().size() != 0
9519 )
9520 return setError(E_FAIL,
9521 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9522 "because it has %d differencing child hard disks"),
9523 medium->i_getLocationFull().c_str(),
9524 dev.uuid.raw(),
9525 mUserData->s.strName.c_str(),
9526 mData->m_strConfigFileFull.c_str(),
9527 medium->i_getChildren().size());
9528
9529 if (i_findAttachment(*mMediumAttachments.data(),
9530 medium))
9531 return setError(E_FAIL,
9532 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9533 medium->i_getLocationFull().c_str(),
9534 dev.uuid.raw(),
9535 mUserData->s.strName.c_str(),
9536 mData->m_strConfigFileFull.c_str());
9537
9538 break;
9539 }
9540
9541 default:
9542 return setError(E_FAIL,
9543 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9544 medium->i_getLocationFull().c_str(),
9545 mUserData->s.strName.c_str(),
9546 mData->m_strConfigFileFull.c_str());
9547 }
9548
9549 if (FAILED(rc))
9550 break;
9551
9552 /* Bandwidth groups are loaded at this point. */
9553 ComObjPtr<BandwidthGroup> pBwGroup;
9554
9555 if (!dev.strBwGroup.isEmpty())
9556 {
9557 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9558 if (FAILED(rc))
9559 return setError(E_FAIL,
9560 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9561 medium->i_getLocationFull().c_str(),
9562 dev.strBwGroup.c_str(),
9563 mUserData->s.strName.c_str(),
9564 mData->m_strConfigFileFull.c_str());
9565 pBwGroup->i_reference();
9566 }
9567
9568 const Utf8Str controllerName = aStorageController->i_getName();
9569 ComObjPtr<MediumAttachment> pAttachment;
9570 pAttachment.createObject();
9571 rc = pAttachment->init(this,
9572 medium,
9573 controllerName,
9574 dev.lPort,
9575 dev.lDevice,
9576 dev.deviceType,
9577 false,
9578 dev.fPassThrough,
9579 dev.fTempEject,
9580 dev.fNonRotational,
9581 dev.fDiscard,
9582 dev.fHotPluggable,
9583 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9584 if (FAILED(rc)) break;
9585
9586 /* associate the medium with this machine and snapshot */
9587 if (!medium.isNull())
9588 {
9589 AutoCaller medCaller(medium);
9590 if (FAILED(medCaller.rc())) return medCaller.rc();
9591 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9592
9593 if (i_isSnapshotMachine())
9594 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9595 else
9596 rc = medium->i_addBackReference(mData->mUuid);
9597 /* If the medium->addBackReference fails it sets an appropriate
9598 * error message, so no need to do any guesswork here. */
9599
9600 if (puuidRegistry)
9601 // caller wants registry ID to be set on all attached media (OVF import case)
9602 medium->i_addRegistry(*puuidRegistry);
9603 }
9604
9605 if (FAILED(rc))
9606 break;
9607
9608 /* back up mMediumAttachments to let registeredInit() properly rollback
9609 * on failure (= limited accessibility) */
9610 i_setModified(IsModified_Storage);
9611 mMediumAttachments.backup();
9612 mMediumAttachments->push_back(pAttachment);
9613 }
9614
9615 return rc;
9616}
9617
9618/**
9619 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9620 *
9621 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9622 * @param aSnapshot where to return the found snapshot
9623 * @param aSetError true to set extended error info on failure
9624 */
9625HRESULT Machine::i_findSnapshotById(const Guid &aId,
9626 ComObjPtr<Snapshot> &aSnapshot,
9627 bool aSetError /* = false */)
9628{
9629 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9630
9631 if (!mData->mFirstSnapshot)
9632 {
9633 if (aSetError)
9634 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9635 return E_FAIL;
9636 }
9637
9638 if (aId.isZero())
9639 aSnapshot = mData->mFirstSnapshot;
9640 else
9641 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9642
9643 if (!aSnapshot)
9644 {
9645 if (aSetError)
9646 return setError(E_FAIL,
9647 tr("Could not find a snapshot with UUID {%s}"),
9648 aId.toString().c_str());
9649 return E_FAIL;
9650 }
9651
9652 return S_OK;
9653}
9654
9655/**
9656 * Returns the snapshot with the given name or fails of no such snapshot.
9657 *
9658 * @param strName snapshot name to find
9659 * @param aSnapshot where to return the found snapshot
9660 * @param aSetError true to set extended error info on failure
9661 */
9662HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9663 ComObjPtr<Snapshot> &aSnapshot,
9664 bool aSetError /* = false */)
9665{
9666 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9667
9668 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9669
9670 if (!mData->mFirstSnapshot)
9671 {
9672 if (aSetError)
9673 return setError(VBOX_E_OBJECT_NOT_FOUND,
9674 tr("This machine does not have any snapshots"));
9675 return VBOX_E_OBJECT_NOT_FOUND;
9676 }
9677
9678 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9679
9680 if (!aSnapshot)
9681 {
9682 if (aSetError)
9683 return setError(VBOX_E_OBJECT_NOT_FOUND,
9684 tr("Could not find a snapshot named '%s'"), strName.c_str());
9685 return VBOX_E_OBJECT_NOT_FOUND;
9686 }
9687
9688 return S_OK;
9689}
9690
9691/**
9692 * Returns a storage controller object with the given name.
9693 *
9694 * @param aName storage controller name to find
9695 * @param aStorageController where to return the found storage controller
9696 * @param aSetError true to set extended error info on failure
9697 */
9698HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9699 ComObjPtr<StorageController> &aStorageController,
9700 bool aSetError /* = false */)
9701{
9702 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9703
9704 for (StorageControllerList::const_iterator
9705 it = mStorageControllers->begin();
9706 it != mStorageControllers->end();
9707 ++it)
9708 {
9709 if ((*it)->i_getName() == aName)
9710 {
9711 aStorageController = (*it);
9712 return S_OK;
9713 }
9714 }
9715
9716 if (aSetError)
9717 return setError(VBOX_E_OBJECT_NOT_FOUND,
9718 tr("Could not find a storage controller named '%s'"),
9719 aName.c_str());
9720 return VBOX_E_OBJECT_NOT_FOUND;
9721}
9722
9723/**
9724 * Returns a USB controller object with the given name.
9725 *
9726 * @param aName USB controller name to find
9727 * @param aUSBController where to return the found USB controller
9728 * @param aSetError true to set extended error info on failure
9729 */
9730HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9731 ComObjPtr<USBController> &aUSBController,
9732 bool aSetError /* = false */)
9733{
9734 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9735
9736 for (USBControllerList::const_iterator
9737 it = mUSBControllers->begin();
9738 it != mUSBControllers->end();
9739 ++it)
9740 {
9741 if ((*it)->i_getName() == aName)
9742 {
9743 aUSBController = (*it);
9744 return S_OK;
9745 }
9746 }
9747
9748 if (aSetError)
9749 return setError(VBOX_E_OBJECT_NOT_FOUND,
9750 tr("Could not find a storage controller named '%s'"),
9751 aName.c_str());
9752 return VBOX_E_OBJECT_NOT_FOUND;
9753}
9754
9755/**
9756 * Returns the number of USB controller instance of the given type.
9757 *
9758 * @param enmType USB controller type.
9759 */
9760ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9761{
9762 ULONG cCtrls = 0;
9763
9764 for (USBControllerList::const_iterator
9765 it = mUSBControllers->begin();
9766 it != mUSBControllers->end();
9767 ++it)
9768 {
9769 if ((*it)->i_getControllerType() == enmType)
9770 cCtrls++;
9771 }
9772
9773 return cCtrls;
9774}
9775
9776HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9777 MediumAttachmentList &atts)
9778{
9779 AutoCaller autoCaller(this);
9780 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9781
9782 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9783
9784 for (MediumAttachmentList::const_iterator
9785 it = mMediumAttachments->begin();
9786 it != mMediumAttachments->end();
9787 ++it)
9788 {
9789 const ComObjPtr<MediumAttachment> &pAtt = *it;
9790 // should never happen, but deal with NULL pointers in the list.
9791 AssertContinue(!pAtt.isNull());
9792
9793 // getControllerName() needs caller+read lock
9794 AutoCaller autoAttCaller(pAtt);
9795 if (FAILED(autoAttCaller.rc()))
9796 {
9797 atts.clear();
9798 return autoAttCaller.rc();
9799 }
9800 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9801
9802 if (pAtt->i_getControllerName() == aName)
9803 atts.push_back(pAtt);
9804 }
9805
9806 return S_OK;
9807}
9808
9809
9810/**
9811 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9812 * file if the machine name was changed and about creating a new settings file
9813 * if this is a new machine.
9814 *
9815 * @note Must be never called directly but only from #saveSettings().
9816 */
9817HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9818{
9819 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9820
9821 HRESULT rc = S_OK;
9822
9823 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9824
9825 /// @todo need to handle primary group change, too
9826
9827 /* attempt to rename the settings file if machine name is changed */
9828 if ( mUserData->s.fNameSync
9829 && mUserData.isBackedUp()
9830 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9831 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9832 )
9833 {
9834 bool dirRenamed = false;
9835 bool fileRenamed = false;
9836
9837 Utf8Str configFile, newConfigFile;
9838 Utf8Str configFilePrev, newConfigFilePrev;
9839 Utf8Str configDir, newConfigDir;
9840
9841 do
9842 {
9843 int vrc = VINF_SUCCESS;
9844
9845 Utf8Str name = mUserData.backedUpData()->s.strName;
9846 Utf8Str newName = mUserData->s.strName;
9847 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9848 if (group == "/")
9849 group.setNull();
9850 Utf8Str newGroup = mUserData->s.llGroups.front();
9851 if (newGroup == "/")
9852 newGroup.setNull();
9853
9854 configFile = mData->m_strConfigFileFull;
9855
9856 /* first, rename the directory if it matches the group and machine name */
9857 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9858 group.c_str(), RTPATH_DELIMITER, name.c_str());
9859 /** @todo hack, make somehow use of ComposeMachineFilename */
9860 if (mUserData->s.fDirectoryIncludesUUID)
9861 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9862 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9863 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9864 /** @todo hack, make somehow use of ComposeMachineFilename */
9865 if (mUserData->s.fDirectoryIncludesUUID)
9866 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9867 configDir = configFile;
9868 configDir.stripFilename();
9869 newConfigDir = configDir;
9870 if ( configDir.length() >= groupPlusName.length()
9871 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9872 groupPlusName.c_str()))
9873 {
9874 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9875 Utf8Str newConfigBaseDir(newConfigDir);
9876 newConfigDir.append(newGroupPlusName);
9877 /* consistency: use \ if appropriate on the platform */
9878 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9879 /* new dir and old dir cannot be equal here because of 'if'
9880 * above and because name != newName */
9881 Assert(configDir != newConfigDir);
9882 if (!fSettingsFileIsNew)
9883 {
9884 /* perform real rename only if the machine is not new */
9885 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9886 if ( vrc == VERR_FILE_NOT_FOUND
9887 || vrc == VERR_PATH_NOT_FOUND)
9888 {
9889 /* create the parent directory, then retry renaming */
9890 Utf8Str parent(newConfigDir);
9891 parent.stripFilename();
9892 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9893 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9894 }
9895 if (RT_FAILURE(vrc))
9896 {
9897 rc = setError(E_FAIL,
9898 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9899 configDir.c_str(),
9900 newConfigDir.c_str(),
9901 vrc);
9902 break;
9903 }
9904 /* delete subdirectories which are no longer needed */
9905 Utf8Str dir(configDir);
9906 dir.stripFilename();
9907 while (dir != newConfigBaseDir && dir != ".")
9908 {
9909 vrc = RTDirRemove(dir.c_str());
9910 if (RT_FAILURE(vrc))
9911 break;
9912 dir.stripFilename();
9913 }
9914 dirRenamed = true;
9915 }
9916 }
9917
9918 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9919 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9920
9921 /* then try to rename the settings file itself */
9922 if (newConfigFile != configFile)
9923 {
9924 /* get the path to old settings file in renamed directory */
9925 configFile = Utf8StrFmt("%s%c%s",
9926 newConfigDir.c_str(),
9927 RTPATH_DELIMITER,
9928 RTPathFilename(configFile.c_str()));
9929 if (!fSettingsFileIsNew)
9930 {
9931 /* perform real rename only if the machine is not new */
9932 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9933 if (RT_FAILURE(vrc))
9934 {
9935 rc = setError(E_FAIL,
9936 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9937 configFile.c_str(),
9938 newConfigFile.c_str(),
9939 vrc);
9940 break;
9941 }
9942 fileRenamed = true;
9943 configFilePrev = configFile;
9944 configFilePrev += "-prev";
9945 newConfigFilePrev = newConfigFile;
9946 newConfigFilePrev += "-prev";
9947 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9948 }
9949 }
9950
9951 // update m_strConfigFileFull amd mConfigFile
9952 mData->m_strConfigFileFull = newConfigFile;
9953 // compute the relative path too
9954 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9955
9956 // store the old and new so that VirtualBox::i_saveSettings() can update
9957 // the media registry
9958 if ( mData->mRegistered
9959 && (configDir != newConfigDir || configFile != newConfigFile))
9960 {
9961 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9962
9963 if (pfNeedsGlobalSaveSettings)
9964 *pfNeedsGlobalSaveSettings = true;
9965 }
9966
9967 // in the saved state file path, replace the old directory with the new directory
9968 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9969 {
9970 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9971 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9972 }
9973
9974 // and do the same thing for the saved state file paths of all the online snapshots
9975 if (mData->mFirstSnapshot)
9976 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9977 newConfigDir.c_str());
9978 }
9979 while (0);
9980
9981 if (FAILED(rc))
9982 {
9983 /* silently try to rename everything back */
9984 if (fileRenamed)
9985 {
9986 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9987 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9988 }
9989 if (dirRenamed)
9990 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9991 }
9992
9993 if (FAILED(rc)) return rc;
9994 }
9995
9996 if (fSettingsFileIsNew)
9997 {
9998 /* create a virgin config file */
9999 int vrc = VINF_SUCCESS;
10000
10001 /* ensure the settings directory exists */
10002 Utf8Str path(mData->m_strConfigFileFull);
10003 path.stripFilename();
10004 if (!RTDirExists(path.c_str()))
10005 {
10006 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10007 if (RT_FAILURE(vrc))
10008 {
10009 return setError(E_FAIL,
10010 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10011 path.c_str(),
10012 vrc);
10013 }
10014 }
10015
10016 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10017 path = Utf8Str(mData->m_strConfigFileFull);
10018 RTFILE f = NIL_RTFILE;
10019 vrc = RTFileOpen(&f, path.c_str(),
10020 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10021 if (RT_FAILURE(vrc))
10022 return setError(E_FAIL,
10023 tr("Could not create the settings file '%s' (%Rrc)"),
10024 path.c_str(),
10025 vrc);
10026 RTFileClose(f);
10027 }
10028
10029 return rc;
10030}
10031
10032/**
10033 * Saves and commits machine data, user data and hardware data.
10034 *
10035 * Note that on failure, the data remains uncommitted.
10036 *
10037 * @a aFlags may combine the following flags:
10038 *
10039 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10040 * Used when saving settings after an operation that makes them 100%
10041 * correspond to the settings from the current snapshot.
10042 * - SaveS_Force: settings will be saved without doing a deep compare of the
10043 * settings structures. This is used when this is called because snapshots
10044 * have changed to avoid the overhead of the deep compare.
10045 *
10046 * @note Must be called from under this object's write lock. Locks children for
10047 * writing.
10048 *
10049 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10050 * initialized to false and that will be set to true by this function if
10051 * the caller must invoke VirtualBox::i_saveSettings() because the global
10052 * settings have changed. This will happen if a machine rename has been
10053 * saved and the global machine and media registries will therefore need
10054 * updating.
10055 * @param aFlags Flags.
10056 */
10057HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10058 int aFlags /*= 0*/)
10059{
10060 LogFlowThisFuncEnter();
10061
10062 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10063
10064 /* make sure child objects are unable to modify the settings while we are
10065 * saving them */
10066 i_ensureNoStateDependencies();
10067
10068 AssertReturn(!i_isSnapshotMachine(),
10069 E_FAIL);
10070
10071 HRESULT rc = S_OK;
10072 bool fNeedsWrite = false;
10073
10074 /* First, prepare to save settings. It will care about renaming the
10075 * settings directory and file if the machine name was changed and about
10076 * creating a new settings file if this is a new machine. */
10077 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10078 if (FAILED(rc)) return rc;
10079
10080 // keep a pointer to the current settings structures
10081 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10082 settings::MachineConfigFile *pNewConfig = NULL;
10083
10084 try
10085 {
10086 // make a fresh one to have everyone write stuff into
10087 pNewConfig = new settings::MachineConfigFile(NULL);
10088 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10089
10090 // now go and copy all the settings data from COM to the settings structures
10091 // (this calls i_saveSettings() on all the COM objects in the machine)
10092 i_copyMachineDataToSettings(*pNewConfig);
10093
10094 if (aFlags & SaveS_ResetCurStateModified)
10095 {
10096 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10097 mData->mCurrentStateModified = FALSE;
10098 fNeedsWrite = true; // always, no need to compare
10099 }
10100 else if (aFlags & SaveS_Force)
10101 {
10102 fNeedsWrite = true; // always, no need to compare
10103 }
10104 else
10105 {
10106 if (!mData->mCurrentStateModified)
10107 {
10108 // do a deep compare of the settings that we just saved with the settings
10109 // previously stored in the config file; this invokes MachineConfigFile::operator==
10110 // which does a deep compare of all the settings, which is expensive but less expensive
10111 // than writing out XML in vain
10112 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10113
10114 // could still be modified if any settings changed
10115 mData->mCurrentStateModified = fAnySettingsChanged;
10116
10117 fNeedsWrite = fAnySettingsChanged;
10118 }
10119 else
10120 fNeedsWrite = true;
10121 }
10122
10123 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10124
10125 if (fNeedsWrite)
10126 // now spit it all out!
10127 pNewConfig->write(mData->m_strConfigFileFull);
10128
10129 mData->pMachineConfigFile = pNewConfig;
10130 delete pOldConfig;
10131 i_commit();
10132
10133 // after saving settings, we are no longer different from the XML on disk
10134 mData->flModifications = 0;
10135 }
10136 catch (HRESULT err)
10137 {
10138 // we assume that error info is set by the thrower
10139 rc = err;
10140
10141 // restore old config
10142 delete pNewConfig;
10143 mData->pMachineConfigFile = pOldConfig;
10144 }
10145 catch (...)
10146 {
10147 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10148 }
10149
10150 if (fNeedsWrite)
10151 {
10152 /* Fire the data change event, even on failure (since we've already
10153 * committed all data). This is done only for SessionMachines because
10154 * mutable Machine instances are always not registered (i.e. private
10155 * to the client process that creates them) and thus don't need to
10156 * inform callbacks. */
10157 if (i_isSessionMachine())
10158 mParent->i_onMachineDataChange(mData->mUuid);
10159 }
10160
10161 LogFlowThisFunc(("rc=%08X\n", rc));
10162 LogFlowThisFuncLeave();
10163 return rc;
10164}
10165
10166/**
10167 * Implementation for saving the machine settings into the given
10168 * settings::MachineConfigFile instance. This copies machine extradata
10169 * from the previous machine config file in the instance data, if any.
10170 *
10171 * This gets called from two locations:
10172 *
10173 * -- Machine::i_saveSettings(), during the regular XML writing;
10174 *
10175 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10176 * exported to OVF and we write the VirtualBox proprietary XML
10177 * into a <vbox:Machine> tag.
10178 *
10179 * This routine fills all the fields in there, including snapshots, *except*
10180 * for the following:
10181 *
10182 * -- fCurrentStateModified. There is some special logic associated with that.
10183 *
10184 * The caller can then call MachineConfigFile::write() or do something else
10185 * with it.
10186 *
10187 * Caller must hold the machine lock!
10188 *
10189 * This throws XML errors and HRESULT, so the caller must have a catch block!
10190 */
10191void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10192{
10193 // deep copy extradata, being extra careful with self assignment (the STL
10194 // map assignment on Mac OS X clang based Xcode isn't checking)
10195 if (&config != mData->pMachineConfigFile)
10196 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10197
10198 config.uuid = mData->mUuid;
10199
10200 // copy name, description, OS type, teleport, UTC etc.
10201 config.machineUserData = mUserData->s;
10202
10203 if ( mData->mMachineState == MachineState_Saved
10204 || mData->mMachineState == MachineState_Restoring
10205 // when doing certain snapshot operations we may or may not have
10206 // a saved state in the current state, so keep everything as is
10207 || ( ( mData->mMachineState == MachineState_Snapshotting
10208 || mData->mMachineState == MachineState_DeletingSnapshot
10209 || mData->mMachineState == MachineState_RestoringSnapshot)
10210 && (!mSSData->strStateFilePath.isEmpty())
10211 )
10212 )
10213 {
10214 Assert(!mSSData->strStateFilePath.isEmpty());
10215 /* try to make the file name relative to the settings file dir */
10216 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10217 }
10218 else
10219 {
10220 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10221 config.strStateFile.setNull();
10222 }
10223
10224 if (mData->mCurrentSnapshot)
10225 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10226 else
10227 config.uuidCurrentSnapshot.clear();
10228
10229 config.timeLastStateChange = mData->mLastStateChange;
10230 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10231 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10232
10233 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10234 if (FAILED(rc)) throw rc;
10235
10236 // save machine's media registry if this is VirtualBox 4.0 or later
10237 if (config.canHaveOwnMediaRegistry())
10238 {
10239 // determine machine folder
10240 Utf8Str strMachineFolder = i_getSettingsFileFull();
10241 strMachineFolder.stripFilename();
10242 mParent->i_saveMediaRegistry(config.mediaRegistry,
10243 i_getId(), // only media with registry ID == machine UUID
10244 strMachineFolder);
10245 // this throws HRESULT
10246 }
10247
10248 // save snapshots
10249 rc = i_saveAllSnapshots(config);
10250 if (FAILED(rc)) throw rc;
10251}
10252
10253/**
10254 * Saves all snapshots of the machine into the given machine config file. Called
10255 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10256 * @param config
10257 * @return
10258 */
10259HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10260{
10261 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10262
10263 HRESULT rc = S_OK;
10264
10265 try
10266 {
10267 config.llFirstSnapshot.clear();
10268
10269 if (mData->mFirstSnapshot)
10270 {
10271 // the settings use a list for "the first snapshot"
10272 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10273
10274 // get reference to the snapshot on the list and work on that
10275 // element straight in the list to avoid excessive copying later
10276 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10277 if (FAILED(rc)) throw rc;
10278 }
10279
10280// if (mType == IsSessionMachine)
10281// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10282
10283 }
10284 catch (HRESULT err)
10285 {
10286 /* we assume that error info is set by the thrower */
10287 rc = err;
10288 }
10289 catch (...)
10290 {
10291 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10292 }
10293
10294 return rc;
10295}
10296
10297/**
10298 * Saves the VM hardware configuration. It is assumed that the
10299 * given node is empty.
10300 *
10301 * @param data Reference to the settings object for the hardware config.
10302 * @param pDbg Pointer to the settings object for the debugging config
10303 * which happens to live in mHWData.
10304 * @param pAutostart Pointer to the settings object for the autostart config
10305 * which happens to live in mHWData.
10306 */
10307HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10308 settings::Autostart *pAutostart)
10309{
10310 HRESULT rc = S_OK;
10311
10312 try
10313 {
10314 /* The hardware version attribute (optional).
10315 Automatically upgrade from 1 to current default hardware version
10316 when there is no saved state. (ugly!) */
10317 if ( mHWData->mHWVersion == "1"
10318 && mSSData->strStateFilePath.isEmpty()
10319 )
10320 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10321
10322 data.strVersion = mHWData->mHWVersion;
10323 data.uuid = mHWData->mHardwareUUID;
10324
10325 // CPU
10326 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10327 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10328 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10329 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10330 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10331 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10332 data.fPAE = !!mHWData->mPAEEnabled;
10333 data.enmLongMode = mHWData->mLongMode;
10334 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10335 data.fAPIC = !!mHWData->mAPIC;
10336 data.fX2APIC = !!mHWData->mX2APIC;
10337 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10338 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10339 data.cCPUs = mHWData->mCPUCount;
10340 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10341 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10342 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10343 data.strCpuProfile = mHWData->mCpuProfile;
10344
10345 data.llCpus.clear();
10346 if (data.fCpuHotPlug)
10347 {
10348 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10349 {
10350 if (mHWData->mCPUAttached[idx])
10351 {
10352 settings::Cpu cpu;
10353 cpu.ulId = idx;
10354 data.llCpus.push_back(cpu);
10355 }
10356 }
10357 }
10358
10359 /* Standard and Extended CPUID leafs. */
10360 data.llCpuIdLeafs.clear();
10361 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10362
10363 // memory
10364 data.ulMemorySizeMB = mHWData->mMemorySize;
10365 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10366
10367 // firmware
10368 data.firmwareType = mHWData->mFirmwareType;
10369
10370 // HID
10371 data.pointingHIDType = mHWData->mPointingHIDType;
10372 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10373
10374 // chipset
10375 data.chipsetType = mHWData->mChipsetType;
10376
10377 // paravirt
10378 data.paravirtProvider = mHWData->mParavirtProvider;
10379 data.strParavirtDebug = mHWData->mParavirtDebug;
10380
10381 // emulated USB card reader
10382 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10383
10384 // HPET
10385 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10386
10387 // boot order
10388 data.mapBootOrder.clear();
10389 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10390 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10391
10392 // display
10393 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10394 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10395 data.cMonitors = mHWData->mMonitorCount;
10396 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10397 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10398 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10399 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10400 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10401 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10402 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10403 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10404 {
10405 if (mHWData->maVideoCaptureScreens[i])
10406 ASMBitSet(&data.u64VideoCaptureScreens, i);
10407 else
10408 ASMBitClear(&data.u64VideoCaptureScreens, i);
10409 }
10410 /* store relative video capture file if possible */
10411 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10412 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10413
10414 /* VRDEServer settings (optional) */
10415 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10416 if (FAILED(rc)) throw rc;
10417
10418 /* BIOS (required) */
10419 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10420 if (FAILED(rc)) throw rc;
10421
10422 /* USB Controller (required) */
10423 data.usbSettings.llUSBControllers.clear();
10424 for (USBControllerList::const_iterator
10425 it = mUSBControllers->begin();
10426 it != mUSBControllers->end();
10427 ++it)
10428 {
10429 ComObjPtr<USBController> ctrl = *it;
10430 settings::USBController settingsCtrl;
10431
10432 settingsCtrl.strName = ctrl->i_getName();
10433 settingsCtrl.enmType = ctrl->i_getControllerType();
10434
10435 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10436 }
10437
10438 /* USB device filters (required) */
10439 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10440 if (FAILED(rc)) throw rc;
10441
10442 /* Network adapters (required) */
10443 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10444 data.llNetworkAdapters.clear();
10445 /* Write out only the nominal number of network adapters for this
10446 * chipset type. Since Machine::commit() hasn't been called there
10447 * may be extra NIC settings in the vector. */
10448 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10449 {
10450 settings::NetworkAdapter nic;
10451 nic.ulSlot = (uint32_t)slot;
10452 /* paranoia check... must not be NULL, but must not crash either. */
10453 if (mNetworkAdapters[slot])
10454 {
10455 if (mNetworkAdapters[slot]->i_hasDefaults())
10456 continue;
10457
10458 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10459 if (FAILED(rc)) throw rc;
10460
10461 data.llNetworkAdapters.push_back(nic);
10462 }
10463 }
10464
10465 /* Serial ports */
10466 data.llSerialPorts.clear();
10467 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10468 {
10469 if (mSerialPorts[slot]->i_hasDefaults())
10470 continue;
10471
10472 settings::SerialPort s;
10473 s.ulSlot = slot;
10474 rc = mSerialPorts[slot]->i_saveSettings(s);
10475 if (FAILED(rc)) return rc;
10476
10477 data.llSerialPorts.push_back(s);
10478 }
10479
10480 /* Parallel ports */
10481 data.llParallelPorts.clear();
10482 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10483 {
10484 if (mParallelPorts[slot]->i_hasDefaults())
10485 continue;
10486
10487 settings::ParallelPort p;
10488 p.ulSlot = slot;
10489 rc = mParallelPorts[slot]->i_saveSettings(p);
10490 if (FAILED(rc)) return rc;
10491
10492 data.llParallelPorts.push_back(p);
10493 }
10494
10495 /* Audio adapter */
10496 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10497 if (FAILED(rc)) return rc;
10498
10499 rc = i_saveStorageControllers(data.storage);
10500 if (FAILED(rc)) return rc;
10501
10502 /* Shared folders */
10503 data.llSharedFolders.clear();
10504 for (HWData::SharedFolderList::const_iterator
10505 it = mHWData->mSharedFolders.begin();
10506 it != mHWData->mSharedFolders.end();
10507 ++it)
10508 {
10509 SharedFolder *pSF = *it;
10510 AutoCaller sfCaller(pSF);
10511 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10512 settings::SharedFolder sf;
10513 sf.strName = pSF->i_getName();
10514 sf.strHostPath = pSF->i_getHostPath();
10515 sf.fWritable = !!pSF->i_isWritable();
10516 sf.fAutoMount = !!pSF->i_isAutoMounted();
10517
10518 data.llSharedFolders.push_back(sf);
10519 }
10520
10521 // clipboard
10522 data.clipboardMode = mHWData->mClipboardMode;
10523
10524 // drag'n'drop
10525 data.dndMode = mHWData->mDnDMode;
10526
10527 /* Guest */
10528 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10529
10530 // IO settings
10531 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10532 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10533
10534 /* BandwidthControl (required) */
10535 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10536 if (FAILED(rc)) throw rc;
10537
10538 /* Host PCI devices */
10539 data.pciAttachments.clear();
10540 for (HWData::PCIDeviceAssignmentList::const_iterator
10541 it = mHWData->mPCIDeviceAssignments.begin();
10542 it != mHWData->mPCIDeviceAssignments.end();
10543 ++it)
10544 {
10545 ComObjPtr<PCIDeviceAttachment> pda = *it;
10546 settings::HostPCIDeviceAttachment hpda;
10547
10548 rc = pda->i_saveSettings(hpda);
10549 if (FAILED(rc)) throw rc;
10550
10551 data.pciAttachments.push_back(hpda);
10552 }
10553
10554 // guest properties
10555 data.llGuestProperties.clear();
10556#ifdef VBOX_WITH_GUEST_PROPS
10557 for (HWData::GuestPropertyMap::const_iterator
10558 it = mHWData->mGuestProperties.begin();
10559 it != mHWData->mGuestProperties.end();
10560 ++it)
10561 {
10562 HWData::GuestProperty property = it->second;
10563
10564 /* Remove transient guest properties at shutdown unless we
10565 * are saving state. Note that restoring snapshot intentionally
10566 * keeps them, they will be removed if appropriate once the final
10567 * machine state is set (as crashes etc. need to work). */
10568 if ( ( mData->mMachineState == MachineState_PoweredOff
10569 || mData->mMachineState == MachineState_Aborted
10570 || mData->mMachineState == MachineState_Teleported)
10571 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10572 continue;
10573 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10574 prop.strName = it->first;
10575 prop.strValue = property.strValue;
10576 prop.timestamp = property.mTimestamp;
10577 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10578 GuestPropWriteFlags(property.mFlags, szFlags);
10579 prop.strFlags = szFlags;
10580
10581 data.llGuestProperties.push_back(prop);
10582 }
10583
10584 /* I presume this doesn't require a backup(). */
10585 mData->mGuestPropertiesModified = FALSE;
10586#endif /* VBOX_WITH_GUEST_PROPS defined */
10587
10588 *pDbg = mHWData->mDebugging;
10589 *pAutostart = mHWData->mAutostart;
10590
10591 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10592 }
10593 catch (std::bad_alloc &)
10594 {
10595 return E_OUTOFMEMORY;
10596 }
10597
10598 AssertComRC(rc);
10599 return rc;
10600}
10601
10602/**
10603 * Saves the storage controller configuration.
10604 *
10605 * @param data storage settings.
10606 */
10607HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10608{
10609 data.llStorageControllers.clear();
10610
10611 for (StorageControllerList::const_iterator
10612 it = mStorageControllers->begin();
10613 it != mStorageControllers->end();
10614 ++it)
10615 {
10616 HRESULT rc;
10617 ComObjPtr<StorageController> pCtl = *it;
10618
10619 settings::StorageController ctl;
10620 ctl.strName = pCtl->i_getName();
10621 ctl.controllerType = pCtl->i_getControllerType();
10622 ctl.storageBus = pCtl->i_getStorageBus();
10623 ctl.ulInstance = pCtl->i_getInstance();
10624 ctl.fBootable = pCtl->i_getBootable();
10625
10626 /* Save the port count. */
10627 ULONG portCount;
10628 rc = pCtl->COMGETTER(PortCount)(&portCount);
10629 ComAssertComRCRet(rc, rc);
10630 ctl.ulPortCount = portCount;
10631
10632 /* Save fUseHostIOCache */
10633 BOOL fUseHostIOCache;
10634 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10635 ComAssertComRCRet(rc, rc);
10636 ctl.fUseHostIOCache = !!fUseHostIOCache;
10637
10638 /* save the devices now. */
10639 rc = i_saveStorageDevices(pCtl, ctl);
10640 ComAssertComRCRet(rc, rc);
10641
10642 data.llStorageControllers.push_back(ctl);
10643 }
10644
10645 return S_OK;
10646}
10647
10648/**
10649 * Saves the hard disk configuration.
10650 */
10651HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10652 settings::StorageController &data)
10653{
10654 MediumAttachmentList atts;
10655
10656 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10657 if (FAILED(rc)) return rc;
10658
10659 data.llAttachedDevices.clear();
10660 for (MediumAttachmentList::const_iterator
10661 it = atts.begin();
10662 it != atts.end();
10663 ++it)
10664 {
10665 settings::AttachedDevice dev;
10666 IMediumAttachment *iA = *it;
10667 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10668 Medium *pMedium = pAttach->i_getMedium();
10669
10670 dev.deviceType = pAttach->i_getType();
10671 dev.lPort = pAttach->i_getPort();
10672 dev.lDevice = pAttach->i_getDevice();
10673 dev.fPassThrough = pAttach->i_getPassthrough();
10674 dev.fHotPluggable = pAttach->i_getHotPluggable();
10675 if (pMedium)
10676 {
10677 if (pMedium->i_isHostDrive())
10678 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10679 else
10680 dev.uuid = pMedium->i_getId();
10681 dev.fTempEject = pAttach->i_getTempEject();
10682 dev.fNonRotational = pAttach->i_getNonRotational();
10683 dev.fDiscard = pAttach->i_getDiscard();
10684 }
10685
10686 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10687
10688 data.llAttachedDevices.push_back(dev);
10689 }
10690
10691 return S_OK;
10692}
10693
10694/**
10695 * Saves machine state settings as defined by aFlags
10696 * (SaveSTS_* values).
10697 *
10698 * @param aFlags Combination of SaveSTS_* flags.
10699 *
10700 * @note Locks objects for writing.
10701 */
10702HRESULT Machine::i_saveStateSettings(int aFlags)
10703{
10704 if (aFlags == 0)
10705 return S_OK;
10706
10707 AutoCaller autoCaller(this);
10708 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10709
10710 /* This object's write lock is also necessary to serialize file access
10711 * (prevent concurrent reads and writes) */
10712 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10713
10714 HRESULT rc = S_OK;
10715
10716 Assert(mData->pMachineConfigFile);
10717
10718 try
10719 {
10720 if (aFlags & SaveSTS_CurStateModified)
10721 mData->pMachineConfigFile->fCurrentStateModified = true;
10722
10723 if (aFlags & SaveSTS_StateFilePath)
10724 {
10725 if (!mSSData->strStateFilePath.isEmpty())
10726 /* try to make the file name relative to the settings file dir */
10727 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10728 else
10729 mData->pMachineConfigFile->strStateFile.setNull();
10730 }
10731
10732 if (aFlags & SaveSTS_StateTimeStamp)
10733 {
10734 Assert( mData->mMachineState != MachineState_Aborted
10735 || mSSData->strStateFilePath.isEmpty());
10736
10737 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10738
10739 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10740/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10741 }
10742
10743 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10744 }
10745 catch (...)
10746 {
10747 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10748 }
10749
10750 return rc;
10751}
10752
10753/**
10754 * Ensures that the given medium is added to a media registry. If this machine
10755 * was created with 4.0 or later, then the machine registry is used. Otherwise
10756 * the global VirtualBox media registry is used.
10757 *
10758 * Caller must NOT hold machine lock, media tree or any medium locks!
10759 *
10760 * @param pMedium
10761 */
10762void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10763{
10764 /* Paranoia checks: do not hold machine or media tree locks. */
10765 AssertReturnVoid(!isWriteLockOnCurrentThread());
10766 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10767
10768 ComObjPtr<Medium> pBase;
10769 {
10770 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10771 pBase = pMedium->i_getBase();
10772 }
10773
10774 /* Paranoia checks: do not hold medium locks. */
10775 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10776 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10777
10778 // decide which medium registry to use now that the medium is attached:
10779 Guid uuid;
10780 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10781 // machine XML is VirtualBox 4.0 or higher:
10782 uuid = i_getId(); // machine UUID
10783 else
10784 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10785
10786 if (pMedium->i_addRegistry(uuid))
10787 mParent->i_markRegistryModified(uuid);
10788
10789 /* For more complex hard disk structures it can happen that the base
10790 * medium isn't yet associated with any medium registry. Do that now. */
10791 if (pMedium != pBase)
10792 {
10793 /* Tree lock needed by Medium::addRegistry when recursing. */
10794 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10795 if (pBase->i_addRegistryRecursive(uuid))
10796 {
10797 treeLock.release();
10798 mParent->i_markRegistryModified(uuid);
10799 }
10800 }
10801}
10802
10803/**
10804 * Creates differencing hard disks for all normal hard disks attached to this
10805 * machine and a new set of attachments to refer to created disks.
10806 *
10807 * Used when taking a snapshot or when deleting the current state. Gets called
10808 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10809 *
10810 * This method assumes that mMediumAttachments contains the original hard disk
10811 * attachments it needs to create diffs for. On success, these attachments will
10812 * be replaced with the created diffs.
10813 *
10814 * Attachments with non-normal hard disks are left as is.
10815 *
10816 * If @a aOnline is @c false then the original hard disks that require implicit
10817 * diffs will be locked for reading. Otherwise it is assumed that they are
10818 * already locked for writing (when the VM was started). Note that in the latter
10819 * case it is responsibility of the caller to lock the newly created diffs for
10820 * writing if this method succeeds.
10821 *
10822 * @param aProgress Progress object to run (must contain at least as
10823 * many operations left as the number of hard disks
10824 * attached).
10825 * @param aWeight Weight of this operation.
10826 * @param aOnline Whether the VM was online prior to this operation.
10827 *
10828 * @note The progress object is not marked as completed, neither on success nor
10829 * on failure. This is a responsibility of the caller.
10830 *
10831 * @note Locks this object and the media tree for writing.
10832 */
10833HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10834 ULONG aWeight,
10835 bool aOnline)
10836{
10837 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10838
10839 AutoCaller autoCaller(this);
10840 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10841
10842 AutoMultiWriteLock2 alock(this->lockHandle(),
10843 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10844
10845 /* must be in a protective state because we release the lock below */
10846 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10847 || mData->mMachineState == MachineState_OnlineSnapshotting
10848 || mData->mMachineState == MachineState_LiveSnapshotting
10849 || mData->mMachineState == MachineState_RestoringSnapshot
10850 || mData->mMachineState == MachineState_DeletingSnapshot
10851 , E_FAIL);
10852
10853 HRESULT rc = S_OK;
10854
10855 // use appropriate locked media map (online or offline)
10856 MediumLockListMap lockedMediaOffline;
10857 MediumLockListMap *lockedMediaMap;
10858 if (aOnline)
10859 lockedMediaMap = &mData->mSession.mLockedMedia;
10860 else
10861 lockedMediaMap = &lockedMediaOffline;
10862
10863 try
10864 {
10865 if (!aOnline)
10866 {
10867 /* lock all attached hard disks early to detect "in use"
10868 * situations before creating actual diffs */
10869 for (MediumAttachmentList::const_iterator
10870 it = mMediumAttachments->begin();
10871 it != mMediumAttachments->end();
10872 ++it)
10873 {
10874 MediumAttachment *pAtt = *it;
10875 if (pAtt->i_getType() == DeviceType_HardDisk)
10876 {
10877 Medium *pMedium = pAtt->i_getMedium();
10878 Assert(pMedium);
10879
10880 MediumLockList *pMediumLockList(new MediumLockList());
10881 alock.release();
10882 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10883 NULL /* pToLockWrite */,
10884 false /* fMediumLockWriteAll */,
10885 NULL,
10886 *pMediumLockList);
10887 alock.acquire();
10888 if (FAILED(rc))
10889 {
10890 delete pMediumLockList;
10891 throw rc;
10892 }
10893 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10894 if (FAILED(rc))
10895 {
10896 throw setError(rc,
10897 tr("Collecting locking information for all attached media failed"));
10898 }
10899 }
10900 }
10901
10902 /* Now lock all media. If this fails, nothing is locked. */
10903 alock.release();
10904 rc = lockedMediaMap->Lock();
10905 alock.acquire();
10906 if (FAILED(rc))
10907 {
10908 throw setError(rc,
10909 tr("Locking of attached media failed"));
10910 }
10911 }
10912
10913 /* remember the current list (note that we don't use backup() since
10914 * mMediumAttachments may be already backed up) */
10915 MediumAttachmentList atts = *mMediumAttachments.data();
10916
10917 /* start from scratch */
10918 mMediumAttachments->clear();
10919
10920 /* go through remembered attachments and create diffs for normal hard
10921 * disks and attach them */
10922 for (MediumAttachmentList::const_iterator
10923 it = atts.begin();
10924 it != atts.end();
10925 ++it)
10926 {
10927 MediumAttachment *pAtt = *it;
10928
10929 DeviceType_T devType = pAtt->i_getType();
10930 Medium *pMedium = pAtt->i_getMedium();
10931
10932 if ( devType != DeviceType_HardDisk
10933 || pMedium == NULL
10934 || pMedium->i_getType() != MediumType_Normal)
10935 {
10936 /* copy the attachment as is */
10937
10938 /** @todo the progress object created in SessionMachine::TakeSnaphot
10939 * only expects operations for hard disks. Later other
10940 * device types need to show up in the progress as well. */
10941 if (devType == DeviceType_HardDisk)
10942 {
10943 if (pMedium == NULL)
10944 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10945 aWeight); // weight
10946 else
10947 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10948 pMedium->i_getBase()->i_getName().c_str()).raw(),
10949 aWeight); // weight
10950 }
10951
10952 mMediumAttachments->push_back(pAtt);
10953 continue;
10954 }
10955
10956 /* need a diff */
10957 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10958 pMedium->i_getBase()->i_getName().c_str()).raw(),
10959 aWeight); // weight
10960
10961 Utf8Str strFullSnapshotFolder;
10962 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10963
10964 ComObjPtr<Medium> diff;
10965 diff.createObject();
10966 // store the diff in the same registry as the parent
10967 // (this cannot fail here because we can't create implicit diffs for
10968 // unregistered images)
10969 Guid uuidRegistryParent;
10970 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10971 Assert(fInRegistry); NOREF(fInRegistry);
10972 rc = diff->init(mParent,
10973 pMedium->i_getPreferredDiffFormat(),
10974 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10975 uuidRegistryParent,
10976 DeviceType_HardDisk);
10977 if (FAILED(rc)) throw rc;
10978
10979 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10980 * the push_back? Looks like we're going to release medium with the
10981 * wrong kind of lock (general issue with if we fail anywhere at all)
10982 * and an orphaned VDI in the snapshots folder. */
10983
10984 /* update the appropriate lock list */
10985 MediumLockList *pMediumLockList;
10986 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10987 AssertComRCThrowRC(rc);
10988 if (aOnline)
10989 {
10990 alock.release();
10991 /* The currently attached medium will be read-only, change
10992 * the lock type to read. */
10993 rc = pMediumLockList->Update(pMedium, false);
10994 alock.acquire();
10995 AssertComRCThrowRC(rc);
10996 }
10997
10998 /* release the locks before the potentially lengthy operation */
10999 alock.release();
11000 rc = pMedium->i_createDiffStorage(diff,
11001 pMedium->i_getPreferredDiffVariant(),
11002 pMediumLockList,
11003 NULL /* aProgress */,
11004 true /* aWait */);
11005 alock.acquire();
11006 if (FAILED(rc)) throw rc;
11007
11008 /* actual lock list update is done in Machine::i_commitMedia */
11009
11010 rc = diff->i_addBackReference(mData->mUuid);
11011 AssertComRCThrowRC(rc);
11012
11013 /* add a new attachment */
11014 ComObjPtr<MediumAttachment> attachment;
11015 attachment.createObject();
11016 rc = attachment->init(this,
11017 diff,
11018 pAtt->i_getControllerName(),
11019 pAtt->i_getPort(),
11020 pAtt->i_getDevice(),
11021 DeviceType_HardDisk,
11022 true /* aImplicit */,
11023 false /* aPassthrough */,
11024 false /* aTempEject */,
11025 pAtt->i_getNonRotational(),
11026 pAtt->i_getDiscard(),
11027 pAtt->i_getHotPluggable(),
11028 pAtt->i_getBandwidthGroup());
11029 if (FAILED(rc)) throw rc;
11030
11031 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11032 AssertComRCThrowRC(rc);
11033 mMediumAttachments->push_back(attachment);
11034 }
11035 }
11036 catch (HRESULT aRC) { rc = aRC; }
11037
11038 /* unlock all hard disks we locked when there is no VM */
11039 if (!aOnline)
11040 {
11041 ErrorInfoKeeper eik;
11042
11043 HRESULT rc1 = lockedMediaMap->Clear();
11044 AssertComRC(rc1);
11045 }
11046
11047 return rc;
11048}
11049
11050/**
11051 * Deletes implicit differencing hard disks created either by
11052 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11053 * mMediumAttachments.
11054 *
11055 * Note that to delete hard disks created by #attachDevice() this method is
11056 * called from #i_rollbackMedia() when the changes are rolled back.
11057 *
11058 * @note Locks this object and the media tree for writing.
11059 */
11060HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11061{
11062 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11063
11064 AutoCaller autoCaller(this);
11065 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11066
11067 AutoMultiWriteLock2 alock(this->lockHandle(),
11068 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11069
11070 /* We absolutely must have backed up state. */
11071 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11072
11073 /* Check if there are any implicitly created diff images. */
11074 bool fImplicitDiffs = false;
11075 for (MediumAttachmentList::const_iterator
11076 it = mMediumAttachments->begin();
11077 it != mMediumAttachments->end();
11078 ++it)
11079 {
11080 const ComObjPtr<MediumAttachment> &pAtt = *it;
11081 if (pAtt->i_isImplicit())
11082 {
11083 fImplicitDiffs = true;
11084 break;
11085 }
11086 }
11087 /* If there is nothing to do, leave early. This saves lots of image locking
11088 * effort. It also avoids a MachineStateChanged event without real reason.
11089 * This is important e.g. when loading a VM config, because there should be
11090 * no events. Otherwise API clients can become thoroughly confused for
11091 * inaccessible VMs (the code for loading VM configs uses this method for
11092 * cleanup if the config makes no sense), as they take such events as an
11093 * indication that the VM is alive, and they would force the VM config to
11094 * be reread, leading to an endless loop. */
11095 if (!fImplicitDiffs)
11096 return S_OK;
11097
11098 HRESULT rc = S_OK;
11099 MachineState_T oldState = mData->mMachineState;
11100
11101 /* will release the lock before the potentially lengthy operation,
11102 * so protect with the special state (unless already protected) */
11103 if ( oldState != MachineState_Snapshotting
11104 && oldState != MachineState_OnlineSnapshotting
11105 && oldState != MachineState_LiveSnapshotting
11106 && oldState != MachineState_RestoringSnapshot
11107 && oldState != MachineState_DeletingSnapshot
11108 && oldState != MachineState_DeletingSnapshotOnline
11109 && oldState != MachineState_DeletingSnapshotPaused
11110 )
11111 i_setMachineState(MachineState_SettingUp);
11112
11113 // use appropriate locked media map (online or offline)
11114 MediumLockListMap lockedMediaOffline;
11115 MediumLockListMap *lockedMediaMap;
11116 if (aOnline)
11117 lockedMediaMap = &mData->mSession.mLockedMedia;
11118 else
11119 lockedMediaMap = &lockedMediaOffline;
11120
11121 try
11122 {
11123 if (!aOnline)
11124 {
11125 /* lock all attached hard disks early to detect "in use"
11126 * situations before deleting actual diffs */
11127 for (MediumAttachmentList::const_iterator
11128 it = mMediumAttachments->begin();
11129 it != mMediumAttachments->end();
11130 ++it)
11131 {
11132 MediumAttachment *pAtt = *it;
11133 if (pAtt->i_getType() == DeviceType_HardDisk)
11134 {
11135 Medium *pMedium = pAtt->i_getMedium();
11136 Assert(pMedium);
11137
11138 MediumLockList *pMediumLockList(new MediumLockList());
11139 alock.release();
11140 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11141 NULL /* pToLockWrite */,
11142 false /* fMediumLockWriteAll */,
11143 NULL,
11144 *pMediumLockList);
11145 alock.acquire();
11146
11147 if (FAILED(rc))
11148 {
11149 delete pMediumLockList;
11150 throw rc;
11151 }
11152
11153 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11154 if (FAILED(rc))
11155 throw rc;
11156 }
11157 }
11158
11159 if (FAILED(rc))
11160 throw rc;
11161 } // end of offline
11162
11163 /* Lock lists are now up to date and include implicitly created media */
11164
11165 /* Go through remembered attachments and delete all implicitly created
11166 * diffs and fix up the attachment information */
11167 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11168 MediumAttachmentList implicitAtts;
11169 for (MediumAttachmentList::const_iterator
11170 it = mMediumAttachments->begin();
11171 it != mMediumAttachments->end();
11172 ++it)
11173 {
11174 ComObjPtr<MediumAttachment> pAtt = *it;
11175 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11176 if (pMedium.isNull())
11177 continue;
11178
11179 // Implicit attachments go on the list for deletion and back references are removed.
11180 if (pAtt->i_isImplicit())
11181 {
11182 /* Deassociate and mark for deletion */
11183 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11184 rc = pMedium->i_removeBackReference(mData->mUuid);
11185 if (FAILED(rc))
11186 throw rc;
11187 implicitAtts.push_back(pAtt);
11188 continue;
11189 }
11190
11191 /* Was this medium attached before? */
11192 if (!i_findAttachment(oldAtts, pMedium))
11193 {
11194 /* no: de-associate */
11195 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11196 rc = pMedium->i_removeBackReference(mData->mUuid);
11197 if (FAILED(rc))
11198 throw rc;
11199 continue;
11200 }
11201 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11202 }
11203
11204 /* If there are implicit attachments to delete, throw away the lock
11205 * map contents (which will unlock all media) since the medium
11206 * attachments will be rolled back. Below we need to completely
11207 * recreate the lock map anyway since it is infinitely complex to
11208 * do this incrementally (would need reconstructing each attachment
11209 * change, which would be extremely hairy). */
11210 if (implicitAtts.size() != 0)
11211 {
11212 ErrorInfoKeeper eik;
11213
11214 HRESULT rc1 = lockedMediaMap->Clear();
11215 AssertComRC(rc1);
11216 }
11217
11218 /* rollback hard disk changes */
11219 mMediumAttachments.rollback();
11220
11221 MultiResult mrc(S_OK);
11222
11223 // Delete unused implicit diffs.
11224 if (implicitAtts.size() != 0)
11225 {
11226 alock.release();
11227
11228 for (MediumAttachmentList::const_iterator
11229 it = implicitAtts.begin();
11230 it != implicitAtts.end();
11231 ++it)
11232 {
11233 // Remove medium associated with this attachment.
11234 ComObjPtr<MediumAttachment> pAtt = *it;
11235 Assert(pAtt);
11236 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11237 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11238 Assert(pMedium);
11239
11240 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11241 // continue on delete failure, just collect error messages
11242 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11243 pMedium->i_getLocationFull().c_str() ));
11244 mrc = rc;
11245 }
11246 // Clear the list of deleted implicit attachments now, while not
11247 // holding the lock, as it will ultimately trigger Medium::uninit()
11248 // calls which assume that the media tree lock isn't held.
11249 implicitAtts.clear();
11250
11251 alock.acquire();
11252
11253 /* if there is a VM recreate media lock map as mentioned above,
11254 * otherwise it is a waste of time and we leave things unlocked */
11255 if (aOnline)
11256 {
11257 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11258 /* must never be NULL, but better safe than sorry */
11259 if (!pMachine.isNull())
11260 {
11261 alock.release();
11262 rc = mData->mSession.mMachine->i_lockMedia();
11263 alock.acquire();
11264 if (FAILED(rc))
11265 throw rc;
11266 }
11267 }
11268 }
11269 }
11270 catch (HRESULT aRC) {rc = aRC;}
11271
11272 if (mData->mMachineState == MachineState_SettingUp)
11273 i_setMachineState(oldState);
11274
11275 /* unlock all hard disks we locked when there is no VM */
11276 if (!aOnline)
11277 {
11278 ErrorInfoKeeper eik;
11279
11280 HRESULT rc1 = lockedMediaMap->Clear();
11281 AssertComRC(rc1);
11282 }
11283
11284 return rc;
11285}
11286
11287
11288/**
11289 * Looks through the given list of media attachments for one with the given parameters
11290 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11291 * can be searched as well if needed.
11292 *
11293 * @param ll
11294 * @param aControllerName
11295 * @param aControllerPort
11296 * @param aDevice
11297 * @return
11298 */
11299MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11300 const Utf8Str &aControllerName,
11301 LONG aControllerPort,
11302 LONG aDevice)
11303{
11304 for (MediumAttachmentList::const_iterator
11305 it = ll.begin();
11306 it != ll.end();
11307 ++it)
11308 {
11309 MediumAttachment *pAttach = *it;
11310 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11311 return pAttach;
11312 }
11313
11314 return NULL;
11315}
11316
11317/**
11318 * Looks through the given list of media attachments for one with the given parameters
11319 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11320 * can be searched as well if needed.
11321 *
11322 * @param ll
11323 * @param pMedium
11324 * @return
11325 */
11326MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11327 ComObjPtr<Medium> pMedium)
11328{
11329 for (MediumAttachmentList::const_iterator
11330 it = ll.begin();
11331 it != ll.end();
11332 ++it)
11333 {
11334 MediumAttachment *pAttach = *it;
11335 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11336 if (pMediumThis == pMedium)
11337 return pAttach;
11338 }
11339
11340 return NULL;
11341}
11342
11343/**
11344 * Looks through the given list of media attachments for one with the given parameters
11345 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11346 * can be searched as well if needed.
11347 *
11348 * @param ll
11349 * @param id
11350 * @return
11351 */
11352MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11353 Guid &id)
11354{
11355 for (MediumAttachmentList::const_iterator
11356 it = ll.begin();
11357 it != ll.end();
11358 ++it)
11359 {
11360 MediumAttachment *pAttach = *it;
11361 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11362 if (pMediumThis->i_getId() == id)
11363 return pAttach;
11364 }
11365
11366 return NULL;
11367}
11368
11369/**
11370 * Main implementation for Machine::DetachDevice. This also gets called
11371 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11372 *
11373 * @param pAttach Medium attachment to detach.
11374 * @param writeLock Machine write lock which the caller must have locked once.
11375 * This may be released temporarily in here.
11376 * @param pSnapshot If NULL, then the detachment is for the current machine.
11377 * Otherwise this is for a SnapshotMachine, and this must be
11378 * its snapshot.
11379 * @return
11380 */
11381HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11382 AutoWriteLock &writeLock,
11383 Snapshot *pSnapshot)
11384{
11385 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11386 DeviceType_T mediumType = pAttach->i_getType();
11387
11388 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11389
11390 if (pAttach->i_isImplicit())
11391 {
11392 /* attempt to implicitly delete the implicitly created diff */
11393
11394 /// @todo move the implicit flag from MediumAttachment to Medium
11395 /// and forbid any hard disk operation when it is implicit. Or maybe
11396 /// a special media state for it to make it even more simple.
11397
11398 Assert(mMediumAttachments.isBackedUp());
11399
11400 /* will release the lock before the potentially lengthy operation, so
11401 * protect with the special state */
11402 MachineState_T oldState = mData->mMachineState;
11403 i_setMachineState(MachineState_SettingUp);
11404
11405 writeLock.release();
11406
11407 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11408 true /*aWait*/);
11409
11410 writeLock.acquire();
11411
11412 i_setMachineState(oldState);
11413
11414 if (FAILED(rc)) return rc;
11415 }
11416
11417 i_setModified(IsModified_Storage);
11418 mMediumAttachments.backup();
11419 mMediumAttachments->remove(pAttach);
11420
11421 if (!oldmedium.isNull())
11422 {
11423 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11424 if (pSnapshot)
11425 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11426 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11427 else if (mediumType != DeviceType_HardDisk)
11428 oldmedium->i_removeBackReference(mData->mUuid);
11429 }
11430
11431 return S_OK;
11432}
11433
11434/**
11435 * Goes thru all media of the given list and
11436 *
11437 * 1) calls i_detachDevice() on each of them for this machine and
11438 * 2) adds all Medium objects found in the process to the given list,
11439 * depending on cleanupMode.
11440 *
11441 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11442 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11443 * media to the list.
11444 *
11445 * This gets called from Machine::Unregister, both for the actual Machine and
11446 * the SnapshotMachine objects that might be found in the snapshots.
11447 *
11448 * Requires caller and locking. The machine lock must be passed in because it
11449 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11450 *
11451 * @param writeLock Machine lock from top-level caller; this gets passed to
11452 * i_detachDevice.
11453 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11454 * object if called for a SnapshotMachine.
11455 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11456 * added to llMedia; if Full, then all media get added;
11457 * otherwise no media get added.
11458 * @param llMedia Caller's list to receive Medium objects which got detached so
11459 * caller can close() them, depending on cleanupMode.
11460 * @return
11461 */
11462HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11463 Snapshot *pSnapshot,
11464 CleanupMode_T cleanupMode,
11465 MediaList &llMedia)
11466{
11467 Assert(isWriteLockOnCurrentThread());
11468
11469 HRESULT rc;
11470
11471 // make a temporary list because i_detachDevice invalidates iterators into
11472 // mMediumAttachments
11473 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11474
11475 for (MediumAttachmentList::iterator
11476 it = llAttachments2.begin();
11477 it != llAttachments2.end();
11478 ++it)
11479 {
11480 ComObjPtr<MediumAttachment> &pAttach = *it;
11481 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11482
11483 if (!pMedium.isNull())
11484 {
11485 AutoCaller mac(pMedium);
11486 if (FAILED(mac.rc())) return mac.rc();
11487 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11488 DeviceType_T devType = pMedium->i_getDeviceType();
11489 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11490 && devType == DeviceType_HardDisk)
11491 || (cleanupMode == CleanupMode_Full)
11492 )
11493 {
11494 llMedia.push_back(pMedium);
11495 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11496 /* Not allowed to keep this lock as below we need the parent
11497 * medium lock, and the lock order is parent to child. */
11498 lock.release();
11499 /*
11500 * Search for medias which are not attached to any machine, but
11501 * in the chain to an attached disk. Mediums are only consided
11502 * if they are:
11503 * - have only one child
11504 * - no references to any machines
11505 * - are of normal medium type
11506 */
11507 while (!pParent.isNull())
11508 {
11509 AutoCaller mac1(pParent);
11510 if (FAILED(mac1.rc())) return mac1.rc();
11511 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11512 if (pParent->i_getChildren().size() == 1)
11513 {
11514 if ( pParent->i_getMachineBackRefCount() == 0
11515 && pParent->i_getType() == MediumType_Normal
11516 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11517 llMedia.push_back(pParent);
11518 }
11519 else
11520 break;
11521 pParent = pParent->i_getParent();
11522 }
11523 }
11524 }
11525
11526 // real machine: then we need to use the proper method
11527 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11528
11529 if (FAILED(rc))
11530 return rc;
11531 }
11532
11533 return S_OK;
11534}
11535
11536/**
11537 * Perform deferred hard disk detachments.
11538 *
11539 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11540 * changed (not backed up).
11541 *
11542 * If @a aOnline is @c true then this method will also unlock the old hard
11543 * disks for which the new implicit diffs were created and will lock these new
11544 * diffs for writing.
11545 *
11546 * @param aOnline Whether the VM was online prior to this operation.
11547 *
11548 * @note Locks this object for writing!
11549 */
11550void Machine::i_commitMedia(bool aOnline /*= false*/)
11551{
11552 AutoCaller autoCaller(this);
11553 AssertComRCReturnVoid(autoCaller.rc());
11554
11555 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11556
11557 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11558
11559 HRESULT rc = S_OK;
11560
11561 /* no attach/detach operations -- nothing to do */
11562 if (!mMediumAttachments.isBackedUp())
11563 return;
11564
11565 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11566 bool fMediaNeedsLocking = false;
11567
11568 /* enumerate new attachments */
11569 for (MediumAttachmentList::const_iterator
11570 it = mMediumAttachments->begin();
11571 it != mMediumAttachments->end();
11572 ++it)
11573 {
11574 MediumAttachment *pAttach = *it;
11575
11576 pAttach->i_commit();
11577
11578 Medium *pMedium = pAttach->i_getMedium();
11579 bool fImplicit = pAttach->i_isImplicit();
11580
11581 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11582 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11583 fImplicit));
11584
11585 /** @todo convert all this Machine-based voodoo to MediumAttachment
11586 * based commit logic. */
11587 if (fImplicit)
11588 {
11589 /* convert implicit attachment to normal */
11590 pAttach->i_setImplicit(false);
11591
11592 if ( aOnline
11593 && pMedium
11594 && pAttach->i_getType() == DeviceType_HardDisk
11595 )
11596 {
11597 /* update the appropriate lock list */
11598 MediumLockList *pMediumLockList;
11599 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11600 AssertComRC(rc);
11601 if (pMediumLockList)
11602 {
11603 /* unlock if there's a need to change the locking */
11604 if (!fMediaNeedsLocking)
11605 {
11606 rc = mData->mSession.mLockedMedia.Unlock();
11607 AssertComRC(rc);
11608 fMediaNeedsLocking = true;
11609 }
11610 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11611 AssertComRC(rc);
11612 rc = pMediumLockList->Append(pMedium, true);
11613 AssertComRC(rc);
11614 }
11615 }
11616
11617 continue;
11618 }
11619
11620 if (pMedium)
11621 {
11622 /* was this medium attached before? */
11623 for (MediumAttachmentList::iterator
11624 oldIt = oldAtts.begin();
11625 oldIt != oldAtts.end();
11626 ++oldIt)
11627 {
11628 MediumAttachment *pOldAttach = *oldIt;
11629 if (pOldAttach->i_getMedium() == pMedium)
11630 {
11631 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11632
11633 /* yes: remove from old to avoid de-association */
11634 oldAtts.erase(oldIt);
11635 break;
11636 }
11637 }
11638 }
11639 }
11640
11641 /* enumerate remaining old attachments and de-associate from the
11642 * current machine state */
11643 for (MediumAttachmentList::const_iterator
11644 it = oldAtts.begin();
11645 it != oldAtts.end();
11646 ++it)
11647 {
11648 MediumAttachment *pAttach = *it;
11649 Medium *pMedium = pAttach->i_getMedium();
11650
11651 /* Detach only hard disks, since DVD/floppy media is detached
11652 * instantly in MountMedium. */
11653 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11654 {
11655 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11656
11657 /* now de-associate from the current machine state */
11658 rc = pMedium->i_removeBackReference(mData->mUuid);
11659 AssertComRC(rc);
11660
11661 if (aOnline)
11662 {
11663 /* unlock since medium is not used anymore */
11664 MediumLockList *pMediumLockList;
11665 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11666 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11667 {
11668 /* this happens for online snapshots, there the attachment
11669 * is changing, but only to a diff image created under
11670 * the old one, so there is no separate lock list */
11671 Assert(!pMediumLockList);
11672 }
11673 else
11674 {
11675 AssertComRC(rc);
11676 if (pMediumLockList)
11677 {
11678 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11679 AssertComRC(rc);
11680 }
11681 }
11682 }
11683 }
11684 }
11685
11686 /* take media locks again so that the locking state is consistent */
11687 if (fMediaNeedsLocking)
11688 {
11689 Assert(aOnline);
11690 rc = mData->mSession.mLockedMedia.Lock();
11691 AssertComRC(rc);
11692 }
11693
11694 /* commit the hard disk changes */
11695 mMediumAttachments.commit();
11696
11697 if (i_isSessionMachine())
11698 {
11699 /*
11700 * Update the parent machine to point to the new owner.
11701 * This is necessary because the stored parent will point to the
11702 * session machine otherwise and cause crashes or errors later
11703 * when the session machine gets invalid.
11704 */
11705 /** @todo Change the MediumAttachment class to behave like any other
11706 * class in this regard by creating peer MediumAttachment
11707 * objects for session machines and share the data with the peer
11708 * machine.
11709 */
11710 for (MediumAttachmentList::const_iterator
11711 it = mMediumAttachments->begin();
11712 it != mMediumAttachments->end();
11713 ++it)
11714 (*it)->i_updateParentMachine(mPeer);
11715
11716 /* attach new data to the primary machine and reshare it */
11717 mPeer->mMediumAttachments.attach(mMediumAttachments);
11718 }
11719
11720 return;
11721}
11722
11723/**
11724 * Perform deferred deletion of implicitly created diffs.
11725 *
11726 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11727 * changed (not backed up).
11728 *
11729 * @note Locks this object for writing!
11730 */
11731void Machine::i_rollbackMedia()
11732{
11733 AutoCaller autoCaller(this);
11734 AssertComRCReturnVoid(autoCaller.rc());
11735
11736 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11737 LogFlowThisFunc(("Entering rollbackMedia\n"));
11738
11739 HRESULT rc = S_OK;
11740
11741 /* no attach/detach operations -- nothing to do */
11742 if (!mMediumAttachments.isBackedUp())
11743 return;
11744
11745 /* enumerate new attachments */
11746 for (MediumAttachmentList::const_iterator
11747 it = mMediumAttachments->begin();
11748 it != mMediumAttachments->end();
11749 ++it)
11750 {
11751 MediumAttachment *pAttach = *it;
11752 /* Fix up the backrefs for DVD/floppy media. */
11753 if (pAttach->i_getType() != DeviceType_HardDisk)
11754 {
11755 Medium *pMedium = pAttach->i_getMedium();
11756 if (pMedium)
11757 {
11758 rc = pMedium->i_removeBackReference(mData->mUuid);
11759 AssertComRC(rc);
11760 }
11761 }
11762
11763 (*it)->i_rollback();
11764
11765 pAttach = *it;
11766 /* Fix up the backrefs for DVD/floppy media. */
11767 if (pAttach->i_getType() != DeviceType_HardDisk)
11768 {
11769 Medium *pMedium = pAttach->i_getMedium();
11770 if (pMedium)
11771 {
11772 rc = pMedium->i_addBackReference(mData->mUuid);
11773 AssertComRC(rc);
11774 }
11775 }
11776 }
11777
11778 /** @todo convert all this Machine-based voodoo to MediumAttachment
11779 * based rollback logic. */
11780 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11781
11782 return;
11783}
11784
11785/**
11786 * Returns true if the settings file is located in the directory named exactly
11787 * as the machine; this means, among other things, that the machine directory
11788 * should be auto-renamed.
11789 *
11790 * @param aSettingsDir if not NULL, the full machine settings file directory
11791 * name will be assigned there.
11792 *
11793 * @note Doesn't lock anything.
11794 * @note Not thread safe (must be called from this object's lock).
11795 */
11796bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11797{
11798 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11799 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11800 if (aSettingsDir)
11801 *aSettingsDir = strMachineDirName;
11802 strMachineDirName.stripPath(); // vmname
11803 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11804 strConfigFileOnly.stripPath() // vmname.vbox
11805 .stripSuffix(); // vmname
11806 /** @todo hack, make somehow use of ComposeMachineFilename */
11807 if (mUserData->s.fDirectoryIncludesUUID)
11808 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11809
11810 AssertReturn(!strMachineDirName.isEmpty(), false);
11811 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11812
11813 return strMachineDirName == strConfigFileOnly;
11814}
11815
11816/**
11817 * Discards all changes to machine settings.
11818 *
11819 * @param aNotify Whether to notify the direct session about changes or not.
11820 *
11821 * @note Locks objects for writing!
11822 */
11823void Machine::i_rollback(bool aNotify)
11824{
11825 AutoCaller autoCaller(this);
11826 AssertComRCReturn(autoCaller.rc(), (void)0);
11827
11828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11829
11830 if (!mStorageControllers.isNull())
11831 {
11832 if (mStorageControllers.isBackedUp())
11833 {
11834 /* unitialize all new devices (absent in the backed up list). */
11835 StorageControllerList *backedList = mStorageControllers.backedUpData();
11836 for (StorageControllerList::const_iterator
11837 it = mStorageControllers->begin();
11838 it != mStorageControllers->end();
11839 ++it)
11840 {
11841 if ( std::find(backedList->begin(), backedList->end(), *it)
11842 == backedList->end()
11843 )
11844 {
11845 (*it)->uninit();
11846 }
11847 }
11848
11849 /* restore the list */
11850 mStorageControllers.rollback();
11851 }
11852
11853 /* rollback any changes to devices after restoring the list */
11854 if (mData->flModifications & IsModified_Storage)
11855 {
11856 for (StorageControllerList::const_iterator
11857 it = mStorageControllers->begin();
11858 it != mStorageControllers->end();
11859 ++it)
11860 {
11861 (*it)->i_rollback();
11862 }
11863 }
11864 }
11865
11866 if (!mUSBControllers.isNull())
11867 {
11868 if (mUSBControllers.isBackedUp())
11869 {
11870 /* unitialize all new devices (absent in the backed up list). */
11871 USBControllerList *backedList = mUSBControllers.backedUpData();
11872 for (USBControllerList::const_iterator
11873 it = mUSBControllers->begin();
11874 it != mUSBControllers->end();
11875 ++it)
11876 {
11877 if ( std::find(backedList->begin(), backedList->end(), *it)
11878 == backedList->end()
11879 )
11880 {
11881 (*it)->uninit();
11882 }
11883 }
11884
11885 /* restore the list */
11886 mUSBControllers.rollback();
11887 }
11888
11889 /* rollback any changes to devices after restoring the list */
11890 if (mData->flModifications & IsModified_USB)
11891 {
11892 for (USBControllerList::const_iterator
11893 it = mUSBControllers->begin();
11894 it != mUSBControllers->end();
11895 ++it)
11896 {
11897 (*it)->i_rollback();
11898 }
11899 }
11900 }
11901
11902 mUserData.rollback();
11903
11904 mHWData.rollback();
11905
11906 if (mData->flModifications & IsModified_Storage)
11907 i_rollbackMedia();
11908
11909 if (mBIOSSettings)
11910 mBIOSSettings->i_rollback();
11911
11912 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11913 mVRDEServer->i_rollback();
11914
11915 if (mAudioAdapter)
11916 mAudioAdapter->i_rollback();
11917
11918 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11919 mUSBDeviceFilters->i_rollback();
11920
11921 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11922 mBandwidthControl->i_rollback();
11923
11924 if (!mHWData.isNull())
11925 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11926 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11927 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11928 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11929
11930 if (mData->flModifications & IsModified_NetworkAdapters)
11931 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11932 if ( mNetworkAdapters[slot]
11933 && mNetworkAdapters[slot]->i_isModified())
11934 {
11935 mNetworkAdapters[slot]->i_rollback();
11936 networkAdapters[slot] = mNetworkAdapters[slot];
11937 }
11938
11939 if (mData->flModifications & IsModified_SerialPorts)
11940 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11941 if ( mSerialPorts[slot]
11942 && mSerialPorts[slot]->i_isModified())
11943 {
11944 mSerialPorts[slot]->i_rollback();
11945 serialPorts[slot] = mSerialPorts[slot];
11946 }
11947
11948 if (mData->flModifications & IsModified_ParallelPorts)
11949 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11950 if ( mParallelPorts[slot]
11951 && mParallelPorts[slot]->i_isModified())
11952 {
11953 mParallelPorts[slot]->i_rollback();
11954 parallelPorts[slot] = mParallelPorts[slot];
11955 }
11956
11957 if (aNotify)
11958 {
11959 /* inform the direct session about changes */
11960
11961 ComObjPtr<Machine> that = this;
11962 uint32_t flModifications = mData->flModifications;
11963 alock.release();
11964
11965 if (flModifications & IsModified_SharedFolders)
11966 that->i_onSharedFolderChange();
11967
11968 if (flModifications & IsModified_VRDEServer)
11969 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11970 if (flModifications & IsModified_USB)
11971 that->i_onUSBControllerChange();
11972
11973 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11974 if (networkAdapters[slot])
11975 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11976 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11977 if (serialPorts[slot])
11978 that->i_onSerialPortChange(serialPorts[slot]);
11979 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11980 if (parallelPorts[slot])
11981 that->i_onParallelPortChange(parallelPorts[slot]);
11982
11983 if (flModifications & IsModified_Storage)
11984 that->i_onStorageControllerChange();
11985
11986#if 0
11987 if (flModifications & IsModified_BandwidthControl)
11988 that->onBandwidthControlChange();
11989#endif
11990 }
11991}
11992
11993/**
11994 * Commits all the changes to machine settings.
11995 *
11996 * Note that this operation is supposed to never fail.
11997 *
11998 * @note Locks this object and children for writing.
11999 */
12000void Machine::i_commit()
12001{
12002 AutoCaller autoCaller(this);
12003 AssertComRCReturnVoid(autoCaller.rc());
12004
12005 AutoCaller peerCaller(mPeer);
12006 AssertComRCReturnVoid(peerCaller.rc());
12007
12008 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12009
12010 /*
12011 * use safe commit to ensure Snapshot machines (that share mUserData)
12012 * will still refer to a valid memory location
12013 */
12014 mUserData.commitCopy();
12015
12016 mHWData.commit();
12017
12018 if (mMediumAttachments.isBackedUp())
12019 i_commitMedia(Global::IsOnline(mData->mMachineState));
12020
12021 mBIOSSettings->i_commit();
12022 mVRDEServer->i_commit();
12023 mAudioAdapter->i_commit();
12024 mUSBDeviceFilters->i_commit();
12025 mBandwidthControl->i_commit();
12026
12027 /* Since mNetworkAdapters is a list which might have been changed (resized)
12028 * without using the Backupable<> template we need to handle the copying
12029 * of the list entries manually, including the creation of peers for the
12030 * new objects. */
12031 bool commitNetworkAdapters = false;
12032 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12033 if (mPeer)
12034 {
12035 /* commit everything, even the ones which will go away */
12036 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12037 mNetworkAdapters[slot]->i_commit();
12038 /* copy over the new entries, creating a peer and uninit the original */
12039 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12040 for (size_t slot = 0; slot < newSize; slot++)
12041 {
12042 /* look if this adapter has a peer device */
12043 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12044 if (!peer)
12045 {
12046 /* no peer means the adapter is a newly created one;
12047 * create a peer owning data this data share it with */
12048 peer.createObject();
12049 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12050 }
12051 mPeer->mNetworkAdapters[slot] = peer;
12052 }
12053 /* uninit any no longer needed network adapters */
12054 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12055 mNetworkAdapters[slot]->uninit();
12056 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12057 {
12058 if (mPeer->mNetworkAdapters[slot])
12059 mPeer->mNetworkAdapters[slot]->uninit();
12060 }
12061 /* Keep the original network adapter count until this point, so that
12062 * discarding a chipset type change will not lose settings. */
12063 mNetworkAdapters.resize(newSize);
12064 mPeer->mNetworkAdapters.resize(newSize);
12065 }
12066 else
12067 {
12068 /* we have no peer (our parent is the newly created machine);
12069 * just commit changes to the network adapters */
12070 commitNetworkAdapters = true;
12071 }
12072 if (commitNetworkAdapters)
12073 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12074 mNetworkAdapters[slot]->i_commit();
12075
12076 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12077 mSerialPorts[slot]->i_commit();
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12079 mParallelPorts[slot]->i_commit();
12080
12081 bool commitStorageControllers = false;
12082
12083 if (mStorageControllers.isBackedUp())
12084 {
12085 mStorageControllers.commit();
12086
12087 if (mPeer)
12088 {
12089 /* Commit all changes to new controllers (this will reshare data with
12090 * peers for those who have peers) */
12091 StorageControllerList *newList = new StorageControllerList();
12092 for (StorageControllerList::const_iterator
12093 it = mStorageControllers->begin();
12094 it != mStorageControllers->end();
12095 ++it)
12096 {
12097 (*it)->i_commit();
12098
12099 /* look if this controller has a peer device */
12100 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12101 if (!peer)
12102 {
12103 /* no peer means the device is a newly created one;
12104 * create a peer owning data this device share it with */
12105 peer.createObject();
12106 peer->init(mPeer, *it, true /* aReshare */);
12107 }
12108 else
12109 {
12110 /* remove peer from the old list */
12111 mPeer->mStorageControllers->remove(peer);
12112 }
12113 /* and add it to the new list */
12114 newList->push_back(peer);
12115 }
12116
12117 /* uninit old peer's controllers that are left */
12118 for (StorageControllerList::const_iterator
12119 it = mPeer->mStorageControllers->begin();
12120 it != mPeer->mStorageControllers->end();
12121 ++it)
12122 {
12123 (*it)->uninit();
12124 }
12125
12126 /* attach new list of controllers to our peer */
12127 mPeer->mStorageControllers.attach(newList);
12128 }
12129 else
12130 {
12131 /* we have no peer (our parent is the newly created machine);
12132 * just commit changes to devices */
12133 commitStorageControllers = true;
12134 }
12135 }
12136 else
12137 {
12138 /* the list of controllers itself is not changed,
12139 * just commit changes to controllers themselves */
12140 commitStorageControllers = true;
12141 }
12142
12143 if (commitStorageControllers)
12144 {
12145 for (StorageControllerList::const_iterator
12146 it = mStorageControllers->begin();
12147 it != mStorageControllers->end();
12148 ++it)
12149 {
12150 (*it)->i_commit();
12151 }
12152 }
12153
12154 bool commitUSBControllers = false;
12155
12156 if (mUSBControllers.isBackedUp())
12157 {
12158 mUSBControllers.commit();
12159
12160 if (mPeer)
12161 {
12162 /* Commit all changes to new controllers (this will reshare data with
12163 * peers for those who have peers) */
12164 USBControllerList *newList = new USBControllerList();
12165 for (USBControllerList::const_iterator
12166 it = mUSBControllers->begin();
12167 it != mUSBControllers->end();
12168 ++it)
12169 {
12170 (*it)->i_commit();
12171
12172 /* look if this controller has a peer device */
12173 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12174 if (!peer)
12175 {
12176 /* no peer means the device is a newly created one;
12177 * create a peer owning data this device share it with */
12178 peer.createObject();
12179 peer->init(mPeer, *it, true /* aReshare */);
12180 }
12181 else
12182 {
12183 /* remove peer from the old list */
12184 mPeer->mUSBControllers->remove(peer);
12185 }
12186 /* and add it to the new list */
12187 newList->push_back(peer);
12188 }
12189
12190 /* uninit old peer's controllers that are left */
12191 for (USBControllerList::const_iterator
12192 it = mPeer->mUSBControllers->begin();
12193 it != mPeer->mUSBControllers->end();
12194 ++it)
12195 {
12196 (*it)->uninit();
12197 }
12198
12199 /* attach new list of controllers to our peer */
12200 mPeer->mUSBControllers.attach(newList);
12201 }
12202 else
12203 {
12204 /* we have no peer (our parent is the newly created machine);
12205 * just commit changes to devices */
12206 commitUSBControllers = true;
12207 }
12208 }
12209 else
12210 {
12211 /* the list of controllers itself is not changed,
12212 * just commit changes to controllers themselves */
12213 commitUSBControllers = true;
12214 }
12215
12216 if (commitUSBControllers)
12217 {
12218 for (USBControllerList::const_iterator
12219 it = mUSBControllers->begin();
12220 it != mUSBControllers->end();
12221 ++it)
12222 {
12223 (*it)->i_commit();
12224 }
12225 }
12226
12227 if (i_isSessionMachine())
12228 {
12229 /* attach new data to the primary machine and reshare it */
12230 mPeer->mUserData.attach(mUserData);
12231 mPeer->mHWData.attach(mHWData);
12232 /* mmMediumAttachments is reshared by fixupMedia */
12233 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12234 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12235 }
12236}
12237
12238/**
12239 * Copies all the hardware data from the given machine.
12240 *
12241 * Currently, only called when the VM is being restored from a snapshot. In
12242 * particular, this implies that the VM is not running during this method's
12243 * call.
12244 *
12245 * @note This method must be called from under this object's lock.
12246 *
12247 * @note This method doesn't call #i_commit(), so all data remains backed up and
12248 * unsaved.
12249 */
12250void Machine::i_copyFrom(Machine *aThat)
12251{
12252 AssertReturnVoid(!i_isSnapshotMachine());
12253 AssertReturnVoid(aThat->i_isSnapshotMachine());
12254
12255 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12256
12257 mHWData.assignCopy(aThat->mHWData);
12258
12259 // create copies of all shared folders (mHWData after attaching a copy
12260 // contains just references to original objects)
12261 for (HWData::SharedFolderList::iterator
12262 it = mHWData->mSharedFolders.begin();
12263 it != mHWData->mSharedFolders.end();
12264 ++it)
12265 {
12266 ComObjPtr<SharedFolder> folder;
12267 folder.createObject();
12268 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12269 AssertComRC(rc);
12270 *it = folder;
12271 }
12272
12273 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12274 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12275 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12276 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12277 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12278
12279 /* create private copies of all controllers */
12280 mStorageControllers.backup();
12281 mStorageControllers->clear();
12282 for (StorageControllerList::const_iterator
12283 it = aThat->mStorageControllers->begin();
12284 it != aThat->mStorageControllers->end();
12285 ++it)
12286 {
12287 ComObjPtr<StorageController> ctrl;
12288 ctrl.createObject();
12289 ctrl->initCopy(this, *it);
12290 mStorageControllers->push_back(ctrl);
12291 }
12292
12293 /* create private copies of all USB controllers */
12294 mUSBControllers.backup();
12295 mUSBControllers->clear();
12296 for (USBControllerList::const_iterator
12297 it = aThat->mUSBControllers->begin();
12298 it != aThat->mUSBControllers->end();
12299 ++it)
12300 {
12301 ComObjPtr<USBController> ctrl;
12302 ctrl.createObject();
12303 ctrl->initCopy(this, *it);
12304 mUSBControllers->push_back(ctrl);
12305 }
12306
12307 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12308 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12309 {
12310 if (mNetworkAdapters[slot].isNotNull())
12311 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12312 else
12313 {
12314 unconst(mNetworkAdapters[slot]).createObject();
12315 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12316 }
12317 }
12318 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12319 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12320 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12321 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12322}
12323
12324/**
12325 * Returns whether the given storage controller is hotplug capable.
12326 *
12327 * @returns true if the controller supports hotplugging
12328 * false otherwise.
12329 * @param enmCtrlType The controller type to check for.
12330 */
12331bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12332{
12333 ComPtr<ISystemProperties> systemProperties;
12334 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12335 if (FAILED(rc))
12336 return false;
12337
12338 BOOL aHotplugCapable = FALSE;
12339 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12340
12341 return RT_BOOL(aHotplugCapable);
12342}
12343
12344#ifdef VBOX_WITH_RESOURCE_USAGE_API
12345
12346void Machine::i_getDiskList(MediaList &list)
12347{
12348 for (MediumAttachmentList::const_iterator
12349 it = mMediumAttachments->begin();
12350 it != mMediumAttachments->end();
12351 ++it)
12352 {
12353 MediumAttachment *pAttach = *it;
12354 /* just in case */
12355 AssertContinue(pAttach);
12356
12357 AutoCaller localAutoCallerA(pAttach);
12358 if (FAILED(localAutoCallerA.rc())) continue;
12359
12360 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12361
12362 if (pAttach->i_getType() == DeviceType_HardDisk)
12363 list.push_back(pAttach->i_getMedium());
12364 }
12365}
12366
12367void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12368{
12369 AssertReturnVoid(isWriteLockOnCurrentThread());
12370 AssertPtrReturnVoid(aCollector);
12371
12372 pm::CollectorHAL *hal = aCollector->getHAL();
12373 /* Create sub metrics */
12374 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12375 "Percentage of processor time spent in user mode by the VM process.");
12376 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12377 "Percentage of processor time spent in kernel mode by the VM process.");
12378 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12379 "Size of resident portion of VM process in memory.");
12380 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12381 "Actual size of all VM disks combined.");
12382 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12383 "Network receive rate.");
12384 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12385 "Network transmit rate.");
12386 /* Create and register base metrics */
12387 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12388 cpuLoadUser, cpuLoadKernel);
12389 aCollector->registerBaseMetric(cpuLoad);
12390 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12391 ramUsageUsed);
12392 aCollector->registerBaseMetric(ramUsage);
12393 MediaList disks;
12394 i_getDiskList(disks);
12395 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12396 diskUsageUsed);
12397 aCollector->registerBaseMetric(diskUsage);
12398
12399 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12400 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12401 new pm::AggregateAvg()));
12402 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12403 new pm::AggregateMin()));
12404 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12405 new pm::AggregateMax()));
12406 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12407 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12408 new pm::AggregateAvg()));
12409 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12410 new pm::AggregateMin()));
12411 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12412 new pm::AggregateMax()));
12413
12414 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12415 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12416 new pm::AggregateAvg()));
12417 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12418 new pm::AggregateMin()));
12419 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12420 new pm::AggregateMax()));
12421
12422 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12423 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12424 new pm::AggregateAvg()));
12425 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12426 new pm::AggregateMin()));
12427 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12428 new pm::AggregateMax()));
12429
12430
12431 /* Guest metrics collector */
12432 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12433 aCollector->registerGuest(mCollectorGuest);
12434 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12435
12436 /* Create sub metrics */
12437 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12438 "Percentage of processor time spent in user mode as seen by the guest.");
12439 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12440 "Percentage of processor time spent in kernel mode as seen by the guest.");
12441 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12442 "Percentage of processor time spent idling as seen by the guest.");
12443
12444 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12445 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12446 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12447 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12448 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12449 pm::SubMetric *guestMemCache = new pm::SubMetric(
12450 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12451
12452 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12453 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12454
12455 /* Create and register base metrics */
12456 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12457 machineNetRx, machineNetTx);
12458 aCollector->registerBaseMetric(machineNetRate);
12459
12460 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12461 guestLoadUser, guestLoadKernel, guestLoadIdle);
12462 aCollector->registerBaseMetric(guestCpuLoad);
12463
12464 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12465 guestMemTotal, guestMemFree,
12466 guestMemBalloon, guestMemShared,
12467 guestMemCache, guestPagedTotal);
12468 aCollector->registerBaseMetric(guestCpuMem);
12469
12470 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12471 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12472 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12473 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12474
12475 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12476 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12477 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12478 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12479
12480 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12481 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12482 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12483 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12484
12485 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12486 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12487 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12488 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12489
12490 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12491 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12492 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12493 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12494
12495 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12496 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12497 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12498 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12499
12500 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12501 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12502 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12503 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12504
12505 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12506 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12507 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12508 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12509
12510 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12511 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12512 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12513 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12514
12515 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12516 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12517 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12518 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12519
12520 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12521 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12522 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12523 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12524}
12525
12526void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12527{
12528 AssertReturnVoid(isWriteLockOnCurrentThread());
12529
12530 if (aCollector)
12531 {
12532 aCollector->unregisterMetricsFor(aMachine);
12533 aCollector->unregisterBaseMetricsFor(aMachine);
12534 }
12535}
12536
12537#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12538
12539
12540////////////////////////////////////////////////////////////////////////////////
12541
12542DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12543
12544HRESULT SessionMachine::FinalConstruct()
12545{
12546 LogFlowThisFunc(("\n"));
12547
12548 mClientToken = NULL;
12549
12550 return BaseFinalConstruct();
12551}
12552
12553void SessionMachine::FinalRelease()
12554{
12555 LogFlowThisFunc(("\n"));
12556
12557 Assert(!mClientToken);
12558 /* paranoia, should not hang around any more */
12559 if (mClientToken)
12560 {
12561 delete mClientToken;
12562 mClientToken = NULL;
12563 }
12564
12565 uninit(Uninit::Unexpected);
12566
12567 BaseFinalRelease();
12568}
12569
12570/**
12571 * @note Must be called only by Machine::LockMachine() from its own write lock.
12572 */
12573HRESULT SessionMachine::init(Machine *aMachine)
12574{
12575 LogFlowThisFuncEnter();
12576 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12577
12578 AssertReturn(aMachine, E_INVALIDARG);
12579
12580 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12581
12582 /* Enclose the state transition NotReady->InInit->Ready */
12583 AutoInitSpan autoInitSpan(this);
12584 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12585
12586 HRESULT rc = S_OK;
12587
12588 RT_ZERO(mAuthLibCtx);
12589
12590 /* create the machine client token */
12591 try
12592 {
12593 mClientToken = new ClientToken(aMachine, this);
12594 if (!mClientToken->isReady())
12595 {
12596 delete mClientToken;
12597 mClientToken = NULL;
12598 rc = E_FAIL;
12599 }
12600 }
12601 catch (std::bad_alloc &)
12602 {
12603 rc = E_OUTOFMEMORY;
12604 }
12605 if (FAILED(rc))
12606 return rc;
12607
12608 /* memorize the peer Machine */
12609 unconst(mPeer) = aMachine;
12610 /* share the parent pointer */
12611 unconst(mParent) = aMachine->mParent;
12612
12613 /* take the pointers to data to share */
12614 mData.share(aMachine->mData);
12615 mSSData.share(aMachine->mSSData);
12616
12617 mUserData.share(aMachine->mUserData);
12618 mHWData.share(aMachine->mHWData);
12619 mMediumAttachments.share(aMachine->mMediumAttachments);
12620
12621 mStorageControllers.allocate();
12622 for (StorageControllerList::const_iterator
12623 it = aMachine->mStorageControllers->begin();
12624 it != aMachine->mStorageControllers->end();
12625 ++it)
12626 {
12627 ComObjPtr<StorageController> ctl;
12628 ctl.createObject();
12629 ctl->init(this, *it);
12630 mStorageControllers->push_back(ctl);
12631 }
12632
12633 mUSBControllers.allocate();
12634 for (USBControllerList::const_iterator
12635 it = aMachine->mUSBControllers->begin();
12636 it != aMachine->mUSBControllers->end();
12637 ++it)
12638 {
12639 ComObjPtr<USBController> ctl;
12640 ctl.createObject();
12641 ctl->init(this, *it);
12642 mUSBControllers->push_back(ctl);
12643 }
12644
12645 unconst(mBIOSSettings).createObject();
12646 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12647 /* create another VRDEServer object that will be mutable */
12648 unconst(mVRDEServer).createObject();
12649 mVRDEServer->init(this, aMachine->mVRDEServer);
12650 /* create another audio adapter object that will be mutable */
12651 unconst(mAudioAdapter).createObject();
12652 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12653 /* create a list of serial ports that will be mutable */
12654 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12655 {
12656 unconst(mSerialPorts[slot]).createObject();
12657 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12658 }
12659 /* create a list of parallel ports that will be mutable */
12660 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12661 {
12662 unconst(mParallelPorts[slot]).createObject();
12663 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12664 }
12665
12666 /* create another USB device filters object that will be mutable */
12667 unconst(mUSBDeviceFilters).createObject();
12668 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12669
12670 /* create a list of network adapters that will be mutable */
12671 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12672 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12673 {
12674 unconst(mNetworkAdapters[slot]).createObject();
12675 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12676 }
12677
12678 /* create another bandwidth control object that will be mutable */
12679 unconst(mBandwidthControl).createObject();
12680 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12681
12682 /* default is to delete saved state on Saved -> PoweredOff transition */
12683 mRemoveSavedState = true;
12684
12685 /* Confirm a successful initialization when it's the case */
12686 autoInitSpan.setSucceeded();
12687
12688 miNATNetworksStarted = 0;
12689
12690 LogFlowThisFuncLeave();
12691 return rc;
12692}
12693
12694/**
12695 * Uninitializes this session object. If the reason is other than
12696 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12697 * or the client watcher code.
12698 *
12699 * @param aReason uninitialization reason
12700 *
12701 * @note Locks mParent + this object for writing.
12702 */
12703void SessionMachine::uninit(Uninit::Reason aReason)
12704{
12705 LogFlowThisFuncEnter();
12706 LogFlowThisFunc(("reason=%d\n", aReason));
12707
12708 /*
12709 * Strongly reference ourselves to prevent this object deletion after
12710 * mData->mSession.mMachine.setNull() below (which can release the last
12711 * reference and call the destructor). Important: this must be done before
12712 * accessing any members (and before AutoUninitSpan that does it as well).
12713 * This self reference will be released as the very last step on return.
12714 */
12715 ComObjPtr<SessionMachine> selfRef;
12716 if (aReason != Uninit::Unexpected)
12717 selfRef = this;
12718
12719 /* Enclose the state transition Ready->InUninit->NotReady */
12720 AutoUninitSpan autoUninitSpan(this);
12721 if (autoUninitSpan.uninitDone())
12722 {
12723 LogFlowThisFunc(("Already uninitialized\n"));
12724 LogFlowThisFuncLeave();
12725 return;
12726 }
12727
12728 if (autoUninitSpan.initFailed())
12729 {
12730 /* We've been called by init() because it's failed. It's not really
12731 * necessary (nor it's safe) to perform the regular uninit sequence
12732 * below, the following is enough.
12733 */
12734 LogFlowThisFunc(("Initialization failed.\n"));
12735 /* destroy the machine client token */
12736 if (mClientToken)
12737 {
12738 delete mClientToken;
12739 mClientToken = NULL;
12740 }
12741 uninitDataAndChildObjects();
12742 mData.free();
12743 unconst(mParent) = NULL;
12744 unconst(mPeer) = NULL;
12745 LogFlowThisFuncLeave();
12746 return;
12747 }
12748
12749 MachineState_T lastState;
12750 {
12751 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12752 lastState = mData->mMachineState;
12753 }
12754 NOREF(lastState);
12755
12756#ifdef VBOX_WITH_USB
12757 // release all captured USB devices, but do this before requesting the locks below
12758 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12759 {
12760 /* Console::captureUSBDevices() is called in the VM process only after
12761 * setting the machine state to Starting or Restoring.
12762 * Console::detachAllUSBDevices() will be called upon successful
12763 * termination. So, we need to release USB devices only if there was
12764 * an abnormal termination of a running VM.
12765 *
12766 * This is identical to SessionMachine::DetachAllUSBDevices except
12767 * for the aAbnormal argument. */
12768 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12769 AssertComRC(rc);
12770 NOREF(rc);
12771
12772 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12773 if (service)
12774 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12775 }
12776#endif /* VBOX_WITH_USB */
12777
12778 // we need to lock this object in uninit() because the lock is shared
12779 // with mPeer (as well as data we modify below). mParent lock is needed
12780 // by several calls to it.
12781 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12782
12783#ifdef VBOX_WITH_RESOURCE_USAGE_API
12784 /*
12785 * It is safe to call Machine::i_unregisterMetrics() here because
12786 * PerformanceCollector::samplerCallback no longer accesses guest methods
12787 * holding the lock.
12788 */
12789 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12790 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12791 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12792 if (mCollectorGuest)
12793 {
12794 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12795 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12796 mCollectorGuest = NULL;
12797 }
12798#endif
12799
12800 if (aReason == Uninit::Abnormal)
12801 {
12802 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12803
12804 /* reset the state to Aborted */
12805 if (mData->mMachineState != MachineState_Aborted)
12806 i_setMachineState(MachineState_Aborted);
12807 }
12808
12809 // any machine settings modified?
12810 if (mData->flModifications)
12811 {
12812 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12813 i_rollback(false /* aNotify */);
12814 }
12815
12816 mData->mSession.mPID = NIL_RTPROCESS;
12817
12818 if (aReason == Uninit::Unexpected)
12819 {
12820 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12821 * client watcher thread to update the set of machines that have open
12822 * sessions. */
12823 mParent->i_updateClientWatcher();
12824 }
12825
12826 /* uninitialize all remote controls */
12827 if (mData->mSession.mRemoteControls.size())
12828 {
12829 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12830 mData->mSession.mRemoteControls.size()));
12831
12832 /* Always restart a the beginning, since the iterator is invalidated
12833 * by using erase(). */
12834 for (Data::Session::RemoteControlList::iterator
12835 it = mData->mSession.mRemoteControls.begin();
12836 it != mData->mSession.mRemoteControls.end();
12837 it = mData->mSession.mRemoteControls.begin())
12838 {
12839 ComPtr<IInternalSessionControl> pControl = *it;
12840 mData->mSession.mRemoteControls.erase(it);
12841 multilock.release();
12842 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12843 HRESULT rc = pControl->Uninitialize();
12844 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12845 if (FAILED(rc))
12846 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12847 multilock.acquire();
12848 }
12849 mData->mSession.mRemoteControls.clear();
12850 }
12851
12852 /* Remove all references to the NAT network service. The service will stop
12853 * if all references (also from other VMs) are removed. */
12854 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12855 {
12856 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12857 {
12858 BOOL enabled;
12859 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12860 if ( FAILED(hrc)
12861 || !enabled)
12862 continue;
12863
12864 NetworkAttachmentType_T type;
12865 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12866 if ( SUCCEEDED(hrc)
12867 && type == NetworkAttachmentType_NATNetwork)
12868 {
12869 Bstr name;
12870 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12871 if (SUCCEEDED(hrc))
12872 {
12873 multilock.release();
12874 Utf8Str strName(name);
12875 LogRel(("VM '%s' stops using NAT network '%s'\n",
12876 mUserData->s.strName.c_str(), strName.c_str()));
12877 mParent->i_natNetworkRefDec(strName);
12878 multilock.acquire();
12879 }
12880 }
12881 }
12882 }
12883
12884 /*
12885 * An expected uninitialization can come only from #i_checkForDeath().
12886 * Otherwise it means that something's gone really wrong (for example,
12887 * the Session implementation has released the VirtualBox reference
12888 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12889 * etc). However, it's also possible, that the client releases the IPC
12890 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12891 * but the VirtualBox release event comes first to the server process.
12892 * This case is practically possible, so we should not assert on an
12893 * unexpected uninit, just log a warning.
12894 */
12895
12896 if (aReason == Uninit::Unexpected)
12897 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12898
12899 if (aReason != Uninit::Normal)
12900 {
12901 mData->mSession.mDirectControl.setNull();
12902 }
12903 else
12904 {
12905 /* this must be null here (see #OnSessionEnd()) */
12906 Assert(mData->mSession.mDirectControl.isNull());
12907 Assert(mData->mSession.mState == SessionState_Unlocking);
12908 Assert(!mData->mSession.mProgress.isNull());
12909 }
12910 if (mData->mSession.mProgress)
12911 {
12912 if (aReason == Uninit::Normal)
12913 mData->mSession.mProgress->i_notifyComplete(S_OK);
12914 else
12915 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12916 COM_IIDOF(ISession),
12917 getComponentName(),
12918 tr("The VM session was aborted"));
12919 mData->mSession.mProgress.setNull();
12920 }
12921
12922 if (mConsoleTaskData.mProgress)
12923 {
12924 Assert(aReason == Uninit::Abnormal);
12925 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12926 COM_IIDOF(ISession),
12927 getComponentName(),
12928 tr("The VM session was aborted"));
12929 mConsoleTaskData.mProgress.setNull();
12930 }
12931
12932 /* remove the association between the peer machine and this session machine */
12933 Assert( (SessionMachine*)mData->mSession.mMachine == this
12934 || aReason == Uninit::Unexpected);
12935
12936 /* reset the rest of session data */
12937 mData->mSession.mLockType = LockType_Null;
12938 mData->mSession.mMachine.setNull();
12939 mData->mSession.mState = SessionState_Unlocked;
12940 mData->mSession.mName.setNull();
12941
12942 /* destroy the machine client token before leaving the exclusive lock */
12943 if (mClientToken)
12944 {
12945 delete mClientToken;
12946 mClientToken = NULL;
12947 }
12948
12949 /* fire an event */
12950 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12951
12952 uninitDataAndChildObjects();
12953
12954 /* free the essential data structure last */
12955 mData.free();
12956
12957 /* release the exclusive lock before setting the below two to NULL */
12958 multilock.release();
12959
12960 unconst(mParent) = NULL;
12961 unconst(mPeer) = NULL;
12962
12963 AuthLibUnload(&mAuthLibCtx);
12964
12965 LogFlowThisFuncLeave();
12966}
12967
12968// util::Lockable interface
12969////////////////////////////////////////////////////////////////////////////////
12970
12971/**
12972 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12973 * with the primary Machine instance (mPeer).
12974 */
12975RWLockHandle *SessionMachine::lockHandle() const
12976{
12977 AssertReturn(mPeer != NULL, NULL);
12978 return mPeer->lockHandle();
12979}
12980
12981// IInternalMachineControl methods
12982////////////////////////////////////////////////////////////////////////////////
12983
12984/**
12985 * Passes collected guest statistics to performance collector object
12986 */
12987HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12988 ULONG aCpuKernel, ULONG aCpuIdle,
12989 ULONG aMemTotal, ULONG aMemFree,
12990 ULONG aMemBalloon, ULONG aMemShared,
12991 ULONG aMemCache, ULONG aPageTotal,
12992 ULONG aAllocVMM, ULONG aFreeVMM,
12993 ULONG aBalloonedVMM, ULONG aSharedVMM,
12994 ULONG aVmNetRx, ULONG aVmNetTx)
12995{
12996#ifdef VBOX_WITH_RESOURCE_USAGE_API
12997 if (mCollectorGuest)
12998 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12999 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13000 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13001 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13002
13003 return S_OK;
13004#else
13005 NOREF(aValidStats);
13006 NOREF(aCpuUser);
13007 NOREF(aCpuKernel);
13008 NOREF(aCpuIdle);
13009 NOREF(aMemTotal);
13010 NOREF(aMemFree);
13011 NOREF(aMemBalloon);
13012 NOREF(aMemShared);
13013 NOREF(aMemCache);
13014 NOREF(aPageTotal);
13015 NOREF(aAllocVMM);
13016 NOREF(aFreeVMM);
13017 NOREF(aBalloonedVMM);
13018 NOREF(aSharedVMM);
13019 NOREF(aVmNetRx);
13020 NOREF(aVmNetTx);
13021 return E_NOTIMPL;
13022#endif
13023}
13024
13025////////////////////////////////////////////////////////////////////////////////
13026//
13027// SessionMachine task records
13028//
13029////////////////////////////////////////////////////////////////////////////////
13030
13031/**
13032 * Task record for saving the machine state.
13033 */
13034class SessionMachine::SaveStateTask
13035 : public Machine::Task
13036{
13037public:
13038 SaveStateTask(SessionMachine *m,
13039 Progress *p,
13040 const Utf8Str &t,
13041 Reason_T enmReason,
13042 const Utf8Str &strStateFilePath)
13043 : Task(m, p, t),
13044 m_enmReason(enmReason),
13045 m_strStateFilePath(strStateFilePath)
13046 {}
13047
13048private:
13049 void handler()
13050 {
13051 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13052 }
13053
13054 Reason_T m_enmReason;
13055 Utf8Str m_strStateFilePath;
13056
13057 friend class SessionMachine;
13058};
13059
13060/**
13061 * Task thread implementation for SessionMachine::SaveState(), called from
13062 * SessionMachine::taskHandler().
13063 *
13064 * @note Locks this object for writing.
13065 *
13066 * @param task
13067 * @return
13068 */
13069void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13070{
13071 LogFlowThisFuncEnter();
13072
13073 AutoCaller autoCaller(this);
13074 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13075 if (FAILED(autoCaller.rc()))
13076 {
13077 /* we might have been uninitialized because the session was accidentally
13078 * closed by the client, so don't assert */
13079 HRESULT rc = setError(E_FAIL,
13080 tr("The session has been accidentally closed"));
13081 task.m_pProgress->i_notifyComplete(rc);
13082 LogFlowThisFuncLeave();
13083 return;
13084 }
13085
13086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13087
13088 HRESULT rc = S_OK;
13089
13090 try
13091 {
13092 ComPtr<IInternalSessionControl> directControl;
13093 if (mData->mSession.mLockType == LockType_VM)
13094 directControl = mData->mSession.mDirectControl;
13095 if (directControl.isNull())
13096 throw setError(VBOX_E_INVALID_VM_STATE,
13097 tr("Trying to save state without a running VM"));
13098 alock.release();
13099 BOOL fSuspendedBySave;
13100 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13101 Assert(!fSuspendedBySave);
13102 alock.acquire();
13103
13104 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13105 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13106 throw E_FAIL);
13107
13108 if (SUCCEEDED(rc))
13109 {
13110 mSSData->strStateFilePath = task.m_strStateFilePath;
13111
13112 /* save all VM settings */
13113 rc = i_saveSettings(NULL);
13114 // no need to check whether VirtualBox.xml needs saving also since
13115 // we can't have a name change pending at this point
13116 }
13117 else
13118 {
13119 // On failure, set the state to the state we had at the beginning.
13120 i_setMachineState(task.m_machineStateBackup);
13121 i_updateMachineStateOnClient();
13122
13123 // Delete the saved state file (might have been already created).
13124 // No need to check whether this is shared with a snapshot here
13125 // because we certainly created a fresh saved state file here.
13126 RTFileDelete(task.m_strStateFilePath.c_str());
13127 }
13128 }
13129 catch (HRESULT aRC) { rc = aRC; }
13130
13131 task.m_pProgress->i_notifyComplete(rc);
13132
13133 LogFlowThisFuncLeave();
13134}
13135
13136/**
13137 * @note Locks this object for writing.
13138 */
13139HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13140{
13141 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13142}
13143
13144HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13145{
13146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13147
13148 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13149 if (FAILED(rc)) return rc;
13150
13151 if ( mData->mMachineState != MachineState_Running
13152 && mData->mMachineState != MachineState_Paused
13153 )
13154 return setError(VBOX_E_INVALID_VM_STATE,
13155 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13156 Global::stringifyMachineState(mData->mMachineState));
13157
13158 ComObjPtr<Progress> pProgress;
13159 pProgress.createObject();
13160 rc = pProgress->init(i_getVirtualBox(),
13161 static_cast<IMachine *>(this) /* aInitiator */,
13162 tr("Saving the execution state of the virtual machine"),
13163 FALSE /* aCancelable */);
13164 if (FAILED(rc))
13165 return rc;
13166
13167 Utf8Str strStateFilePath;
13168 i_composeSavedStateFilename(strStateFilePath);
13169
13170 /* create and start the task on a separate thread (note that it will not
13171 * start working until we release alock) */
13172 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13173 rc = pTask->createThread();
13174 if (FAILED(rc))
13175 return rc;
13176
13177 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13178 i_setMachineState(MachineState_Saving);
13179 i_updateMachineStateOnClient();
13180
13181 pProgress.queryInterfaceTo(aProgress.asOutParam());
13182
13183 return S_OK;
13184}
13185
13186/**
13187 * @note Locks this object for writing.
13188 */
13189HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13190{
13191 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13192
13193 HRESULT rc = i_checkStateDependency(MutableStateDep);
13194 if (FAILED(rc)) return rc;
13195
13196 if ( mData->mMachineState != MachineState_PoweredOff
13197 && mData->mMachineState != MachineState_Teleported
13198 && mData->mMachineState != MachineState_Aborted
13199 )
13200 return setError(VBOX_E_INVALID_VM_STATE,
13201 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13202 Global::stringifyMachineState(mData->mMachineState));
13203
13204 com::Utf8Str stateFilePathFull;
13205 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13206 if (RT_FAILURE(vrc))
13207 return setError(VBOX_E_FILE_ERROR,
13208 tr("Invalid saved state file path '%s' (%Rrc)"),
13209 aSavedStateFile.c_str(),
13210 vrc);
13211
13212 mSSData->strStateFilePath = stateFilePathFull;
13213
13214 /* The below i_setMachineState() will detect the state transition and will
13215 * update the settings file */
13216
13217 return i_setMachineState(MachineState_Saved);
13218}
13219
13220/**
13221 * @note Locks this object for writing.
13222 */
13223HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13224{
13225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13226
13227 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13228 if (FAILED(rc)) return rc;
13229
13230 if (mData->mMachineState != MachineState_Saved)
13231 return setError(VBOX_E_INVALID_VM_STATE,
13232 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13233 Global::stringifyMachineState(mData->mMachineState));
13234
13235 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13236
13237 /*
13238 * Saved -> PoweredOff transition will be detected in the SessionMachine
13239 * and properly handled.
13240 */
13241 rc = i_setMachineState(MachineState_PoweredOff);
13242 return rc;
13243}
13244
13245
13246/**
13247 * @note Locks the same as #i_setMachineState() does.
13248 */
13249HRESULT SessionMachine::updateState(MachineState_T aState)
13250{
13251 return i_setMachineState(aState);
13252}
13253
13254/**
13255 * @note Locks this object for writing.
13256 */
13257HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13258{
13259 IProgress *pProgress(aProgress);
13260
13261 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13262
13263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13264
13265 if (mData->mSession.mState != SessionState_Locked)
13266 return VBOX_E_INVALID_OBJECT_STATE;
13267
13268 if (!mData->mSession.mProgress.isNull())
13269 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13270
13271 /* If we didn't reference the NAT network service yet, add a reference to
13272 * force a start */
13273 if (miNATNetworksStarted < 1)
13274 {
13275 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13276 {
13277 BOOL enabled;
13278 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13279 if ( FAILED(hrc)
13280 || !enabled)
13281 continue;
13282
13283 NetworkAttachmentType_T type;
13284 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13285 if ( SUCCEEDED(hrc)
13286 && type == NetworkAttachmentType_NATNetwork)
13287 {
13288 Bstr name;
13289 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13290 if (SUCCEEDED(hrc))
13291 {
13292 Utf8Str strName(name);
13293 LogRel(("VM '%s' starts using NAT network '%s'\n",
13294 mUserData->s.strName.c_str(), strName.c_str()));
13295 mPeer->lockHandle()->unlockWrite();
13296 mParent->i_natNetworkRefInc(strName);
13297#ifdef RT_LOCK_STRICT
13298 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13299#else
13300 mPeer->lockHandle()->lockWrite();
13301#endif
13302 }
13303 }
13304 }
13305 miNATNetworksStarted++;
13306 }
13307
13308 LogFlowThisFunc(("returns S_OK.\n"));
13309 return S_OK;
13310}
13311
13312/**
13313 * @note Locks this object for writing.
13314 */
13315HRESULT SessionMachine::endPowerUp(LONG aResult)
13316{
13317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13318
13319 if (mData->mSession.mState != SessionState_Locked)
13320 return VBOX_E_INVALID_OBJECT_STATE;
13321
13322 /* Finalize the LaunchVMProcess progress object. */
13323 if (mData->mSession.mProgress)
13324 {
13325 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13326 mData->mSession.mProgress.setNull();
13327 }
13328
13329 if (SUCCEEDED((HRESULT)aResult))
13330 {
13331#ifdef VBOX_WITH_RESOURCE_USAGE_API
13332 /* The VM has been powered up successfully, so it makes sense
13333 * now to offer the performance metrics for a running machine
13334 * object. Doing it earlier wouldn't be safe. */
13335 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13336 mData->mSession.mPID);
13337#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13338 }
13339
13340 return S_OK;
13341}
13342
13343/**
13344 * @note Locks this object for writing.
13345 */
13346HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13347{
13348 LogFlowThisFuncEnter();
13349
13350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13351
13352 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13353 E_FAIL);
13354
13355 /* create a progress object to track operation completion */
13356 ComObjPtr<Progress> pProgress;
13357 pProgress.createObject();
13358 pProgress->init(i_getVirtualBox(),
13359 static_cast<IMachine *>(this) /* aInitiator */,
13360 tr("Stopping the virtual machine"),
13361 FALSE /* aCancelable */);
13362
13363 /* fill in the console task data */
13364 mConsoleTaskData.mLastState = mData->mMachineState;
13365 mConsoleTaskData.mProgress = pProgress;
13366
13367 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13368 i_setMachineState(MachineState_Stopping);
13369
13370 pProgress.queryInterfaceTo(aProgress.asOutParam());
13371
13372 return S_OK;
13373}
13374
13375/**
13376 * @note Locks this object for writing.
13377 */
13378HRESULT SessionMachine::endPoweringDown(LONG aResult,
13379 const com::Utf8Str &aErrMsg)
13380{
13381 LogFlowThisFuncEnter();
13382
13383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13384
13385 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13386 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13387 && mConsoleTaskData.mLastState != MachineState_Null,
13388 E_FAIL);
13389
13390 /*
13391 * On failure, set the state to the state we had when BeginPoweringDown()
13392 * was called (this is expected by Console::PowerDown() and the associated
13393 * task). On success the VM process already changed the state to
13394 * MachineState_PoweredOff, so no need to do anything.
13395 */
13396 if (FAILED(aResult))
13397 i_setMachineState(mConsoleTaskData.mLastState);
13398
13399 /* notify the progress object about operation completion */
13400 Assert(mConsoleTaskData.mProgress);
13401 if (SUCCEEDED(aResult))
13402 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13403 else
13404 {
13405 if (aErrMsg.length())
13406 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13407 COM_IIDOF(ISession),
13408 getComponentName(),
13409 aErrMsg.c_str());
13410 else
13411 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13412 }
13413
13414 /* clear out the temporary saved state data */
13415 mConsoleTaskData.mLastState = MachineState_Null;
13416 mConsoleTaskData.mProgress.setNull();
13417
13418 LogFlowThisFuncLeave();
13419 return S_OK;
13420}
13421
13422
13423/**
13424 * Goes through the USB filters of the given machine to see if the given
13425 * device matches any filter or not.
13426 *
13427 * @note Locks the same as USBController::hasMatchingFilter() does.
13428 */
13429HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13430 BOOL *aMatched,
13431 ULONG *aMaskedInterfaces)
13432{
13433 LogFlowThisFunc(("\n"));
13434
13435#ifdef VBOX_WITH_USB
13436 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13437#else
13438 NOREF(aDevice);
13439 NOREF(aMaskedInterfaces);
13440 *aMatched = FALSE;
13441#endif
13442
13443 return S_OK;
13444}
13445
13446/**
13447 * @note Locks the same as Host::captureUSBDevice() does.
13448 */
13449HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13450{
13451 LogFlowThisFunc(("\n"));
13452
13453#ifdef VBOX_WITH_USB
13454 /* if captureDeviceForVM() fails, it must have set extended error info */
13455 clearError();
13456 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13457 if (FAILED(rc)) return rc;
13458
13459 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13460 AssertReturn(service, E_FAIL);
13461 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13462#else
13463 NOREF(aId);
13464 return E_NOTIMPL;
13465#endif
13466}
13467
13468/**
13469 * @note Locks the same as Host::detachUSBDevice() does.
13470 */
13471HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13472 BOOL aDone)
13473{
13474 LogFlowThisFunc(("\n"));
13475
13476#ifdef VBOX_WITH_USB
13477 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13478 AssertReturn(service, E_FAIL);
13479 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13480#else
13481 NOREF(aId);
13482 NOREF(aDone);
13483 return E_NOTIMPL;
13484#endif
13485}
13486
13487/**
13488 * Inserts all machine filters to the USB proxy service and then calls
13489 * Host::autoCaptureUSBDevices().
13490 *
13491 * Called by Console from the VM process upon VM startup.
13492 *
13493 * @note Locks what called methods lock.
13494 */
13495HRESULT SessionMachine::autoCaptureUSBDevices()
13496{
13497 LogFlowThisFunc(("\n"));
13498
13499#ifdef VBOX_WITH_USB
13500 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13501 AssertComRC(rc);
13502 NOREF(rc);
13503
13504 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13505 AssertReturn(service, E_FAIL);
13506 return service->autoCaptureDevicesForVM(this);
13507#else
13508 return S_OK;
13509#endif
13510}
13511
13512/**
13513 * Removes all machine filters from the USB proxy service and then calls
13514 * Host::detachAllUSBDevices().
13515 *
13516 * Called by Console from the VM process upon normal VM termination or by
13517 * SessionMachine::uninit() upon abnormal VM termination (from under the
13518 * Machine/SessionMachine lock).
13519 *
13520 * @note Locks what called methods lock.
13521 */
13522HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526#ifdef VBOX_WITH_USB
13527 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13528 AssertComRC(rc);
13529 NOREF(rc);
13530
13531 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13532 AssertReturn(service, E_FAIL);
13533 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13534#else
13535 NOREF(aDone);
13536 return S_OK;
13537#endif
13538}
13539
13540/**
13541 * @note Locks this object for writing.
13542 */
13543HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13544 ComPtr<IProgress> &aProgress)
13545{
13546 LogFlowThisFuncEnter();
13547
13548 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13549 /*
13550 * We don't assert below because it might happen that a non-direct session
13551 * informs us it is closed right after we've been uninitialized -- it's ok.
13552 */
13553
13554 /* get IInternalSessionControl interface */
13555 ComPtr<IInternalSessionControl> control(aSession);
13556
13557 ComAssertRet(!control.isNull(), E_INVALIDARG);
13558
13559 /* Creating a Progress object requires the VirtualBox lock, and
13560 * thus locking it here is required by the lock order rules. */
13561 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13562
13563 if (control == mData->mSession.mDirectControl)
13564 {
13565 /* The direct session is being normally closed by the client process
13566 * ----------------------------------------------------------------- */
13567
13568 /* go to the closing state (essential for all open*Session() calls and
13569 * for #i_checkForDeath()) */
13570 Assert(mData->mSession.mState == SessionState_Locked);
13571 mData->mSession.mState = SessionState_Unlocking;
13572
13573 /* set direct control to NULL to release the remote instance */
13574 mData->mSession.mDirectControl.setNull();
13575 LogFlowThisFunc(("Direct control is set to NULL\n"));
13576
13577 if (mData->mSession.mProgress)
13578 {
13579 /* finalize the progress, someone might wait if a frontend
13580 * closes the session before powering on the VM. */
13581 mData->mSession.mProgress->notifyComplete(E_FAIL,
13582 COM_IIDOF(ISession),
13583 getComponentName(),
13584 tr("The VM session was closed before any attempt to power it on"));
13585 mData->mSession.mProgress.setNull();
13586 }
13587
13588 /* Create the progress object the client will use to wait until
13589 * #i_checkForDeath() is called to uninitialize this session object after
13590 * it releases the IPC semaphore.
13591 * Note! Because we're "reusing" mProgress here, this must be a proxy
13592 * object just like for LaunchVMProcess. */
13593 Assert(mData->mSession.mProgress.isNull());
13594 ComObjPtr<ProgressProxy> progress;
13595 progress.createObject();
13596 ComPtr<IUnknown> pPeer(mPeer);
13597 progress->init(mParent, pPeer,
13598 Bstr(tr("Closing session")).raw(),
13599 FALSE /* aCancelable */);
13600 progress.queryInterfaceTo(aProgress.asOutParam());
13601 mData->mSession.mProgress = progress;
13602 }
13603 else
13604 {
13605 /* the remote session is being normally closed */
13606 bool found = false;
13607 for (Data::Session::RemoteControlList::iterator
13608 it = mData->mSession.mRemoteControls.begin();
13609 it != mData->mSession.mRemoteControls.end();
13610 ++it)
13611 {
13612 if (control == *it)
13613 {
13614 found = true;
13615 // This MUST be erase(it), not remove(*it) as the latter
13616 // triggers a very nasty use after free due to the place where
13617 // the value "lives".
13618 mData->mSession.mRemoteControls.erase(it);
13619 break;
13620 }
13621 }
13622 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13623 E_INVALIDARG);
13624 }
13625
13626 /* signal the client watcher thread, because the client is going away */
13627 mParent->i_updateClientWatcher();
13628
13629 LogFlowThisFuncLeave();
13630 return S_OK;
13631}
13632
13633HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13634 std::vector<com::Utf8Str> &aValues,
13635 std::vector<LONG64> &aTimestamps,
13636 std::vector<com::Utf8Str> &aFlags)
13637{
13638 LogFlowThisFunc(("\n"));
13639
13640#ifdef VBOX_WITH_GUEST_PROPS
13641 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13642
13643 size_t cEntries = mHWData->mGuestProperties.size();
13644 aNames.resize(cEntries);
13645 aValues.resize(cEntries);
13646 aTimestamps.resize(cEntries);
13647 aFlags.resize(cEntries);
13648
13649 size_t i = 0;
13650 for (HWData::GuestPropertyMap::const_iterator
13651 it = mHWData->mGuestProperties.begin();
13652 it != mHWData->mGuestProperties.end();
13653 ++it, ++i)
13654 {
13655 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13656 aNames[i] = it->first;
13657 aValues[i] = it->second.strValue;
13658 aTimestamps[i] = it->second.mTimestamp;
13659
13660 /* If it is NULL, keep it NULL. */
13661 if (it->second.mFlags)
13662 {
13663 GuestPropWriteFlags(it->second.mFlags, szFlags);
13664 aFlags[i] = szFlags;
13665 }
13666 else
13667 aFlags[i] = "";
13668 }
13669 return S_OK;
13670#else
13671 ReturnComNotImplemented();
13672#endif
13673}
13674
13675HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13676 const com::Utf8Str &aValue,
13677 LONG64 aTimestamp,
13678 const com::Utf8Str &aFlags)
13679{
13680 LogFlowThisFunc(("\n"));
13681
13682#ifdef VBOX_WITH_GUEST_PROPS
13683 try
13684 {
13685 /*
13686 * Convert input up front.
13687 */
13688 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13689 if (aFlags.length())
13690 {
13691 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13692 AssertRCReturn(vrc, E_INVALIDARG);
13693 }
13694
13695 /*
13696 * Now grab the object lock, validate the state and do the update.
13697 */
13698
13699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13700
13701 if (!Global::IsOnline(mData->mMachineState))
13702 {
13703 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13704 VBOX_E_INVALID_VM_STATE);
13705 }
13706
13707 i_setModified(IsModified_MachineData);
13708 mHWData.backup();
13709
13710 bool fDelete = !aValue.length();
13711 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13712 if (it != mHWData->mGuestProperties.end())
13713 {
13714 if (!fDelete)
13715 {
13716 it->second.strValue = aValue;
13717 it->second.mTimestamp = aTimestamp;
13718 it->second.mFlags = fFlags;
13719 }
13720 else
13721 mHWData->mGuestProperties.erase(it);
13722
13723 mData->mGuestPropertiesModified = TRUE;
13724 }
13725 else if (!fDelete)
13726 {
13727 HWData::GuestProperty prop;
13728 prop.strValue = aValue;
13729 prop.mTimestamp = aTimestamp;
13730 prop.mFlags = fFlags;
13731
13732 mHWData->mGuestProperties[aName] = prop;
13733 mData->mGuestPropertiesModified = TRUE;
13734 }
13735
13736 alock.release();
13737
13738 mParent->i_onGuestPropertyChange(mData->mUuid,
13739 Bstr(aName).raw(),
13740 Bstr(aValue).raw(),
13741 Bstr(aFlags).raw());
13742 }
13743 catch (...)
13744 {
13745 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13746 }
13747 return S_OK;
13748#else
13749 ReturnComNotImplemented();
13750#endif
13751}
13752
13753
13754HRESULT SessionMachine::lockMedia()
13755{
13756 AutoMultiWriteLock2 alock(this->lockHandle(),
13757 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13758
13759 AssertReturn( mData->mMachineState == MachineState_Starting
13760 || mData->mMachineState == MachineState_Restoring
13761 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13762
13763 clearError();
13764 alock.release();
13765 return i_lockMedia();
13766}
13767
13768HRESULT SessionMachine::unlockMedia()
13769{
13770 HRESULT hrc = i_unlockMedia();
13771 return hrc;
13772}
13773
13774HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13775 ComPtr<IMediumAttachment> &aNewAttachment)
13776{
13777 // request the host lock first, since might be calling Host methods for getting host drives;
13778 // next, protect the media tree all the while we're in here, as well as our member variables
13779 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13780 this->lockHandle(),
13781 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13782
13783 IMediumAttachment *iAttach = aAttachment;
13784 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13785
13786 Utf8Str ctrlName;
13787 LONG lPort;
13788 LONG lDevice;
13789 bool fTempEject;
13790 {
13791 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13792
13793 /* Need to query the details first, as the IMediumAttachment reference
13794 * might be to the original settings, which we are going to change. */
13795 ctrlName = pAttach->i_getControllerName();
13796 lPort = pAttach->i_getPort();
13797 lDevice = pAttach->i_getDevice();
13798 fTempEject = pAttach->i_getTempEject();
13799 }
13800
13801 if (!fTempEject)
13802 {
13803 /* Remember previously mounted medium. The medium before taking the
13804 * backup is not necessarily the same thing. */
13805 ComObjPtr<Medium> oldmedium;
13806 oldmedium = pAttach->i_getMedium();
13807
13808 i_setModified(IsModified_Storage);
13809 mMediumAttachments.backup();
13810
13811 // The backup operation makes the pAttach reference point to the
13812 // old settings. Re-get the correct reference.
13813 pAttach = i_findAttachment(*mMediumAttachments.data(),
13814 ctrlName,
13815 lPort,
13816 lDevice);
13817
13818 {
13819 AutoCaller autoAttachCaller(this);
13820 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13821
13822 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13823 if (!oldmedium.isNull())
13824 oldmedium->i_removeBackReference(mData->mUuid);
13825
13826 pAttach->i_updateMedium(NULL);
13827 pAttach->i_updateEjected();
13828 }
13829
13830 i_setModified(IsModified_Storage);
13831 }
13832 else
13833 {
13834 {
13835 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13836 pAttach->i_updateEjected();
13837 }
13838 }
13839
13840 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13841
13842 return S_OK;
13843}
13844
13845HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13846 com::Utf8Str &aResult)
13847{
13848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13849
13850 HRESULT hr = S_OK;
13851
13852 if (!mAuthLibCtx.hAuthLibrary)
13853 {
13854 /* Load the external authentication library. */
13855 Bstr authLibrary;
13856 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13857
13858 Utf8Str filename = authLibrary;
13859
13860 int rc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13861 if (RT_FAILURE(rc))
13862 {
13863 hr = setError(E_FAIL,
13864 tr("Could not load the external authentication library '%s' (%Rrc)"),
13865 filename.c_str(), rc);
13866 }
13867 }
13868
13869 /* The auth library might need the machine lock. */
13870 alock.release();
13871
13872 if (FAILED(hr))
13873 return hr;
13874
13875 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13876 {
13877 enum VRDEAuthParams
13878 {
13879 parmUuid = 1,
13880 parmGuestJudgement,
13881 parmUser,
13882 parmPassword,
13883 parmDomain,
13884 parmClientId
13885 };
13886
13887 AuthResult result = AuthResultAccessDenied;
13888
13889 Guid uuid(aAuthParams[parmUuid]);
13890 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13891 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13892
13893 result = AuthLibAuthenticate(&mAuthLibCtx,
13894 uuid.raw(), guestJudgement,
13895 aAuthParams[parmUser].c_str(),
13896 aAuthParams[parmPassword].c_str(),
13897 aAuthParams[parmDomain].c_str(),
13898 u32ClientId);
13899
13900 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13901 size_t cbPassword = aAuthParams[parmPassword].length();
13902 if (cbPassword)
13903 {
13904 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13905 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13906 }
13907
13908 if (result == AuthResultAccessGranted)
13909 aResult = "granted";
13910 else
13911 aResult = "denied";
13912
13913 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13914 aAuthParams[parmUser].c_str(), aResult.c_str()));
13915 }
13916 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13917 {
13918 enum VRDEAuthDisconnectParams
13919 {
13920 parmUuid = 1,
13921 parmClientId
13922 };
13923
13924 Guid uuid(aAuthParams[parmUuid]);
13925 uint32_t u32ClientId = 0;
13926 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13927 }
13928 else
13929 {
13930 hr = E_INVALIDARG;
13931 }
13932
13933 return hr;
13934}
13935
13936// public methods only for internal purposes
13937/////////////////////////////////////////////////////////////////////////////
13938
13939#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13940/**
13941 * Called from the client watcher thread to check for expected or unexpected
13942 * death of the client process that has a direct session to this machine.
13943 *
13944 * On Win32 and on OS/2, this method is called only when we've got the
13945 * mutex (i.e. the client has either died or terminated normally) so it always
13946 * returns @c true (the client is terminated, the session machine is
13947 * uninitialized).
13948 *
13949 * On other platforms, the method returns @c true if the client process has
13950 * terminated normally or abnormally and the session machine was uninitialized,
13951 * and @c false if the client process is still alive.
13952 *
13953 * @note Locks this object for writing.
13954 */
13955bool SessionMachine::i_checkForDeath()
13956{
13957 Uninit::Reason reason;
13958 bool terminated = false;
13959
13960 /* Enclose autoCaller with a block because calling uninit() from under it
13961 * will deadlock. */
13962 {
13963 AutoCaller autoCaller(this);
13964 if (!autoCaller.isOk())
13965 {
13966 /* return true if not ready, to cause the client watcher to exclude
13967 * the corresponding session from watching */
13968 LogFlowThisFunc(("Already uninitialized!\n"));
13969 return true;
13970 }
13971
13972 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13973
13974 /* Determine the reason of death: if the session state is Closing here,
13975 * everything is fine. Otherwise it means that the client did not call
13976 * OnSessionEnd() before it released the IPC semaphore. This may happen
13977 * either because the client process has abnormally terminated, or
13978 * because it simply forgot to call ISession::Close() before exiting. We
13979 * threat the latter also as an abnormal termination (see
13980 * Session::uninit() for details). */
13981 reason = mData->mSession.mState == SessionState_Unlocking ?
13982 Uninit::Normal :
13983 Uninit::Abnormal;
13984
13985 if (mClientToken)
13986 terminated = mClientToken->release();
13987 } /* AutoCaller block */
13988
13989 if (terminated)
13990 uninit(reason);
13991
13992 return terminated;
13993}
13994
13995void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13996{
13997 LogFlowThisFunc(("\n"));
13998
13999 strTokenId.setNull();
14000
14001 AutoCaller autoCaller(this);
14002 AssertComRCReturnVoid(autoCaller.rc());
14003
14004 Assert(mClientToken);
14005 if (mClientToken)
14006 mClientToken->getId(strTokenId);
14007}
14008#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14009IToken *SessionMachine::i_getToken()
14010{
14011 LogFlowThisFunc(("\n"));
14012
14013 AutoCaller autoCaller(this);
14014 AssertComRCReturn(autoCaller.rc(), NULL);
14015
14016 Assert(mClientToken);
14017 if (mClientToken)
14018 return mClientToken->getToken();
14019 else
14020 return NULL;
14021}
14022#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14023
14024Machine::ClientToken *SessionMachine::i_getClientToken()
14025{
14026 LogFlowThisFunc(("\n"));
14027
14028 AutoCaller autoCaller(this);
14029 AssertComRCReturn(autoCaller.rc(), NULL);
14030
14031 return mClientToken;
14032}
14033
14034
14035/**
14036 * @note Locks this object for reading.
14037 */
14038HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14039{
14040 LogFlowThisFunc(("\n"));
14041
14042 AutoCaller autoCaller(this);
14043 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14044
14045 ComPtr<IInternalSessionControl> directControl;
14046 {
14047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14048 if (mData->mSession.mLockType == LockType_VM)
14049 directControl = mData->mSession.mDirectControl;
14050 }
14051
14052 /* ignore notifications sent after #OnSessionEnd() is called */
14053 if (!directControl)
14054 return S_OK;
14055
14056 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14057}
14058
14059/**
14060 * @note Locks this object for reading.
14061 */
14062HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14063 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14064 IN_BSTR aGuestIp, LONG aGuestPort)
14065{
14066 LogFlowThisFunc(("\n"));
14067
14068 AutoCaller autoCaller(this);
14069 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14070
14071 ComPtr<IInternalSessionControl> directControl;
14072 {
14073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14074 if (mData->mSession.mLockType == LockType_VM)
14075 directControl = mData->mSession.mDirectControl;
14076 }
14077
14078 /* ignore notifications sent after #OnSessionEnd() is called */
14079 if (!directControl)
14080 return S_OK;
14081 /*
14082 * instead acting like callback we ask IVirtualBox deliver corresponding event
14083 */
14084
14085 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14086 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14087 return S_OK;
14088}
14089
14090/**
14091 * @note Locks this object for reading.
14092 */
14093HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14094{
14095 LogFlowThisFunc(("\n"));
14096
14097 AutoCaller autoCaller(this);
14098 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14099
14100 ComPtr<IInternalSessionControl> directControl;
14101 {
14102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14103 if (mData->mSession.mLockType == LockType_VM)
14104 directControl = mData->mSession.mDirectControl;
14105 }
14106
14107 /* ignore notifications sent after #OnSessionEnd() is called */
14108 if (!directControl)
14109 return S_OK;
14110
14111 return directControl->OnAudioAdapterChange(audioAdapter);
14112}
14113
14114/**
14115 * @note Locks this object for reading.
14116 */
14117HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14118{
14119 LogFlowThisFunc(("\n"));
14120
14121 AutoCaller autoCaller(this);
14122 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14123
14124 ComPtr<IInternalSessionControl> directControl;
14125 {
14126 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14127 if (mData->mSession.mLockType == LockType_VM)
14128 directControl = mData->mSession.mDirectControl;
14129 }
14130
14131 /* ignore notifications sent after #OnSessionEnd() is called */
14132 if (!directControl)
14133 return S_OK;
14134
14135 return directControl->OnSerialPortChange(serialPort);
14136}
14137
14138/**
14139 * @note Locks this object for reading.
14140 */
14141HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14142{
14143 LogFlowThisFunc(("\n"));
14144
14145 AutoCaller autoCaller(this);
14146 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14147
14148 ComPtr<IInternalSessionControl> directControl;
14149 {
14150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14151 if (mData->mSession.mLockType == LockType_VM)
14152 directControl = mData->mSession.mDirectControl;
14153 }
14154
14155 /* ignore notifications sent after #OnSessionEnd() is called */
14156 if (!directControl)
14157 return S_OK;
14158
14159 return directControl->OnParallelPortChange(parallelPort);
14160}
14161
14162/**
14163 * @note Locks this object for reading.
14164 */
14165HRESULT SessionMachine::i_onStorageControllerChange()
14166{
14167 LogFlowThisFunc(("\n"));
14168
14169 AutoCaller autoCaller(this);
14170 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14171
14172 ComPtr<IInternalSessionControl> directControl;
14173 {
14174 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14175 if (mData->mSession.mLockType == LockType_VM)
14176 directControl = mData->mSession.mDirectControl;
14177 }
14178
14179 /* ignore notifications sent after #OnSessionEnd() is called */
14180 if (!directControl)
14181 return S_OK;
14182
14183 return directControl->OnStorageControllerChange();
14184}
14185
14186/**
14187 * @note Locks this object for reading.
14188 */
14189HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14190{
14191 LogFlowThisFunc(("\n"));
14192
14193 AutoCaller autoCaller(this);
14194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14195
14196 ComPtr<IInternalSessionControl> directControl;
14197 {
14198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14199 if (mData->mSession.mLockType == LockType_VM)
14200 directControl = mData->mSession.mDirectControl;
14201 }
14202
14203 /* ignore notifications sent after #OnSessionEnd() is called */
14204 if (!directControl)
14205 return S_OK;
14206
14207 return directControl->OnMediumChange(aAttachment, aForce);
14208}
14209
14210/**
14211 * @note Locks this object for reading.
14212 */
14213HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14214{
14215 LogFlowThisFunc(("\n"));
14216
14217 AutoCaller autoCaller(this);
14218 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14219
14220 ComPtr<IInternalSessionControl> directControl;
14221 {
14222 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14223 if (mData->mSession.mLockType == LockType_VM)
14224 directControl = mData->mSession.mDirectControl;
14225 }
14226
14227 /* ignore notifications sent after #OnSessionEnd() is called */
14228 if (!directControl)
14229 return S_OK;
14230
14231 return directControl->OnCPUChange(aCPU, aRemove);
14232}
14233
14234HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnVRDEServerChange(aRestart);
14277}
14278
14279/**
14280 * @note Locks this object for reading.
14281 */
14282HRESULT SessionMachine::i_onVideoCaptureChange()
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 if (mData->mSession.mLockType == LockType_VM)
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnVideoCaptureChange();
14301}
14302
14303/**
14304 * @note Locks this object for reading.
14305 */
14306HRESULT SessionMachine::i_onUSBControllerChange()
14307{
14308 LogFlowThisFunc(("\n"));
14309
14310 AutoCaller autoCaller(this);
14311 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14312
14313 ComPtr<IInternalSessionControl> directControl;
14314 {
14315 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14316 if (mData->mSession.mLockType == LockType_VM)
14317 directControl = mData->mSession.mDirectControl;
14318 }
14319
14320 /* ignore notifications sent after #OnSessionEnd() is called */
14321 if (!directControl)
14322 return S_OK;
14323
14324 return directControl->OnUSBControllerChange();
14325}
14326
14327/**
14328 * @note Locks this object for reading.
14329 */
14330HRESULT SessionMachine::i_onSharedFolderChange()
14331{
14332 LogFlowThisFunc(("\n"));
14333
14334 AutoCaller autoCaller(this);
14335 AssertComRCReturnRC(autoCaller.rc());
14336
14337 ComPtr<IInternalSessionControl> directControl;
14338 {
14339 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14340 if (mData->mSession.mLockType == LockType_VM)
14341 directControl = mData->mSession.mDirectControl;
14342 }
14343
14344 /* ignore notifications sent after #OnSessionEnd() is called */
14345 if (!directControl)
14346 return S_OK;
14347
14348 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14349}
14350
14351/**
14352 * @note Locks this object for reading.
14353 */
14354HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14355{
14356 LogFlowThisFunc(("\n"));
14357
14358 AutoCaller autoCaller(this);
14359 AssertComRCReturnRC(autoCaller.rc());
14360
14361 ComPtr<IInternalSessionControl> directControl;
14362 {
14363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14364 if (mData->mSession.mLockType == LockType_VM)
14365 directControl = mData->mSession.mDirectControl;
14366 }
14367
14368 /* ignore notifications sent after #OnSessionEnd() is called */
14369 if (!directControl)
14370 return S_OK;
14371
14372 return directControl->OnClipboardModeChange(aClipboardMode);
14373}
14374
14375/**
14376 * @note Locks this object for reading.
14377 */
14378HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14379{
14380 LogFlowThisFunc(("\n"));
14381
14382 AutoCaller autoCaller(this);
14383 AssertComRCReturnRC(autoCaller.rc());
14384
14385 ComPtr<IInternalSessionControl> directControl;
14386 {
14387 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14388 if (mData->mSession.mLockType == LockType_VM)
14389 directControl = mData->mSession.mDirectControl;
14390 }
14391
14392 /* ignore notifications sent after #OnSessionEnd() is called */
14393 if (!directControl)
14394 return S_OK;
14395
14396 return directControl->OnDnDModeChange(aDnDMode);
14397}
14398
14399/**
14400 * @note Locks this object for reading.
14401 */
14402HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14403{
14404 LogFlowThisFunc(("\n"));
14405
14406 AutoCaller autoCaller(this);
14407 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14408
14409 ComPtr<IInternalSessionControl> directControl;
14410 {
14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14412 if (mData->mSession.mLockType == LockType_VM)
14413 directControl = mData->mSession.mDirectControl;
14414 }
14415
14416 /* ignore notifications sent after #OnSessionEnd() is called */
14417 if (!directControl)
14418 return S_OK;
14419
14420 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14421}
14422
14423/**
14424 * @note Locks this object for reading.
14425 */
14426HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14427{
14428 LogFlowThisFunc(("\n"));
14429
14430 AutoCaller autoCaller(this);
14431 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14432
14433 ComPtr<IInternalSessionControl> directControl;
14434 {
14435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14436 if (mData->mSession.mLockType == LockType_VM)
14437 directControl = mData->mSession.mDirectControl;
14438 }
14439
14440 /* ignore notifications sent after #OnSessionEnd() is called */
14441 if (!directControl)
14442 return S_OK;
14443
14444 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14445}
14446
14447/**
14448 * Returns @c true if this machine's USB controller reports it has a matching
14449 * filter for the given USB device and @c false otherwise.
14450 *
14451 * @note locks this object for reading.
14452 */
14453bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14454{
14455 AutoCaller autoCaller(this);
14456 /* silently return if not ready -- this method may be called after the
14457 * direct machine session has been called */
14458 if (!autoCaller.isOk())
14459 return false;
14460
14461#ifdef VBOX_WITH_USB
14462 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14463
14464 switch (mData->mMachineState)
14465 {
14466 case MachineState_Starting:
14467 case MachineState_Restoring:
14468 case MachineState_TeleportingIn:
14469 case MachineState_Paused:
14470 case MachineState_Running:
14471 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14472 * elsewhere... */
14473 alock.release();
14474 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14475 default: break;
14476 }
14477#else
14478 NOREF(aDevice);
14479 NOREF(aMaskedIfs);
14480#endif
14481 return false;
14482}
14483
14484/**
14485 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14486 */
14487HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14488 IVirtualBoxErrorInfo *aError,
14489 ULONG aMaskedIfs,
14490 const com::Utf8Str &aCaptureFilename)
14491{
14492 LogFlowThisFunc(("\n"));
14493
14494 AutoCaller autoCaller(this);
14495
14496 /* This notification may happen after the machine object has been
14497 * uninitialized (the session was closed), so don't assert. */
14498 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14499
14500 ComPtr<IInternalSessionControl> directControl;
14501 {
14502 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14503 if (mData->mSession.mLockType == LockType_VM)
14504 directControl = mData->mSession.mDirectControl;
14505 }
14506
14507 /* fail on notifications sent after #OnSessionEnd() is called, it is
14508 * expected by the caller */
14509 if (!directControl)
14510 return E_FAIL;
14511
14512 /* No locks should be held at this point. */
14513 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14514 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14515
14516 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14517}
14518
14519/**
14520 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14521 */
14522HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14523 IVirtualBoxErrorInfo *aError)
14524{
14525 LogFlowThisFunc(("\n"));
14526
14527 AutoCaller autoCaller(this);
14528
14529 /* This notification may happen after the machine object has been
14530 * uninitialized (the session was closed), so don't assert. */
14531 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14532
14533 ComPtr<IInternalSessionControl> directControl;
14534 {
14535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14536 if (mData->mSession.mLockType == LockType_VM)
14537 directControl = mData->mSession.mDirectControl;
14538 }
14539
14540 /* fail on notifications sent after #OnSessionEnd() is called, it is
14541 * expected by the caller */
14542 if (!directControl)
14543 return E_FAIL;
14544
14545 /* No locks should be held at this point. */
14546 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14547 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14548
14549 return directControl->OnUSBDeviceDetach(aId, aError);
14550}
14551
14552// protected methods
14553/////////////////////////////////////////////////////////////////////////////
14554
14555/**
14556 * Deletes the given file if it is no longer in use by either the current machine state
14557 * (if the machine is "saved") or any of the machine's snapshots.
14558 *
14559 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14560 * but is different for each SnapshotMachine. When calling this, the order of calling this
14561 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14562 * is therefore critical. I know, it's all rather messy.
14563 *
14564 * @param strStateFile
14565 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14566 * the test for whether the saved state file is in use.
14567 */
14568void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14569 Snapshot *pSnapshotToIgnore)
14570{
14571 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14572 if ( (strStateFile.isNotEmpty())
14573 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14574 )
14575 // ... and it must also not be shared with other snapshots
14576 if ( !mData->mFirstSnapshot
14577 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14578 // this checks the SnapshotMachine's state file paths
14579 )
14580 RTFileDelete(strStateFile.c_str());
14581}
14582
14583/**
14584 * Locks the attached media.
14585 *
14586 * All attached hard disks are locked for writing and DVD/floppy are locked for
14587 * reading. Parents of attached hard disks (if any) are locked for reading.
14588 *
14589 * This method also performs accessibility check of all media it locks: if some
14590 * media is inaccessible, the method will return a failure and a bunch of
14591 * extended error info objects per each inaccessible medium.
14592 *
14593 * Note that this method is atomic: if it returns a success, all media are
14594 * locked as described above; on failure no media is locked at all (all
14595 * succeeded individual locks will be undone).
14596 *
14597 * The caller is responsible for doing the necessary state sanity checks.
14598 *
14599 * The locks made by this method must be undone by calling #unlockMedia() when
14600 * no more needed.
14601 */
14602HRESULT SessionMachine::i_lockMedia()
14603{
14604 AutoCaller autoCaller(this);
14605 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14606
14607 AutoMultiWriteLock2 alock(this->lockHandle(),
14608 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14609
14610 /* bail out if trying to lock things with already set up locking */
14611 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14612
14613 MultiResult mrc(S_OK);
14614
14615 /* Collect locking information for all medium objects attached to the VM. */
14616 for (MediumAttachmentList::const_iterator
14617 it = mMediumAttachments->begin();
14618 it != mMediumAttachments->end();
14619 ++it)
14620 {
14621 MediumAttachment *pAtt = *it;
14622 DeviceType_T devType = pAtt->i_getType();
14623 Medium *pMedium = pAtt->i_getMedium();
14624
14625 MediumLockList *pMediumLockList(new MediumLockList());
14626 // There can be attachments without a medium (floppy/dvd), and thus
14627 // it's impossible to create a medium lock list. It still makes sense
14628 // to have the empty medium lock list in the map in case a medium is
14629 // attached later.
14630 if (pMedium != NULL)
14631 {
14632 MediumType_T mediumType = pMedium->i_getType();
14633 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14634 || mediumType == MediumType_Shareable;
14635 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14636
14637 alock.release();
14638 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14639 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14640 false /* fMediumLockWriteAll */,
14641 NULL,
14642 *pMediumLockList);
14643 alock.acquire();
14644 if (FAILED(mrc))
14645 {
14646 delete pMediumLockList;
14647 mData->mSession.mLockedMedia.Clear();
14648 break;
14649 }
14650 }
14651
14652 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14653 if (FAILED(rc))
14654 {
14655 mData->mSession.mLockedMedia.Clear();
14656 mrc = setError(rc,
14657 tr("Collecting locking information for all attached media failed"));
14658 break;
14659 }
14660 }
14661
14662 if (SUCCEEDED(mrc))
14663 {
14664 /* Now lock all media. If this fails, nothing is locked. */
14665 alock.release();
14666 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14667 alock.acquire();
14668 if (FAILED(rc))
14669 {
14670 mrc = setError(rc,
14671 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14672 }
14673 }
14674
14675 return mrc;
14676}
14677
14678/**
14679 * Undoes the locks made by by #lockMedia().
14680 */
14681HRESULT SessionMachine::i_unlockMedia()
14682{
14683 AutoCaller autoCaller(this);
14684 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14685
14686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14687
14688 /* we may be holding important error info on the current thread;
14689 * preserve it */
14690 ErrorInfoKeeper eik;
14691
14692 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14693 AssertComRC(rc);
14694 return rc;
14695}
14696
14697/**
14698 * Helper to change the machine state (reimplementation).
14699 *
14700 * @note Locks this object for writing.
14701 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14702 * it can cause crashes in random places due to unexpectedly committing
14703 * the current settings. The caller is responsible for that. The call
14704 * to saveStateSettings is fine, because this method does not commit.
14705 */
14706HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14707{
14708 LogFlowThisFuncEnter();
14709 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14710
14711 AutoCaller autoCaller(this);
14712 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14713
14714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14715
14716 MachineState_T oldMachineState = mData->mMachineState;
14717
14718 AssertMsgReturn(oldMachineState != aMachineState,
14719 ("oldMachineState=%s, aMachineState=%s\n",
14720 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14721 E_FAIL);
14722
14723 HRESULT rc = S_OK;
14724
14725 int stsFlags = 0;
14726 bool deleteSavedState = false;
14727
14728 /* detect some state transitions */
14729
14730 if ( ( oldMachineState == MachineState_Saved
14731 && aMachineState == MachineState_Restoring)
14732 || ( ( oldMachineState == MachineState_PoweredOff
14733 || oldMachineState == MachineState_Teleported
14734 || oldMachineState == MachineState_Aborted
14735 )
14736 && ( aMachineState == MachineState_TeleportingIn
14737 || aMachineState == MachineState_Starting
14738 )
14739 )
14740 )
14741 {
14742 /* The EMT thread is about to start */
14743
14744 /* Nothing to do here for now... */
14745
14746 /// @todo NEWMEDIA don't let mDVDDrive and other children
14747 /// change anything when in the Starting/Restoring state
14748 }
14749 else if ( ( oldMachineState == MachineState_Running
14750 || oldMachineState == MachineState_Paused
14751 || oldMachineState == MachineState_Teleporting
14752 || oldMachineState == MachineState_OnlineSnapshotting
14753 || oldMachineState == MachineState_LiveSnapshotting
14754 || oldMachineState == MachineState_Stuck
14755 || oldMachineState == MachineState_Starting
14756 || oldMachineState == MachineState_Stopping
14757 || oldMachineState == MachineState_Saving
14758 || oldMachineState == MachineState_Restoring
14759 || oldMachineState == MachineState_TeleportingPausedVM
14760 || oldMachineState == MachineState_TeleportingIn
14761 )
14762 && ( aMachineState == MachineState_PoweredOff
14763 || aMachineState == MachineState_Saved
14764 || aMachineState == MachineState_Teleported
14765 || aMachineState == MachineState_Aborted
14766 )
14767 )
14768 {
14769 /* The EMT thread has just stopped, unlock attached media. Note that as
14770 * opposed to locking that is done from Console, we do unlocking here
14771 * because the VM process may have aborted before having a chance to
14772 * properly unlock all media it locked. */
14773
14774 unlockMedia();
14775 }
14776
14777 if (oldMachineState == MachineState_Restoring)
14778 {
14779 if (aMachineState != MachineState_Saved)
14780 {
14781 /*
14782 * delete the saved state file once the machine has finished
14783 * restoring from it (note that Console sets the state from
14784 * Restoring to Saved if the VM couldn't restore successfully,
14785 * to give the user an ability to fix an error and retry --
14786 * we keep the saved state file in this case)
14787 */
14788 deleteSavedState = true;
14789 }
14790 }
14791 else if ( oldMachineState == MachineState_Saved
14792 && ( aMachineState == MachineState_PoweredOff
14793 || aMachineState == MachineState_Aborted
14794 || aMachineState == MachineState_Teleported
14795 )
14796 )
14797 {
14798 /*
14799 * delete the saved state after SessionMachine::ForgetSavedState() is called
14800 * or if the VM process (owning a direct VM session) crashed while the
14801 * VM was Saved
14802 */
14803
14804 /// @todo (dmik)
14805 // Not sure that deleting the saved state file just because of the
14806 // client death before it attempted to restore the VM is a good
14807 // thing. But when it crashes we need to go to the Aborted state
14808 // which cannot have the saved state file associated... The only
14809 // way to fix this is to make the Aborted condition not a VM state
14810 // but a bool flag: i.e., when a crash occurs, set it to true and
14811 // change the state to PoweredOff or Saved depending on the
14812 // saved state presence.
14813
14814 deleteSavedState = true;
14815 mData->mCurrentStateModified = TRUE;
14816 stsFlags |= SaveSTS_CurStateModified;
14817 }
14818
14819 if ( aMachineState == MachineState_Starting
14820 || aMachineState == MachineState_Restoring
14821 || aMachineState == MachineState_TeleportingIn
14822 )
14823 {
14824 /* set the current state modified flag to indicate that the current
14825 * state is no more identical to the state in the
14826 * current snapshot */
14827 if (!mData->mCurrentSnapshot.isNull())
14828 {
14829 mData->mCurrentStateModified = TRUE;
14830 stsFlags |= SaveSTS_CurStateModified;
14831 }
14832 }
14833
14834 if (deleteSavedState)
14835 {
14836 if (mRemoveSavedState)
14837 {
14838 Assert(!mSSData->strStateFilePath.isEmpty());
14839
14840 // it is safe to delete the saved state file if ...
14841 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14842 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14843 // ... none of the snapshots share the saved state file
14844 )
14845 RTFileDelete(mSSData->strStateFilePath.c_str());
14846 }
14847
14848 mSSData->strStateFilePath.setNull();
14849 stsFlags |= SaveSTS_StateFilePath;
14850 }
14851
14852 /* redirect to the underlying peer machine */
14853 mPeer->i_setMachineState(aMachineState);
14854
14855 if ( oldMachineState != MachineState_RestoringSnapshot
14856 && ( aMachineState == MachineState_PoweredOff
14857 || aMachineState == MachineState_Teleported
14858 || aMachineState == MachineState_Aborted
14859 || aMachineState == MachineState_Saved))
14860 {
14861 /* the machine has stopped execution
14862 * (or the saved state file was adopted) */
14863 stsFlags |= SaveSTS_StateTimeStamp;
14864 }
14865
14866 if ( ( oldMachineState == MachineState_PoweredOff
14867 || oldMachineState == MachineState_Aborted
14868 || oldMachineState == MachineState_Teleported
14869 )
14870 && aMachineState == MachineState_Saved)
14871 {
14872 /* the saved state file was adopted */
14873 Assert(!mSSData->strStateFilePath.isEmpty());
14874 stsFlags |= SaveSTS_StateFilePath;
14875 }
14876
14877#ifdef VBOX_WITH_GUEST_PROPS
14878 if ( aMachineState == MachineState_PoweredOff
14879 || aMachineState == MachineState_Aborted
14880 || aMachineState == MachineState_Teleported)
14881 {
14882 /* Make sure any transient guest properties get removed from the
14883 * property store on shutdown. */
14884 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14885
14886 /* remove it from the settings representation */
14887 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14888 for (settings::GuestPropertiesList::iterator
14889 it = llGuestProperties.begin();
14890 it != llGuestProperties.end();
14891 /*nothing*/)
14892 {
14893 const settings::GuestProperty &prop = *it;
14894 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14895 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14896 {
14897 it = llGuestProperties.erase(it);
14898 fNeedsSaving = true;
14899 }
14900 else
14901 {
14902 ++it;
14903 }
14904 }
14905
14906 /* Additionally remove it from the HWData representation. Required to
14907 * keep everything in sync, as this is what the API keeps using. */
14908 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14909 for (HWData::GuestPropertyMap::iterator
14910 it = llHWGuestProperties.begin();
14911 it != llHWGuestProperties.end();
14912 /*nothing*/)
14913 {
14914 uint32_t fFlags = it->second.mFlags;
14915 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14916 {
14917 /* iterator where we need to continue after the erase call
14918 * (C++03 is a fact still, and it doesn't return the iterator
14919 * which would allow continuing) */
14920 HWData::GuestPropertyMap::iterator it2 = it;
14921 ++it2;
14922 llHWGuestProperties.erase(it);
14923 it = it2;
14924 fNeedsSaving = true;
14925 }
14926 else
14927 {
14928 ++it;
14929 }
14930 }
14931
14932 if (fNeedsSaving)
14933 {
14934 mData->mCurrentStateModified = TRUE;
14935 stsFlags |= SaveSTS_CurStateModified;
14936 }
14937 }
14938#endif /* VBOX_WITH_GUEST_PROPS */
14939
14940 rc = i_saveStateSettings(stsFlags);
14941
14942 if ( ( oldMachineState != MachineState_PoweredOff
14943 && oldMachineState != MachineState_Aborted
14944 && oldMachineState != MachineState_Teleported
14945 )
14946 && ( aMachineState == MachineState_PoweredOff
14947 || aMachineState == MachineState_Aborted
14948 || aMachineState == MachineState_Teleported
14949 )
14950 )
14951 {
14952 /* we've been shut down for any reason */
14953 /* no special action so far */
14954 }
14955
14956 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14957 LogFlowThisFuncLeave();
14958 return rc;
14959}
14960
14961/**
14962 * Sends the current machine state value to the VM process.
14963 *
14964 * @note Locks this object for reading, then calls a client process.
14965 */
14966HRESULT SessionMachine::i_updateMachineStateOnClient()
14967{
14968 AutoCaller autoCaller(this);
14969 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14970
14971 ComPtr<IInternalSessionControl> directControl;
14972 {
14973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14974 AssertReturn(!!mData, E_FAIL);
14975 if (mData->mSession.mLockType == LockType_VM)
14976 directControl = mData->mSession.mDirectControl;
14977
14978 /* directControl may be already set to NULL here in #OnSessionEnd()
14979 * called too early by the direct session process while there is still
14980 * some operation (like deleting the snapshot) in progress. The client
14981 * process in this case is waiting inside Session::close() for the
14982 * "end session" process object to complete, while #uninit() called by
14983 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14984 * operation to complete. For now, we accept this inconsistent behavior
14985 * and simply do nothing here. */
14986
14987 if (mData->mSession.mState == SessionState_Unlocking)
14988 return S_OK;
14989 }
14990
14991 /* ignore notifications sent after #OnSessionEnd() is called */
14992 if (!directControl)
14993 return S_OK;
14994
14995 return directControl->UpdateMachineState(mData->mMachineState);
14996}
14997
14998
14999/*static*/
15000HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15001{
15002 va_list args;
15003 va_start(args, pcszMsg);
15004 HRESULT rc = setErrorInternal(aResultCode,
15005 getStaticClassIID(),
15006 getStaticComponentName(),
15007 Utf8Str(pcszMsg, args),
15008 false /* aWarning */,
15009 true /* aLogIt */);
15010 va_end(args);
15011 return rc;
15012}
15013
15014
15015HRESULT Machine::updateState(MachineState_T aState)
15016{
15017 NOREF(aState);
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15022{
15023 NOREF(aProgress);
15024 ReturnComNotImplemented();
15025}
15026
15027HRESULT Machine::endPowerUp(LONG aResult)
15028{
15029 NOREF(aResult);
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15034{
15035 NOREF(aProgress);
15036 ReturnComNotImplemented();
15037}
15038
15039HRESULT Machine::endPoweringDown(LONG aResult,
15040 const com::Utf8Str &aErrMsg)
15041{
15042 NOREF(aResult);
15043 NOREF(aErrMsg);
15044 ReturnComNotImplemented();
15045}
15046
15047HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15048 BOOL *aMatched,
15049 ULONG *aMaskedInterfaces)
15050{
15051 NOREF(aDevice);
15052 NOREF(aMatched);
15053 NOREF(aMaskedInterfaces);
15054 ReturnComNotImplemented();
15055
15056}
15057
15058HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15059{
15060 NOREF(aId); NOREF(aCaptureFilename);
15061 ReturnComNotImplemented();
15062}
15063
15064HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15065 BOOL aDone)
15066{
15067 NOREF(aId);
15068 NOREF(aDone);
15069 ReturnComNotImplemented();
15070}
15071
15072HRESULT Machine::autoCaptureUSBDevices()
15073{
15074 ReturnComNotImplemented();
15075}
15076
15077HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15078{
15079 NOREF(aDone);
15080 ReturnComNotImplemented();
15081}
15082
15083HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15084 ComPtr<IProgress> &aProgress)
15085{
15086 NOREF(aSession);
15087 NOREF(aProgress);
15088 ReturnComNotImplemented();
15089}
15090
15091HRESULT Machine::finishOnlineMergeMedium()
15092{
15093 ReturnComNotImplemented();
15094}
15095
15096HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15097 std::vector<com::Utf8Str> &aValues,
15098 std::vector<LONG64> &aTimestamps,
15099 std::vector<com::Utf8Str> &aFlags)
15100{
15101 NOREF(aNames);
15102 NOREF(aValues);
15103 NOREF(aTimestamps);
15104 NOREF(aFlags);
15105 ReturnComNotImplemented();
15106}
15107
15108HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15109 const com::Utf8Str &aValue,
15110 LONG64 aTimestamp,
15111 const com::Utf8Str &aFlags)
15112{
15113 NOREF(aName);
15114 NOREF(aValue);
15115 NOREF(aTimestamp);
15116 NOREF(aFlags);
15117 ReturnComNotImplemented();
15118}
15119
15120HRESULT Machine::lockMedia()
15121{
15122 ReturnComNotImplemented();
15123}
15124
15125HRESULT Machine::unlockMedia()
15126{
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15131 ComPtr<IMediumAttachment> &aNewAttachment)
15132{
15133 NOREF(aAttachment);
15134 NOREF(aNewAttachment);
15135 ReturnComNotImplemented();
15136}
15137
15138HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15139 ULONG aCpuUser,
15140 ULONG aCpuKernel,
15141 ULONG aCpuIdle,
15142 ULONG aMemTotal,
15143 ULONG aMemFree,
15144 ULONG aMemBalloon,
15145 ULONG aMemShared,
15146 ULONG aMemCache,
15147 ULONG aPagedTotal,
15148 ULONG aMemAllocTotal,
15149 ULONG aMemFreeTotal,
15150 ULONG aMemBalloonTotal,
15151 ULONG aMemSharedTotal,
15152 ULONG aVmNetRx,
15153 ULONG aVmNetTx)
15154{
15155 NOREF(aValidStats);
15156 NOREF(aCpuUser);
15157 NOREF(aCpuKernel);
15158 NOREF(aCpuIdle);
15159 NOREF(aMemTotal);
15160 NOREF(aMemFree);
15161 NOREF(aMemBalloon);
15162 NOREF(aMemShared);
15163 NOREF(aMemCache);
15164 NOREF(aPagedTotal);
15165 NOREF(aMemAllocTotal);
15166 NOREF(aMemFreeTotal);
15167 NOREF(aMemBalloonTotal);
15168 NOREF(aMemSharedTotal);
15169 NOREF(aVmNetRx);
15170 NOREF(aVmNetTx);
15171 ReturnComNotImplemented();
15172}
15173
15174HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15175 com::Utf8Str &aResult)
15176{
15177 NOREF(aAuthParams);
15178 NOREF(aResult);
15179 ReturnComNotImplemented();
15180}
15181
15182HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15183{
15184 NOREF(aFlags);
15185 ReturnComNotImplemented();
15186}
15187
15188/* This isn't handled entirely by the wrapper generator yet. */
15189#ifdef VBOX_WITH_XPCOM
15190NS_DECL_CLASSINFO(SessionMachine)
15191NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15192
15193NS_DECL_CLASSINFO(SnapshotMachine)
15194NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15195#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