VirtualBox

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

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

Main: Allow changing the passthrough flag when the VM is running if the storage controller supports hot plugging

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 529.1 KB
Line 
1/* $Id: MachineImpl.cpp 73589 2018-08-09 13:56:15Z 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.215389.xyz. 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 mHWVirtExUseNativeApi = false;
190#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
191 mPAEEnabled = true;
192#else
193 mPAEEnabled = false;
194#endif
195 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
196 mTripleFaultReset = false;
197 mAPIC = true;
198 mX2APIC = false;
199 mIBPBOnVMExit = false;
200 mIBPBOnVMEntry = false;
201 mSpecCtrl = false;
202 mSpecCtrlByHost = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param strOsType OS Type string (stored as is if aOsType is NULL).
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
285 * scheme (includes the UUID).
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 const Utf8Str &strOsType,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 if (llGroups.size())
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Let the OS type select 64-bit ness. */
348 mHWData->mLongMode = aOsType->i_is64Bit()
349 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
350
351 /* Let the OS type enable the X2APIC */
352 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
353 }
354 else if (!strOsType.isEmpty())
355 {
356 /* Store OS type */
357 mUserData->s.strOsType = strOsType;
358
359 /* No guest OS type object. Pick some plausible defaults which the
360 * host can handle. There's no way to know or validate anything. */
361 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 mHWData->mX2APIC = false;
363 }
364
365 /* Apply BIOS defaults */
366 mBIOSSettings->i_applyDefaults(aOsType);
367
368 /* Apply network adapters defaults */
369 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
370 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
371
372 /* Apply serial port defaults */
373 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
374 mSerialPorts[slot]->i_applyDefaults(aOsType);
375
376 /* Apply parallel port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
378 mParallelPorts[slot]->i_applyDefaults();
379
380 /* At this point the changing of the current state modification
381 * flag is allowed. */
382 i_allowStateModification();
383
384 /* commit all changes made during the initialization */
385 i_commit();
386 }
387
388 /* Confirm a successful initialization when it's the case */
389 if (SUCCEEDED(rc))
390 {
391 if (mData->mAccessible)
392 autoInitSpan.setSucceeded();
393 else
394 autoInitSpan.setLimited();
395 }
396
397 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
398 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
399 mData->mRegistered,
400 mData->mAccessible,
401 rc));
402
403 LogFlowThisFuncLeave();
404
405 return rc;
406}
407
408/**
409 * Initializes a new instance with data from machine XML (formerly Init_Registered).
410 * Gets called in two modes:
411 *
412 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
413 * UUID is specified and we mark the machine as "registered";
414 *
415 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
416 * and the machine remains unregistered until RegisterMachine() is called.
417 *
418 * @param aParent Associated parent object
419 * @param strConfigFile Local file system path to the VM settings file (can
420 * be relative to the VirtualBox config directory).
421 * @param aId UUID of the machine or NULL (see above).
422 *
423 * @return Success indicator. if not S_OK, the machine object is invalid
424 */
425HRESULT Machine::initFromSettings(VirtualBox *aParent,
426 const Utf8Str &strConfigFile,
427 const Guid *aId)
428{
429 LogFlowThisFuncEnter();
430 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
431
432 /* Enclose the state transition NotReady->InInit->Ready */
433 AutoInitSpan autoInitSpan(this);
434 AssertReturn(autoInitSpan.isOk(), E_FAIL);
435
436 HRESULT rc = initImpl(aParent, strConfigFile);
437 if (FAILED(rc)) return rc;
438
439 if (aId)
440 {
441 // loading a registered VM:
442 unconst(mData->mUuid) = *aId;
443 mData->mRegistered = TRUE;
444 // now load the settings from XML:
445 rc = i_registeredInit();
446 // this calls initDataAndChildObjects() and loadSettings()
447 }
448 else
449 {
450 // opening an unregistered VM (VirtualBox::OpenMachine()):
451 rc = initDataAndChildObjects();
452
453 if (SUCCEEDED(rc))
454 {
455 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
456 mData->mAccessible = TRUE;
457
458 try
459 {
460 // load and parse machine XML; this will throw on XML or logic errors
461 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
462
463 // reject VM UUID duplicates, they can happen if someone
464 // tries to register an already known VM config again
465 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
466 true /* fPermitInaccessible */,
467 false /* aDoSetError */,
468 NULL) != VBOX_E_OBJECT_NOT_FOUND)
469 {
470 throw setError(E_FAIL,
471 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
472 mData->m_strConfigFile.c_str());
473 }
474
475 // use UUID from machine config
476 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
477
478 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
479 NULL /* puuidRegistry */);
480 if (FAILED(rc)) throw rc;
481
482 /* At this point the changing of the current state modification
483 * flag is allowed. */
484 i_allowStateModification();
485
486 i_commit();
487 }
488 catch (HRESULT err)
489 {
490 /* we assume that error info is set by the thrower */
491 rc = err;
492 }
493 catch (...)
494 {
495 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
496 }
497 }
498 }
499
500 /* Confirm a successful initialization when it's the case */
501 if (SUCCEEDED(rc))
502 {
503 if (mData->mAccessible)
504 autoInitSpan.setSucceeded();
505 else
506 {
507 autoInitSpan.setLimited();
508
509 // uninit media from this machine's media registry, or else
510 // reloading the settings will fail
511 mParent->i_unregisterMachineMedia(i_getId());
512 }
513 }
514
515 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
516 "rc=%08X\n",
517 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
518 mData->mRegistered, mData->mAccessible, rc));
519
520 LogFlowThisFuncLeave();
521
522 return rc;
523}
524
525/**
526 * Initializes a new instance from a machine config that is already in memory
527 * (import OVF case). Since we are importing, the UUID in the machine
528 * config is ignored and we always generate a fresh one.
529 *
530 * @param aParent Associated parent object.
531 * @param strName Name for the new machine; this overrides what is specified in config.
532 * @param strSettingsFilename File name of .vbox file.
533 * @param config Machine configuration loaded and parsed from XML.
534 *
535 * @return Success indicator. if not S_OK, the machine object is invalid
536 */
537HRESULT Machine::init(VirtualBox *aParent,
538 const Utf8Str &strName,
539 const Utf8Str &strSettingsFilename,
540 const settings::MachineConfigFile &config)
541{
542 LogFlowThisFuncEnter();
543
544 /* Enclose the state transition NotReady->InInit->Ready */
545 AutoInitSpan autoInitSpan(this);
546 AssertReturn(autoInitSpan.isOk(), E_FAIL);
547
548 HRESULT rc = initImpl(aParent, strSettingsFilename);
549 if (FAILED(rc)) return rc;
550
551 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
552 if (FAILED(rc)) return rc;
553
554 rc = initDataAndChildObjects();
555
556 if (SUCCEEDED(rc))
557 {
558 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
559 mData->mAccessible = TRUE;
560
561 // create empty machine config for instance data
562 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
563
564 // generate fresh UUID, ignore machine config
565 unconst(mData->mUuid).create();
566
567 rc = i_loadMachineDataFromSettings(config,
568 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
569
570 // override VM name as well, it may be different
571 mUserData->s.strName = strName;
572
573 if (SUCCEEDED(rc))
574 {
575 /* At this point the changing of the current state modification
576 * flag is allowed. */
577 i_allowStateModification();
578
579 /* commit all changes made during the initialization */
580 i_commit();
581 }
582 }
583
584 /* Confirm a successful initialization when it's the case */
585 if (SUCCEEDED(rc))
586 {
587 if (mData->mAccessible)
588 autoInitSpan.setSucceeded();
589 else
590 {
591 /* Ignore all errors from unregistering, they would destroy
592- * the more interesting error information we already have,
593- * pinpointing the issue with the VM config. */
594 ErrorInfoKeeper eik;
595
596 autoInitSpan.setLimited();
597
598 // uninit media from this machine's media registry, or else
599 // reloading the settings will fail
600 mParent->i_unregisterMachineMedia(i_getId());
601 }
602 }
603
604 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
605 "rc=%08X\n",
606 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
607 mData->mRegistered, mData->mAccessible, rc));
608
609 LogFlowThisFuncLeave();
610
611 return rc;
612}
613
614/**
615 * Shared code between the various init() implementations.
616 * @param aParent The VirtualBox object.
617 * @param strConfigFile Settings file.
618 * @return
619 */
620HRESULT Machine::initImpl(VirtualBox *aParent,
621 const Utf8Str &strConfigFile)
622{
623 LogFlowThisFuncEnter();
624
625 AssertReturn(aParent, E_INVALIDARG);
626 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
627
628 HRESULT rc = S_OK;
629
630 /* share the parent weakly */
631 unconst(mParent) = aParent;
632
633 /* allocate the essential machine data structure (the rest will be
634 * allocated later by initDataAndChildObjects() */
635 mData.allocate();
636
637 /* memorize the config file name (as provided) */
638 mData->m_strConfigFile = strConfigFile;
639
640 /* get the full file name */
641 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
642 if (RT_FAILURE(vrc1))
643 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
644 tr("Invalid machine settings file name '%s' (%Rrc)"),
645 strConfigFile.c_str(),
646 vrc1);
647
648 LogFlowThisFuncLeave();
649
650 return rc;
651}
652
653/**
654 * Tries to create a machine settings file in the path stored in the machine
655 * instance data. Used when a new machine is created to fail gracefully if
656 * the settings file could not be written (e.g. because machine dir is read-only).
657 * @return
658 */
659HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
660{
661 HRESULT rc = S_OK;
662
663 // when we create a new machine, we must be able to create the settings file
664 RTFILE f = NIL_RTFILE;
665 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
666 if ( RT_SUCCESS(vrc)
667 || vrc == VERR_SHARING_VIOLATION
668 )
669 {
670 if (RT_SUCCESS(vrc))
671 RTFileClose(f);
672 if (!fForceOverwrite)
673 rc = setError(VBOX_E_FILE_ERROR,
674 tr("Machine settings file '%s' already exists"),
675 mData->m_strConfigFileFull.c_str());
676 else
677 {
678 /* try to delete the config file, as otherwise the creation
679 * of a new settings file will fail. */
680 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
681 if (RT_FAILURE(vrc2))
682 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
683 tr("Could not delete the existing settings file '%s' (%Rrc)"),
684 mData->m_strConfigFileFull.c_str(), vrc2);
685 }
686 }
687 else if ( vrc != VERR_FILE_NOT_FOUND
688 && vrc != VERR_PATH_NOT_FOUND
689 )
690 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
691 tr("Invalid machine settings file name '%s' (%Rrc)"),
692 mData->m_strConfigFileFull.c_str(),
693 vrc);
694 return rc;
695}
696
697/**
698 * Initializes the registered machine by loading the settings file.
699 * This method is separated from #init() in order to make it possible to
700 * retry the operation after VirtualBox startup instead of refusing to
701 * startup the whole VirtualBox server in case if the settings file of some
702 * registered VM is invalid or inaccessible.
703 *
704 * @note Must be always called from this object's write lock
705 * (unless called from #init() that doesn't need any locking).
706 * @note Locks the mUSBController method for writing.
707 * @note Subclasses must not call this method.
708 */
709HRESULT Machine::i_registeredInit()
710{
711 AssertReturn(!i_isSessionMachine(), E_FAIL);
712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
713 AssertReturn(mData->mUuid.isValid(), E_FAIL);
714 AssertReturn(!mData->mAccessible, E_FAIL);
715
716 HRESULT rc = initDataAndChildObjects();
717
718 if (SUCCEEDED(rc))
719 {
720 /* Temporarily reset the registered flag in order to let setters
721 * potentially called from loadSettings() succeed (isMutable() used in
722 * all setters will return FALSE for a Machine instance if mRegistered
723 * is TRUE). */
724 mData->mRegistered = FALSE;
725
726 try
727 {
728 // load and parse machine XML; this will throw on XML or logic errors
729 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
730
731 if (mData->mUuid != mData->pMachineConfigFile->uuid)
732 throw setError(E_FAIL,
733 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
734 mData->pMachineConfigFile->uuid.raw(),
735 mData->m_strConfigFileFull.c_str(),
736 mData->mUuid.toString().c_str(),
737 mParent->i_settingsFilePath().c_str());
738
739 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
740 NULL /* const Guid *puuidRegistry */);
741 if (FAILED(rc)) throw rc;
742 }
743 catch (HRESULT err)
744 {
745 /* we assume that error info is set by the thrower */
746 rc = err;
747 }
748 catch (...)
749 {
750 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
751 }
752
753 /* Restore the registered flag (even on failure) */
754 mData->mRegistered = TRUE;
755 }
756
757 if (SUCCEEDED(rc))
758 {
759 /* Set mAccessible to TRUE only if we successfully locked and loaded
760 * the settings file */
761 mData->mAccessible = TRUE;
762
763 /* commit all changes made during loading the settings file */
764 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
765 /// @todo r=klaus for some reason the settings loading logic backs up
766 // the settings, and therefore a commit is needed. Should probably be changed.
767 }
768 else
769 {
770 /* If the machine is registered, then, instead of returning a
771 * failure, we mark it as inaccessible and set the result to
772 * success to give it a try later */
773
774 /* fetch the current error info */
775 mData->mAccessError = com::ErrorInfo();
776 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
777
778 /* rollback all changes */
779 i_rollback(false /* aNotify */);
780
781 // uninit media from this machine's media registry, or else
782 // reloading the settings will fail
783 mParent->i_unregisterMachineMedia(i_getId());
784
785 /* uninitialize the common part to make sure all data is reset to
786 * default (null) values */
787 uninitDataAndChildObjects();
788
789 rc = S_OK;
790 }
791
792 return rc;
793}
794
795/**
796 * Uninitializes the instance.
797 * Called either from FinalRelease() or by the parent when it gets destroyed.
798 *
799 * @note The caller of this method must make sure that this object
800 * a) doesn't have active callers on the current thread and b) is not locked
801 * by the current thread; otherwise uninit() will hang either a) due to
802 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
803 * a dead-lock caused by this thread waiting for all callers on the other
804 * threads are done but preventing them from doing so by holding a lock.
805 */
806void Machine::uninit()
807{
808 LogFlowThisFuncEnter();
809
810 Assert(!isWriteLockOnCurrentThread());
811
812 Assert(!uRegistryNeedsSaving);
813 if (uRegistryNeedsSaving)
814 {
815 AutoCaller autoCaller(this);
816 if (SUCCEEDED(autoCaller.rc()))
817 {
818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
819 i_saveSettings(NULL, Machine::SaveS_Force);
820 }
821 }
822
823 /* Enclose the state transition Ready->InUninit->NotReady */
824 AutoUninitSpan autoUninitSpan(this);
825 if (autoUninitSpan.uninitDone())
826 return;
827
828 Assert(!i_isSnapshotMachine());
829 Assert(!i_isSessionMachine());
830 Assert(!!mData);
831
832 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
833 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
834
835 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
836
837 if (!mData->mSession.mMachine.isNull())
838 {
839 /* Theoretically, this can only happen if the VirtualBox server has been
840 * terminated while there were clients running that owned open direct
841 * sessions. Since in this case we are definitely called by
842 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
843 * won't happen on the client watcher thread (because it has a
844 * VirtualBox caller for the duration of the
845 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
846 * cannot happen until the VirtualBox caller is released). This is
847 * important, because SessionMachine::uninit() cannot correctly operate
848 * after we return from this method (it expects the Machine instance is
849 * still valid). We'll call it ourselves below.
850 */
851 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
852 (SessionMachine*)mData->mSession.mMachine));
853
854 if (Global::IsOnlineOrTransient(mData->mMachineState))
855 {
856 Log1WarningThisFunc(("Setting state to Aborted!\n"));
857 /* set machine state using SessionMachine reimplementation */
858 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
859 }
860
861 /*
862 * Uninitialize SessionMachine using public uninit() to indicate
863 * an unexpected uninitialization.
864 */
865 mData->mSession.mMachine->uninit();
866 /* SessionMachine::uninit() must set mSession.mMachine to null */
867 Assert(mData->mSession.mMachine.isNull());
868 }
869
870 // uninit media from this machine's media registry, if they're still there
871 Guid uuidMachine(i_getId());
872
873 /* the lock is no more necessary (SessionMachine is uninitialized) */
874 alock.release();
875
876 /* XXX This will fail with
877 * "cannot be closed because it is still attached to 1 virtual machines"
878 * because at this point we did not call uninitDataAndChildObjects() yet
879 * and therefore also removeBackReference() for all these mediums was not called! */
880
881 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
882 mParent->i_unregisterMachineMedia(uuidMachine);
883
884 // has machine been modified?
885 if (mData->flModifications)
886 {
887 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
888 i_rollback(false /* aNotify */);
889 }
890
891 if (mData->mAccessible)
892 uninitDataAndChildObjects();
893
894 /* free the essential data structure last */
895 mData.free();
896
897 LogFlowThisFuncLeave();
898}
899
900// Wrapped IMachine properties
901/////////////////////////////////////////////////////////////////////////////
902HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
903{
904 /* mParent is constant during life time, no need to lock */
905 ComObjPtr<VirtualBox> pVirtualBox(mParent);
906 aParent = pVirtualBox;
907
908 return S_OK;
909}
910
911
912HRESULT Machine::getAccessible(BOOL *aAccessible)
913{
914 /* In some cases (medium registry related), it is necessary to be able to
915 * go through the list of all machines. Happens when an inaccessible VM
916 * has a sensible medium registry. */
917 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
919
920 HRESULT rc = S_OK;
921
922 if (!mData->mAccessible)
923 {
924 /* try to initialize the VM once more if not accessible */
925
926 AutoReinitSpan autoReinitSpan(this);
927 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
928
929#ifdef DEBUG
930 LogFlowThisFunc(("Dumping media backreferences\n"));
931 mParent->i_dumpAllBackRefs();
932#endif
933
934 if (mData->pMachineConfigFile)
935 {
936 // reset the XML file to force loadSettings() (called from i_registeredInit())
937 // to parse it again; the file might have changed
938 delete mData->pMachineConfigFile;
939 mData->pMachineConfigFile = NULL;
940 }
941
942 rc = i_registeredInit();
943
944 if (SUCCEEDED(rc) && mData->mAccessible)
945 {
946 autoReinitSpan.setSucceeded();
947
948 /* make sure interesting parties will notice the accessibility
949 * state change */
950 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
951 mParent->i_onMachineDataChange(mData->mUuid);
952 }
953 }
954
955 if (SUCCEEDED(rc))
956 *aAccessible = mData->mAccessible;
957
958 LogFlowThisFuncLeave();
959
960 return rc;
961}
962
963HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
964{
965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
966
967 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
968 {
969 /* return shortly */
970 aAccessError = NULL;
971 return S_OK;
972 }
973
974 HRESULT rc = S_OK;
975
976 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
977 rc = errorInfo.createObject();
978 if (SUCCEEDED(rc))
979 {
980 errorInfo->init(mData->mAccessError.getResultCode(),
981 mData->mAccessError.getInterfaceID().ref(),
982 Utf8Str(mData->mAccessError.getComponent()).c_str(),
983 Utf8Str(mData->mAccessError.getText()));
984 aAccessError = errorInfo;
985 }
986
987 return rc;
988}
989
990HRESULT Machine::getName(com::Utf8Str &aName)
991{
992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
993
994 aName = mUserData->s.strName;
995
996 return S_OK;
997}
998
999HRESULT Machine::setName(const com::Utf8Str &aName)
1000{
1001 // prohibit setting a UUID only as the machine name, or else it can
1002 // never be found by findMachine()
1003 Guid test(aName);
1004
1005 if (test.isValid())
1006 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1007
1008 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 HRESULT rc = i_checkStateDependency(MutableStateDep);
1011 if (FAILED(rc)) return rc;
1012
1013 i_setModified(IsModified_MachineData);
1014 mUserData.backup();
1015 mUserData->s.strName = aName;
1016
1017 return S_OK;
1018}
1019
1020HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1021{
1022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1023
1024 aDescription = mUserData->s.strDescription;
1025
1026 return S_OK;
1027}
1028
1029HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1030{
1031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1032
1033 // this can be done in principle in any state as it doesn't affect the VM
1034 // significantly, but play safe by not messing around while complex
1035 // activities are going on
1036 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1037 if (FAILED(rc)) return rc;
1038
1039 i_setModified(IsModified_MachineData);
1040 mUserData.backup();
1041 mUserData->s.strDescription = aDescription;
1042
1043 return S_OK;
1044}
1045
1046HRESULT Machine::getId(com::Guid &aId)
1047{
1048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1049
1050 aId = mData->mUuid;
1051
1052 return S_OK;
1053}
1054
1055HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1056{
1057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1058 aGroups.resize(mUserData->s.llGroups.size());
1059 size_t i = 0;
1060 for (StringsList::const_iterator
1061 it = mUserData->s.llGroups.begin();
1062 it != mUserData->s.llGroups.end();
1063 ++it, ++i)
1064 aGroups[i] = (*it);
1065
1066 return S_OK;
1067}
1068
1069HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1070{
1071 StringsList llGroups;
1072 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1073 if (FAILED(rc))
1074 return rc;
1075
1076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1077
1078 rc = i_checkStateDependency(MutableOrSavedStateDep);
1079 if (FAILED(rc)) return rc;
1080
1081 i_setModified(IsModified_MachineData);
1082 mUserData.backup();
1083 mUserData->s.llGroups = llGroups;
1084
1085 return S_OK;
1086}
1087
1088HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1089{
1090 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1091
1092 aOSTypeId = mUserData->s.strOsType;
1093
1094 return S_OK;
1095}
1096
1097HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1098{
1099 /* look up the object by Id to check it is valid */
1100 ComObjPtr<GuestOSType> pGuestOSType;
1101 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1102
1103 /* when setting, always use the "etalon" value for consistency -- lookup
1104 * by ID is case-insensitive and the input value may have different case */
1105 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1106
1107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1108
1109 HRESULT rc = i_checkStateDependency(MutableStateDep);
1110 if (FAILED(rc)) return rc;
1111
1112 i_setModified(IsModified_MachineData);
1113 mUserData.backup();
1114 mUserData->s.strOsType = osTypeId;
1115
1116 return S_OK;
1117}
1118
1119HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1120{
1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1122
1123 *aFirmwareType = mHWData->mFirmwareType;
1124
1125 return S_OK;
1126}
1127
1128HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1129{
1130 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1131
1132 HRESULT rc = i_checkStateDependency(MutableStateDep);
1133 if (FAILED(rc)) return rc;
1134
1135 i_setModified(IsModified_MachineData);
1136 mHWData.backup();
1137 mHWData->mFirmwareType = aFirmwareType;
1138
1139 return S_OK;
1140}
1141
1142HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1143{
1144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1145
1146 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1147
1148 return S_OK;
1149}
1150
1151HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1152{
1153 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1154
1155 HRESULT rc = i_checkStateDependency(MutableStateDep);
1156 if (FAILED(rc)) return rc;
1157
1158 i_setModified(IsModified_MachineData);
1159 mHWData.backup();
1160 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1161
1162 return S_OK;
1163}
1164
1165HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1166{
1167 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1168
1169 *aPointingHIDType = mHWData->mPointingHIDType;
1170
1171 return S_OK;
1172}
1173
1174HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1175{
1176 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1177
1178 HRESULT rc = i_checkStateDependency(MutableStateDep);
1179 if (FAILED(rc)) return rc;
1180
1181 i_setModified(IsModified_MachineData);
1182 mHWData.backup();
1183 mHWData->mPointingHIDType = aPointingHIDType;
1184
1185 return S_OK;
1186}
1187
1188HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1189{
1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1191
1192 *aChipsetType = mHWData->mChipsetType;
1193
1194 return S_OK;
1195}
1196
1197HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1198{
1199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1200
1201 HRESULT rc = i_checkStateDependency(MutableStateDep);
1202 if (FAILED(rc)) return rc;
1203
1204 if (aChipsetType != mHWData->mChipsetType)
1205 {
1206 i_setModified(IsModified_MachineData);
1207 mHWData.backup();
1208 mHWData->mChipsetType = aChipsetType;
1209
1210 // Resize network adapter array, to be finalized on commit/rollback.
1211 // We must not throw away entries yet, otherwise settings are lost
1212 // without a way to roll back.
1213 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1214 size_t oldCount = mNetworkAdapters.size();
1215 if (newCount > oldCount)
1216 {
1217 mNetworkAdapters.resize(newCount);
1218 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1219 {
1220 unconst(mNetworkAdapters[slot]).createObject();
1221 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1222 }
1223 }
1224 }
1225
1226 return S_OK;
1227}
1228
1229HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1230{
1231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1232
1233 aParavirtDebug = mHWData->mParavirtDebug;
1234 return S_OK;
1235}
1236
1237HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1238{
1239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 HRESULT rc = i_checkStateDependency(MutableStateDep);
1242 if (FAILED(rc)) return rc;
1243
1244 /** @todo Parse/validate options? */
1245 if (aParavirtDebug != mHWData->mParavirtDebug)
1246 {
1247 i_setModified(IsModified_MachineData);
1248 mHWData.backup();
1249 mHWData->mParavirtDebug = aParavirtDebug;
1250 }
1251
1252 return S_OK;
1253}
1254
1255HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1256{
1257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1258
1259 *aParavirtProvider = mHWData->mParavirtProvider;
1260
1261 return S_OK;
1262}
1263
1264HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1265{
1266 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1267
1268 HRESULT rc = i_checkStateDependency(MutableStateDep);
1269 if (FAILED(rc)) return rc;
1270
1271 if (aParavirtProvider != mHWData->mParavirtProvider)
1272 {
1273 i_setModified(IsModified_MachineData);
1274 mHWData.backup();
1275 mHWData->mParavirtProvider = aParavirtProvider;
1276 }
1277
1278 return S_OK;
1279}
1280
1281HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1282{
1283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1284
1285 *aParavirtProvider = mHWData->mParavirtProvider;
1286 switch (mHWData->mParavirtProvider)
1287 {
1288 case ParavirtProvider_None:
1289 case ParavirtProvider_HyperV:
1290 case ParavirtProvider_KVM:
1291 case ParavirtProvider_Minimal:
1292 break;
1293
1294 /* Resolve dynamic provider types to the effective types. */
1295 default:
1296 {
1297 ComObjPtr<GuestOSType> pGuestOSType;
1298 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1299 pGuestOSType);
1300 if (FAILED(hrc2) || pGuestOSType.isNull())
1301 {
1302 *aParavirtProvider = ParavirtProvider_None;
1303 break;
1304 }
1305
1306 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1307 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1308
1309 switch (mHWData->mParavirtProvider)
1310 {
1311 case ParavirtProvider_Legacy:
1312 {
1313 if (fOsXGuest)
1314 *aParavirtProvider = ParavirtProvider_Minimal;
1315 else
1316 *aParavirtProvider = ParavirtProvider_None;
1317 break;
1318 }
1319
1320 case ParavirtProvider_Default:
1321 {
1322 if (fOsXGuest)
1323 *aParavirtProvider = ParavirtProvider_Minimal;
1324 else if ( mUserData->s.strOsType == "Windows10"
1325 || mUserData->s.strOsType == "Windows10_64"
1326 || mUserData->s.strOsType == "Windows81"
1327 || mUserData->s.strOsType == "Windows81_64"
1328 || mUserData->s.strOsType == "Windows8"
1329 || mUserData->s.strOsType == "Windows8_64"
1330 || mUserData->s.strOsType == "Windows7"
1331 || mUserData->s.strOsType == "Windows7_64"
1332 || mUserData->s.strOsType == "WindowsVista"
1333 || mUserData->s.strOsType == "WindowsVista_64"
1334 || mUserData->s.strOsType == "Windows2012"
1335 || mUserData->s.strOsType == "Windows2012_64"
1336 || mUserData->s.strOsType == "Windows2008"
1337 || mUserData->s.strOsType == "Windows2008_64")
1338 {
1339 *aParavirtProvider = ParavirtProvider_HyperV;
1340 }
1341 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1342 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1343 || mUserData->s.strOsType == "Linux"
1344 || mUserData->s.strOsType == "Linux_64"
1345 || mUserData->s.strOsType == "ArchLinux"
1346 || mUserData->s.strOsType == "ArchLinux_64"
1347 || mUserData->s.strOsType == "Debian"
1348 || mUserData->s.strOsType == "Debian_64"
1349 || mUserData->s.strOsType == "Fedora"
1350 || mUserData->s.strOsType == "Fedora_64"
1351 || mUserData->s.strOsType == "Gentoo"
1352 || mUserData->s.strOsType == "Gentoo_64"
1353 || mUserData->s.strOsType == "Mandriva"
1354 || mUserData->s.strOsType == "Mandriva_64"
1355 || mUserData->s.strOsType == "OpenSUSE"
1356 || mUserData->s.strOsType == "OpenSUSE_64"
1357 || mUserData->s.strOsType == "Oracle"
1358 || mUserData->s.strOsType == "Oracle_64"
1359 || mUserData->s.strOsType == "RedHat"
1360 || mUserData->s.strOsType == "RedHat_64"
1361 || mUserData->s.strOsType == "Turbolinux"
1362 || mUserData->s.strOsType == "Turbolinux_64"
1363 || mUserData->s.strOsType == "Ubuntu"
1364 || mUserData->s.strOsType == "Ubuntu_64"
1365 || mUserData->s.strOsType == "Xandros"
1366 || mUserData->s.strOsType == "Xandros_64")
1367 {
1368 *aParavirtProvider = ParavirtProvider_KVM;
1369 }
1370 else
1371 *aParavirtProvider = ParavirtProvider_None;
1372 break;
1373 }
1374
1375 default: AssertFailedBreak(); /* Shut up MSC. */
1376 }
1377 break;
1378 }
1379 }
1380
1381 Assert( *aParavirtProvider == ParavirtProvider_None
1382 || *aParavirtProvider == ParavirtProvider_Minimal
1383 || *aParavirtProvider == ParavirtProvider_HyperV
1384 || *aParavirtProvider == ParavirtProvider_KVM);
1385 return S_OK;
1386}
1387
1388HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1389{
1390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1391
1392 aHardwareVersion = mHWData->mHWVersion;
1393
1394 return S_OK;
1395}
1396
1397HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1398{
1399 /* check known version */
1400 Utf8Str hwVersion = aHardwareVersion;
1401 if ( hwVersion.compare("1") != 0
1402 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1403 return setError(E_INVALIDARG,
1404 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1405
1406 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1407
1408 HRESULT rc = i_checkStateDependency(MutableStateDep);
1409 if (FAILED(rc)) return rc;
1410
1411 i_setModified(IsModified_MachineData);
1412 mHWData.backup();
1413 mHWData->mHWVersion = aHardwareVersion;
1414
1415 return S_OK;
1416}
1417
1418HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1419{
1420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1421
1422 if (!mHWData->mHardwareUUID.isZero())
1423 aHardwareUUID = mHWData->mHardwareUUID;
1424 else
1425 aHardwareUUID = mData->mUuid;
1426
1427 return S_OK;
1428}
1429
1430HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1431{
1432 if (!aHardwareUUID.isValid())
1433 return E_INVALIDARG;
1434
1435 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1436
1437 HRESULT rc = i_checkStateDependency(MutableStateDep);
1438 if (FAILED(rc)) return rc;
1439
1440 i_setModified(IsModified_MachineData);
1441 mHWData.backup();
1442 if (aHardwareUUID == mData->mUuid)
1443 mHWData->mHardwareUUID.clear();
1444 else
1445 mHWData->mHardwareUUID = aHardwareUUID;
1446
1447 return S_OK;
1448}
1449
1450HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1451{
1452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1453
1454 *aMemorySize = mHWData->mMemorySize;
1455
1456 return S_OK;
1457}
1458
1459HRESULT Machine::setMemorySize(ULONG aMemorySize)
1460{
1461 /* check RAM limits */
1462 if ( aMemorySize < MM_RAM_MIN_IN_MB
1463 || aMemorySize > MM_RAM_MAX_IN_MB
1464 )
1465 return setError(E_INVALIDARG,
1466 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1467 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1468
1469 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mMemorySize = aMemorySize;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUCount = mHWData->mCPUCount;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUCount(ULONG aCPUCount)
1491{
1492 /* check CPU limits */
1493 if ( aCPUCount < SchemaDefs::MinCPUCount
1494 || aCPUCount > SchemaDefs::MaxCPUCount
1495 )
1496 return setError(E_INVALIDARG,
1497 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1498 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1499
1500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1501
1502 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1503 if (mHWData->mCPUHotPlugEnabled)
1504 {
1505 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1506 {
1507 if (mHWData->mCPUAttached[idx])
1508 return setError(E_INVALIDARG,
1509 tr("There is still a CPU attached to socket %lu."
1510 "Detach the CPU before removing the socket"),
1511 aCPUCount, idx+1);
1512 }
1513 }
1514
1515 HRESULT rc = i_checkStateDependency(MutableStateDep);
1516 if (FAILED(rc)) return rc;
1517
1518 i_setModified(IsModified_MachineData);
1519 mHWData.backup();
1520 mHWData->mCPUCount = aCPUCount;
1521
1522 return S_OK;
1523}
1524
1525HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1526{
1527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1528
1529 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1530
1531 return S_OK;
1532}
1533
1534HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1535{
1536 HRESULT rc = S_OK;
1537
1538 /* check throttle limits */
1539 if ( aCPUExecutionCap < 1
1540 || aCPUExecutionCap > 100
1541 )
1542 return setError(E_INVALIDARG,
1543 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1544 aCPUExecutionCap, 1, 100);
1545
1546 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1547
1548 alock.release();
1549 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1550 alock.acquire();
1551 if (FAILED(rc)) return rc;
1552
1553 i_setModified(IsModified_MachineData);
1554 mHWData.backup();
1555 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1556
1557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1558 if (Global::IsOnline(mData->mMachineState))
1559 i_saveSettings(NULL);
1560
1561 return S_OK;
1562}
1563
1564HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1565{
1566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1567
1568 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1569
1570 return S_OK;
1571}
1572
1573HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1574{
1575 HRESULT rc = S_OK;
1576
1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1578
1579 rc = i_checkStateDependency(MutableStateDep);
1580 if (FAILED(rc)) return rc;
1581
1582 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1583 {
1584 if (aCPUHotPlugEnabled)
1585 {
1586 i_setModified(IsModified_MachineData);
1587 mHWData.backup();
1588
1589 /* Add the amount of CPUs currently attached */
1590 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1591 mHWData->mCPUAttached[i] = true;
1592 }
1593 else
1594 {
1595 /*
1596 * We can disable hotplug only if the amount of maximum CPUs is equal
1597 * to the amount of attached CPUs
1598 */
1599 unsigned cCpusAttached = 0;
1600 unsigned iHighestId = 0;
1601
1602 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1603 {
1604 if (mHWData->mCPUAttached[i])
1605 {
1606 cCpusAttached++;
1607 iHighestId = i;
1608 }
1609 }
1610
1611 if ( (cCpusAttached != mHWData->mCPUCount)
1612 || (iHighestId >= mHWData->mCPUCount))
1613 return setError(E_INVALIDARG,
1614 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1615
1616 i_setModified(IsModified_MachineData);
1617 mHWData.backup();
1618 }
1619 }
1620
1621 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1622
1623 return rc;
1624}
1625
1626HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1627{
1628 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1629
1630 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1631
1632 return S_OK;
1633}
1634
1635HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1636{
1637 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1638
1639 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1640 if (SUCCEEDED(hrc))
1641 {
1642 i_setModified(IsModified_MachineData);
1643 mHWData.backup();
1644 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1645 }
1646 return hrc;
1647}
1648
1649HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1650{
1651 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1652 aCPUProfile = mHWData->mCpuProfile;
1653 return S_OK;
1654}
1655
1656HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1657{
1658 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1659 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1660 if (SUCCEEDED(hrc))
1661 {
1662 i_setModified(IsModified_MachineData);
1663 mHWData.backup();
1664 /* Empty equals 'host'. */
1665 if (aCPUProfile.isNotEmpty())
1666 mHWData->mCpuProfile = aCPUProfile;
1667 else
1668 mHWData->mCpuProfile = "host";
1669 }
1670 return hrc;
1671}
1672
1673HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1674{
1675#ifdef VBOX_WITH_USB_CARDREADER
1676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1679
1680 return S_OK;
1681#else
1682 NOREF(aEmulatedUSBCardReaderEnabled);
1683 return E_NOTIMPL;
1684#endif
1685}
1686
1687HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1688{
1689#ifdef VBOX_WITH_USB_CARDREADER
1690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1691
1692 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1693 if (FAILED(rc)) return rc;
1694
1695 i_setModified(IsModified_MachineData);
1696 mHWData.backup();
1697 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1698
1699 return S_OK;
1700#else
1701 NOREF(aEmulatedUSBCardReaderEnabled);
1702 return E_NOTIMPL;
1703#endif
1704}
1705
1706HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1707{
1708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1709
1710 *aHPETEnabled = mHWData->mHPETEnabled;
1711
1712 return S_OK;
1713}
1714
1715HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1716{
1717 HRESULT rc = S_OK;
1718
1719 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1720
1721 rc = i_checkStateDependency(MutableStateDep);
1722 if (FAILED(rc)) return rc;
1723
1724 i_setModified(IsModified_MachineData);
1725 mHWData.backup();
1726
1727 mHWData->mHPETEnabled = aHPETEnabled;
1728
1729 return rc;
1730}
1731
1732HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1737 return S_OK;
1738}
1739
1740HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1741{
1742 HRESULT rc = S_OK;
1743
1744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 i_setModified(IsModified_MachineData);
1747 mHWData.backup();
1748 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1749
1750 alock.release();
1751 rc = i_onVideoCaptureChange();
1752 alock.acquire();
1753 if (FAILED(rc))
1754 {
1755 /*
1756 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1757 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1758 * determine if it should start or stop capturing. Therefore we need to manually
1759 * undo change.
1760 */
1761 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1762 return rc;
1763 }
1764
1765 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1766 if (Global::IsOnline(mData->mMachineState))
1767 i_saveSettings(NULL);
1768
1769 return rc;
1770}
1771
1772HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1773{
1774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1775 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1776 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1777 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1778 return S_OK;
1779}
1780
1781HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1782{
1783 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1784 bool fChanged = false;
1785
1786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1787
1788 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1789 {
1790 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1791 {
1792 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1793 fChanged = true;
1794 }
1795 }
1796 if (fChanged)
1797 {
1798 alock.release();
1799 HRESULT rc = i_onVideoCaptureChange();
1800 alock.acquire();
1801 if (FAILED(rc)) return rc;
1802 i_setModified(IsModified_MachineData);
1803
1804 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1805 if (Global::IsOnline(mData->mMachineState))
1806 i_saveSettings(NULL);
1807 }
1808
1809 return S_OK;
1810}
1811
1812HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1813{
1814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1815 if (mHWData->mVideoCaptureFile.isEmpty())
1816 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1817 else
1818 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1819 return S_OK;
1820}
1821
1822HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1823{
1824 Utf8Str strFile(aVideoCaptureFile);
1825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1826
1827 if ( Global::IsOnline(mData->mMachineState)
1828 && mHWData->mVideoCaptureEnabled)
1829 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1830
1831 if (!RTPathStartsWithRoot(strFile.c_str()))
1832 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1833
1834 if (!strFile.isEmpty())
1835 {
1836 Utf8Str defaultFile;
1837 i_getDefaultVideoCaptureFile(defaultFile);
1838 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1839 strFile.setNull();
1840 }
1841
1842 i_setModified(IsModified_MachineData);
1843 mHWData.backup();
1844 mHWData->mVideoCaptureFile = strFile;
1845
1846 return S_OK;
1847}
1848
1849HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1850{
1851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1852 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1853 return S_OK;
1854}
1855
1856HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1857{
1858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1859
1860 if ( Global::IsOnline(mData->mMachineState)
1861 && mHWData->mVideoCaptureEnabled)
1862 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1863
1864 i_setModified(IsModified_MachineData);
1865 mHWData.backup();
1866 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1867
1868 return S_OK;
1869}
1870
1871HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1872{
1873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1874 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1875 return S_OK;
1876}
1877
1878HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1879{
1880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1881
1882 if ( Global::IsOnline(mData->mMachineState)
1883 && mHWData->mVideoCaptureEnabled)
1884 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1885
1886 i_setModified(IsModified_MachineData);
1887 mHWData.backup();
1888 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1889
1890 return S_OK;
1891}
1892
1893HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1894{
1895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1896 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1897 return S_OK;
1898}
1899
1900HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1901{
1902 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1903
1904 if ( Global::IsOnline(mData->mMachineState)
1905 && mHWData->mVideoCaptureEnabled)
1906 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1907
1908 i_setModified(IsModified_MachineData);
1909 mHWData.backup();
1910 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1911
1912 return S_OK;
1913}
1914
1915HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1916{
1917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1918 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1919 return S_OK;
1920}
1921
1922HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1923{
1924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1925
1926 if ( Global::IsOnline(mData->mMachineState)
1927 && mHWData->mVideoCaptureEnabled)
1928 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1929
1930 i_setModified(IsModified_MachineData);
1931 mHWData.backup();
1932 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1933
1934 return S_OK;
1935}
1936
1937HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1938{
1939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1940 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1941 return S_OK;
1942}
1943
1944HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1945{
1946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1947
1948 if ( Global::IsOnline(mData->mMachineState)
1949 && mHWData->mVideoCaptureEnabled)
1950 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1951
1952 i_setModified(IsModified_MachineData);
1953 mHWData.backup();
1954 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1955
1956 return S_OK;
1957}
1958
1959HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1960{
1961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1962 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1963 return S_OK;
1964}
1965
1966HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1967{
1968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1969
1970 if ( Global::IsOnline(mData->mMachineState)
1971 && mHWData->mVideoCaptureEnabled)
1972 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1973
1974 i_setModified(IsModified_MachineData);
1975 mHWData.backup();
1976 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1977
1978 return S_OK;
1979}
1980
1981HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1982{
1983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1984
1985 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1986 return S_OK;
1987}
1988
1989HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
1990{
1991 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1992
1993 if ( Global::IsOnline(mData->mMachineState)
1994 && mHWData->mVideoCaptureEnabled)
1995 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1996
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
2000
2001 return S_OK;
2002}
2003
2004HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
2005{
2006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2007
2008 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
2009
2010 return S_OK;
2011}
2012
2013HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
2014{
2015 switch (aGraphicsControllerType)
2016 {
2017 case GraphicsControllerType_Null:
2018 case GraphicsControllerType_VBoxVGA:
2019#ifdef VBOX_WITH_VMSVGA
2020 case GraphicsControllerType_VMSVGA:
2021#endif
2022 break;
2023 default:
2024 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
2025 }
2026
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 HRESULT rc = i_checkStateDependency(MutableStateDep);
2030 if (FAILED(rc)) return rc;
2031
2032 i_setModified(IsModified_MachineData);
2033 mHWData.backup();
2034 mHWData->mGraphicsControllerType = aGraphicsControllerType;
2035
2036 return S_OK;
2037}
2038
2039HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
2040{
2041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2042
2043 *aVRAMSize = mHWData->mVRAMSize;
2044
2045 return S_OK;
2046}
2047
2048HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
2049{
2050 /* check VRAM limits */
2051 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
2052 return setError(E_INVALIDARG,
2053 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
2054 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
2055
2056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2057
2058 HRESULT rc = i_checkStateDependency(MutableStateDep);
2059 if (FAILED(rc)) return rc;
2060
2061 i_setModified(IsModified_MachineData);
2062 mHWData.backup();
2063 mHWData->mVRAMSize = aVRAMSize;
2064
2065 return S_OK;
2066}
2067
2068/** @todo this method should not be public */
2069HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2070{
2071 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2072
2073 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2074
2075 return S_OK;
2076}
2077
2078/**
2079 * Set the memory balloon size.
2080 *
2081 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2082 * we have to make sure that we never call IGuest from here.
2083 */
2084HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2085{
2086 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2087#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2088 /* check limits */
2089 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2090 return setError(E_INVALIDARG,
2091 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2092 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2093
2094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2095
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2099
2100 return S_OK;
2101#else
2102 NOREF(aMemoryBalloonSize);
2103 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2104#endif
2105}
2106
2107HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2108{
2109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2110
2111 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2112 return S_OK;
2113}
2114
2115HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2116{
2117#ifdef VBOX_WITH_PAGE_SHARING
2118 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2119
2120 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2121 i_setModified(IsModified_MachineData);
2122 mHWData.backup();
2123 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2124 return S_OK;
2125#else
2126 NOREF(aPageFusionEnabled);
2127 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2128#endif
2129}
2130
2131HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2132{
2133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2136
2137 return S_OK;
2138}
2139
2140HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2141{
2142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2143
2144 HRESULT rc = i_checkStateDependency(MutableStateDep);
2145 if (FAILED(rc)) return rc;
2146
2147 /** @todo check validity! */
2148
2149 i_setModified(IsModified_MachineData);
2150 mHWData.backup();
2151 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2152
2153 return S_OK;
2154}
2155
2156
2157HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2158{
2159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2160
2161 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2162
2163 return S_OK;
2164}
2165
2166HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2167{
2168 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2169
2170 HRESULT rc = i_checkStateDependency(MutableStateDep);
2171 if (FAILED(rc)) return rc;
2172
2173 /** @todo check validity! */
2174 i_setModified(IsModified_MachineData);
2175 mHWData.backup();
2176 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2177
2178 return S_OK;
2179}
2180
2181HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2182{
2183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2184
2185 *aMonitorCount = mHWData->mMonitorCount;
2186
2187 return S_OK;
2188}
2189
2190HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2191{
2192 /* make sure monitor count is a sensible number */
2193 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2194 return setError(E_INVALIDARG,
2195 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2196 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2197
2198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2199
2200 HRESULT rc = i_checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 i_setModified(IsModified_MachineData);
2204 mHWData.backup();
2205 mHWData->mMonitorCount = aMonitorCount;
2206
2207 return S_OK;
2208}
2209
2210HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2211{
2212 /* mBIOSSettings is constant during life time, no need to lock */
2213 aBIOSSettings = mBIOSSettings;
2214
2215 return S_OK;
2216}
2217
2218HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2219{
2220 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2221
2222 switch (aProperty)
2223 {
2224 case CPUPropertyType_PAE:
2225 *aValue = mHWData->mPAEEnabled;
2226 break;
2227
2228 case CPUPropertyType_LongMode:
2229 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2230 *aValue = TRUE;
2231 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2232 *aValue = FALSE;
2233#if HC_ARCH_BITS == 64
2234 else
2235 *aValue = TRUE;
2236#else
2237 else
2238 {
2239 *aValue = FALSE;
2240
2241 ComObjPtr<GuestOSType> pGuestOSType;
2242 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
2243 pGuestOSType);
2244 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
2245 {
2246 if (pGuestOSType->i_is64Bit())
2247 {
2248 ComObjPtr<Host> pHost = mParent->i_host();
2249 alock.release();
2250
2251 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2252 if (FAILED(hrc2))
2253 *aValue = FALSE;
2254 }
2255 }
2256 }
2257#endif
2258 break;
2259
2260 case CPUPropertyType_TripleFaultReset:
2261 *aValue = mHWData->mTripleFaultReset;
2262 break;
2263
2264 case CPUPropertyType_APIC:
2265 *aValue = mHWData->mAPIC;
2266 break;
2267
2268 case CPUPropertyType_X2APIC:
2269 *aValue = mHWData->mX2APIC;
2270 break;
2271
2272 case CPUPropertyType_IBPBOnVMExit:
2273 *aValue = mHWData->mIBPBOnVMExit;
2274 break;
2275
2276 case CPUPropertyType_IBPBOnVMEntry:
2277 *aValue = mHWData->mIBPBOnVMEntry;
2278 break;
2279
2280 case CPUPropertyType_SpecCtrl:
2281 *aValue = mHWData->mSpecCtrl;
2282 break;
2283
2284 case CPUPropertyType_SpecCtrlByHost:
2285 *aValue = mHWData->mSpecCtrlByHost;
2286 break;
2287
2288 case CPUPropertyType_HWVirt:
2289 *aValue = mHWData->mNestedHWVirt;
2290 break;
2291
2292 default:
2293 return E_INVALIDARG;
2294 }
2295 return S_OK;
2296}
2297
2298HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2299{
2300 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2301
2302 HRESULT rc = i_checkStateDependency(MutableStateDep);
2303 if (FAILED(rc)) return rc;
2304
2305 switch (aProperty)
2306 {
2307 case CPUPropertyType_PAE:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mPAEEnabled = !!aValue;
2311 break;
2312
2313 case CPUPropertyType_LongMode:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2317 break;
2318
2319 case CPUPropertyType_TripleFaultReset:
2320 i_setModified(IsModified_MachineData);
2321 mHWData.backup();
2322 mHWData->mTripleFaultReset = !!aValue;
2323 break;
2324
2325 case CPUPropertyType_APIC:
2326 if (mHWData->mX2APIC)
2327 aValue = TRUE;
2328 i_setModified(IsModified_MachineData);
2329 mHWData.backup();
2330 mHWData->mAPIC = !!aValue;
2331 break;
2332
2333 case CPUPropertyType_X2APIC:
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mX2APIC = !!aValue;
2337 if (aValue)
2338 mHWData->mAPIC = !!aValue;
2339 break;
2340
2341 case CPUPropertyType_IBPBOnVMExit:
2342 i_setModified(IsModified_MachineData);
2343 mHWData.backup();
2344 mHWData->mIBPBOnVMExit = !!aValue;
2345 break;
2346
2347 case CPUPropertyType_IBPBOnVMEntry:
2348 i_setModified(IsModified_MachineData);
2349 mHWData.backup();
2350 mHWData->mIBPBOnVMEntry = !!aValue;
2351 break;
2352
2353 case CPUPropertyType_SpecCtrl:
2354 i_setModified(IsModified_MachineData);
2355 mHWData.backup();
2356 mHWData->mSpecCtrl = !!aValue;
2357 break;
2358
2359 case CPUPropertyType_SpecCtrlByHost:
2360 i_setModified(IsModified_MachineData);
2361 mHWData.backup();
2362 mHWData->mSpecCtrlByHost = !!aValue;
2363 break;
2364
2365 case CPUPropertyType_HWVirt:
2366 i_setModified(IsModified_MachineData);
2367 mHWData.backup();
2368 mHWData->mNestedHWVirt = !!aValue;
2369 break;
2370
2371 default:
2372 return E_INVALIDARG;
2373 }
2374 return S_OK;
2375}
2376
2377HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2378 ULONG *aValEcx, ULONG *aValEdx)
2379{
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2382 {
2383 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2384 it != mHWData->mCpuIdLeafList.end();
2385 ++it)
2386 {
2387 if (aOrdinal == 0)
2388 {
2389 const settings::CpuIdLeaf &rLeaf= *it;
2390 *aIdx = rLeaf.idx;
2391 *aSubIdx = rLeaf.idxSub;
2392 *aValEax = rLeaf.uEax;
2393 *aValEbx = rLeaf.uEbx;
2394 *aValEcx = rLeaf.uEcx;
2395 *aValEdx = rLeaf.uEdx;
2396 return S_OK;
2397 }
2398 aOrdinal--;
2399 }
2400 }
2401 return E_INVALIDARG;
2402}
2403
2404HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2405{
2406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2407
2408 /*
2409 * Search the list.
2410 */
2411 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2412 {
2413 const settings::CpuIdLeaf &rLeaf= *it;
2414 if ( rLeaf.idx == aIdx
2415 && ( aSubIdx == UINT32_MAX
2416 || rLeaf.idxSub == aSubIdx) )
2417 {
2418 *aValEax = rLeaf.uEax;
2419 *aValEbx = rLeaf.uEbx;
2420 *aValEcx = rLeaf.uEcx;
2421 *aValEdx = rLeaf.uEdx;
2422 return S_OK;
2423 }
2424 }
2425
2426 return E_INVALIDARG;
2427}
2428
2429
2430HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2431{
2432 /*
2433 * Validate input before taking locks and checking state.
2434 */
2435 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2436 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2437 if ( aIdx >= UINT32_C(0x20)
2438 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2439 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2440 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2441
2442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2443 HRESULT rc = i_checkStateDependency(MutableStateDep);
2444 if (FAILED(rc)) return rc;
2445
2446 /*
2447 * Impose a maximum number of leaves.
2448 */
2449 if (mHWData->mCpuIdLeafList.size() > 256)
2450 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2451
2452 /*
2453 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2454 */
2455 i_setModified(IsModified_MachineData);
2456 mHWData.backup();
2457
2458 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2459 {
2460 settings::CpuIdLeaf &rLeaf= *it;
2461 if ( rLeaf.idx == aIdx
2462 && ( aSubIdx == UINT32_MAX
2463 || rLeaf.idxSub == aSubIdx) )
2464 it = mHWData->mCpuIdLeafList.erase(it);
2465 else
2466 ++it;
2467 }
2468
2469 settings::CpuIdLeaf NewLeaf;
2470 NewLeaf.idx = aIdx;
2471 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2472 NewLeaf.uEax = aValEax;
2473 NewLeaf.uEbx = aValEbx;
2474 NewLeaf.uEcx = aValEcx;
2475 NewLeaf.uEdx = aValEdx;
2476 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2477 return S_OK;
2478}
2479
2480HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2481{
2482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 HRESULT rc = i_checkStateDependency(MutableStateDep);
2485 if (FAILED(rc)) return rc;
2486
2487 /*
2488 * Do the removal.
2489 */
2490 bool fModified = false;
2491 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2492 {
2493 settings::CpuIdLeaf &rLeaf= *it;
2494 if ( rLeaf.idx == aIdx
2495 && ( aSubIdx == UINT32_MAX
2496 || rLeaf.idxSub == aSubIdx) )
2497 {
2498 if (!fModified)
2499 {
2500 fModified = true;
2501 i_setModified(IsModified_MachineData);
2502 mHWData.backup();
2503 }
2504 it = mHWData->mCpuIdLeafList.erase(it);
2505 }
2506 else
2507 ++it;
2508 }
2509
2510 return S_OK;
2511}
2512
2513HRESULT Machine::removeAllCPUIDLeaves()
2514{
2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 HRESULT rc = i_checkStateDependency(MutableStateDep);
2518 if (FAILED(rc)) return rc;
2519
2520 if (mHWData->mCpuIdLeafList.size() > 0)
2521 {
2522 i_setModified(IsModified_MachineData);
2523 mHWData.backup();
2524
2525 mHWData->mCpuIdLeafList.clear();
2526 }
2527
2528 return S_OK;
2529}
2530HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 switch(aProperty)
2535 {
2536 case HWVirtExPropertyType_Enabled:
2537 *aValue = mHWData->mHWVirtExEnabled;
2538 break;
2539
2540 case HWVirtExPropertyType_VPID:
2541 *aValue = mHWData->mHWVirtExVPIDEnabled;
2542 break;
2543
2544 case HWVirtExPropertyType_NestedPaging:
2545 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2546 break;
2547
2548 case HWVirtExPropertyType_UnrestrictedExecution:
2549 *aValue = mHWData->mHWVirtExUXEnabled;
2550 break;
2551
2552 case HWVirtExPropertyType_LargePages:
2553 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2554#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2555 *aValue = FALSE;
2556#endif
2557 break;
2558
2559 case HWVirtExPropertyType_Force:
2560 *aValue = mHWData->mHWVirtExForceEnabled;
2561 break;
2562
2563 case HWVirtExPropertyType_UseNativeApi:
2564 *aValue = mHWData->mHWVirtExUseNativeApi;
2565 break;
2566
2567 default:
2568 return E_INVALIDARG;
2569 }
2570 return S_OK;
2571}
2572
2573HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2574{
2575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 HRESULT rc = i_checkStateDependency(MutableStateDep);
2578 if (FAILED(rc)) return rc;
2579
2580 switch (aProperty)
2581 {
2582 case HWVirtExPropertyType_Enabled:
2583 i_setModified(IsModified_MachineData);
2584 mHWData.backup();
2585 mHWData->mHWVirtExEnabled = !!aValue;
2586 break;
2587
2588 case HWVirtExPropertyType_VPID:
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2592 break;
2593
2594 case HWVirtExPropertyType_NestedPaging:
2595 i_setModified(IsModified_MachineData);
2596 mHWData.backup();
2597 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2598 break;
2599
2600 case HWVirtExPropertyType_UnrestrictedExecution:
2601 i_setModified(IsModified_MachineData);
2602 mHWData.backup();
2603 mHWData->mHWVirtExUXEnabled = !!aValue;
2604 break;
2605
2606 case HWVirtExPropertyType_LargePages:
2607 i_setModified(IsModified_MachineData);
2608 mHWData.backup();
2609 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2610 break;
2611
2612 case HWVirtExPropertyType_Force:
2613 i_setModified(IsModified_MachineData);
2614 mHWData.backup();
2615 mHWData->mHWVirtExForceEnabled = !!aValue;
2616 break;
2617
2618 case HWVirtExPropertyType_UseNativeApi:
2619 i_setModified(IsModified_MachineData);
2620 mHWData.backup();
2621 mHWData->mHWVirtExUseNativeApi = !!aValue;
2622 break;
2623
2624 default:
2625 return E_INVALIDARG;
2626 }
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2641{
2642 /** @todo (r=dmik):
2643 * 1. Allow to change the name of the snapshot folder containing snapshots
2644 * 2. Rename the folder on disk instead of just changing the property
2645 * value (to be smart and not to leave garbage). Note that it cannot be
2646 * done here because the change may be rolled back. Thus, the right
2647 * place is #saveSettings().
2648 */
2649
2650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2651
2652 HRESULT rc = i_checkStateDependency(MutableStateDep);
2653 if (FAILED(rc)) return rc;
2654
2655 if (!mData->mCurrentSnapshot.isNull())
2656 return setError(E_FAIL,
2657 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2658
2659 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2660
2661 if (strSnapshotFolder.isEmpty())
2662 strSnapshotFolder = "Snapshots";
2663 int vrc = i_calculateFullPath(strSnapshotFolder,
2664 strSnapshotFolder);
2665 if (RT_FAILURE(vrc))
2666 return setErrorBoth(E_FAIL, vrc,
2667 tr("Invalid snapshot folder '%s' (%Rrc)"),
2668 strSnapshotFolder.c_str(), vrc);
2669
2670 i_setModified(IsModified_MachineData);
2671 mUserData.backup();
2672
2673 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 aMediumAttachments.resize(mMediumAttachments->size());
2683 size_t i = 0;
2684 for (MediumAttachmentList::const_iterator
2685 it = mMediumAttachments->begin();
2686 it != mMediumAttachments->end();
2687 ++it, ++i)
2688 aMediumAttachments[i] = *it;
2689
2690 return S_OK;
2691}
2692
2693HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2694{
2695 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2696
2697 Assert(!!mVRDEServer);
2698
2699 aVRDEServer = mVRDEServer;
2700
2701 return S_OK;
2702}
2703
2704HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2705{
2706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2707
2708 aAudioAdapter = mAudioAdapter;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2714{
2715#ifdef VBOX_WITH_VUSB
2716 clearError();
2717 MultiResult rc(S_OK);
2718
2719# ifdef VBOX_WITH_USB
2720 rc = mParent->i_host()->i_checkUSBProxyService();
2721 if (FAILED(rc)) return rc;
2722# endif
2723
2724 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2725
2726 aUSBControllers.resize(mUSBControllers->size());
2727 size_t i = 0;
2728 for (USBControllerList::const_iterator
2729 it = mUSBControllers->begin();
2730 it != mUSBControllers->end();
2731 ++it, ++i)
2732 aUSBControllers[i] = *it;
2733
2734 return S_OK;
2735#else
2736 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2737 * extended error info to indicate that USB is simply not available
2738 * (w/o treating it as a failure), for example, as in OSE */
2739 NOREF(aUSBControllers);
2740 ReturnComNotImplemented();
2741#endif /* VBOX_WITH_VUSB */
2742}
2743
2744HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2745{
2746#ifdef VBOX_WITH_VUSB
2747 clearError();
2748 MultiResult rc(S_OK);
2749
2750# ifdef VBOX_WITH_USB
2751 rc = mParent->i_host()->i_checkUSBProxyService();
2752 if (FAILED(rc)) return rc;
2753# endif
2754
2755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2756
2757 aUSBDeviceFilters = mUSBDeviceFilters;
2758 return rc;
2759#else
2760 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2761 * extended error info to indicate that USB is simply not available
2762 * (w/o treating it as a failure), for example, as in OSE */
2763 NOREF(aUSBDeviceFilters);
2764 ReturnComNotImplemented();
2765#endif /* VBOX_WITH_VUSB */
2766}
2767
2768HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aSettingsFilePath = mData->m_strConfigFileFull;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2778{
2779 RT_NOREF(aSettingsFilePath);
2780 ReturnComNotImplemented();
2781}
2782
2783HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2788 if (FAILED(rc)) return rc;
2789
2790 if (!mData->pMachineConfigFile->fileExists())
2791 // this is a new machine, and no config file exists yet:
2792 *aSettingsModified = TRUE;
2793 else
2794 *aSettingsModified = (mData->flModifications != 0);
2795
2796 return S_OK;
2797}
2798
2799HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 *aSessionState = mData->mSession.mState;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2809{
2810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 aSessionName = mData->mSession.mName;
2813
2814 return S_OK;
2815}
2816
2817HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2818{
2819 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2820
2821 *aSessionPID = mData->mSession.mPID;
2822
2823 return S_OK;
2824}
2825
2826HRESULT Machine::getState(MachineState_T *aState)
2827{
2828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2829
2830 *aState = mData->mMachineState;
2831 Assert(mData->mMachineState != MachineState_Null);
2832
2833 return S_OK;
2834}
2835
2836HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2837{
2838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2839
2840 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2841
2842 return S_OK;
2843}
2844
2845HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2846{
2847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2848
2849 aStateFilePath = mSSData->strStateFilePath;
2850
2851 return S_OK;
2852}
2853
2854HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2855{
2856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2857
2858 i_getLogFolder(aLogFolder);
2859
2860 return S_OK;
2861}
2862
2863HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2864{
2865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2866
2867 aCurrentSnapshot = mData->mCurrentSnapshot;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2877 ? 0
2878 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2879
2880 return S_OK;
2881}
2882
2883HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2884{
2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2886
2887 /* Note: for machines with no snapshots, we always return FALSE
2888 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2889 * reasons :) */
2890
2891 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2892 ? FALSE
2893 : mData->mCurrentStateModified;
2894
2895 return S_OK;
2896}
2897
2898HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2899{
2900 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2901
2902 aSharedFolders.resize(mHWData->mSharedFolders.size());
2903 size_t i = 0;
2904 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2905 it = mHWData->mSharedFolders.begin();
2906 it != mHWData->mSharedFolders.end();
2907 ++it, ++i)
2908 aSharedFolders[i] = *it;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aClipboardMode = mHWData->mClipboardMode;
2918
2919 return S_OK;
2920}
2921
2922HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2923{
2924 HRESULT rc = S_OK;
2925
2926 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2927
2928 alock.release();
2929 rc = i_onClipboardModeChange(aClipboardMode);
2930 alock.acquire();
2931 if (FAILED(rc)) return rc;
2932
2933 i_setModified(IsModified_MachineData);
2934 mHWData.backup();
2935 mHWData->mClipboardMode = aClipboardMode;
2936
2937 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2938 if (Global::IsOnline(mData->mMachineState))
2939 i_saveSettings(NULL);
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2945{
2946 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2947
2948 *aDnDMode = mHWData->mDnDMode;
2949
2950 return S_OK;
2951}
2952
2953HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2954{
2955 HRESULT rc = S_OK;
2956
2957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2958
2959 alock.release();
2960 rc = i_onDnDModeChange(aDnDMode);
2961
2962 alock.acquire();
2963 if (FAILED(rc)) return rc;
2964
2965 i_setModified(IsModified_MachineData);
2966 mHWData.backup();
2967 mHWData->mDnDMode = aDnDMode;
2968
2969 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2970 if (Global::IsOnline(mData->mMachineState))
2971 i_saveSettings(NULL);
2972
2973 return S_OK;
2974}
2975
2976HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2977{
2978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2979
2980 aStorageControllers.resize(mStorageControllers->size());
2981 size_t i = 0;
2982 for (StorageControllerList::const_iterator
2983 it = mStorageControllers->begin();
2984 it != mStorageControllers->end();
2985 ++it, ++i)
2986 aStorageControllers[i] = *it;
2987
2988 return S_OK;
2989}
2990
2991HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2992{
2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2994
2995 *aEnabled = mUserData->s.fTeleporterEnabled;
2996
2997 return S_OK;
2998}
2999
3000HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
3001{
3002 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 /* Only allow it to be set to true when PoweredOff or Aborted.
3005 (Clearing it is always permitted.) */
3006 if ( aTeleporterEnabled
3007 && mData->mRegistered
3008 && ( !i_isSessionMachine()
3009 || ( mData->mMachineState != MachineState_PoweredOff
3010 && mData->mMachineState != MachineState_Teleported
3011 && mData->mMachineState != MachineState_Aborted
3012 )
3013 )
3014 )
3015 return setError(VBOX_E_INVALID_VM_STATE,
3016 tr("The machine is not powered off (state is %s)"),
3017 Global::stringifyMachineState(mData->mMachineState));
3018
3019 i_setModified(IsModified_MachineData);
3020 mUserData.backup();
3021 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
3022
3023 return S_OK;
3024}
3025
3026HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
3027{
3028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3029
3030 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
3031
3032 return S_OK;
3033}
3034
3035HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
3036{
3037 if (aTeleporterPort >= _64K)
3038 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
3039
3040 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3041
3042 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3043 if (FAILED(rc)) return rc;
3044
3045 i_setModified(IsModified_MachineData);
3046 mUserData.backup();
3047 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
3048
3049 return S_OK;
3050}
3051
3052HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
3053{
3054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3055
3056 aTeleporterAddress = mUserData->s.strTeleporterAddress;
3057
3058 return S_OK;
3059}
3060
3061HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
3062{
3063 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3064
3065 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3066 if (FAILED(rc)) return rc;
3067
3068 i_setModified(IsModified_MachineData);
3069 mUserData.backup();
3070 mUserData->s.strTeleporterAddress = aTeleporterAddress;
3071
3072 return S_OK;
3073}
3074
3075HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
3076{
3077 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3078 aTeleporterPassword = mUserData->s.strTeleporterPassword;
3079
3080 return S_OK;
3081}
3082
3083HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
3084{
3085 /*
3086 * Hash the password first.
3087 */
3088 com::Utf8Str aT = aTeleporterPassword;
3089
3090 if (!aT.isEmpty())
3091 {
3092 if (VBoxIsPasswordHashed(&aT))
3093 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
3094 VBoxHashPassword(&aT);
3095 }
3096
3097 /*
3098 * Do the update.
3099 */
3100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3101 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
3102 if (SUCCEEDED(hrc))
3103 {
3104 i_setModified(IsModified_MachineData);
3105 mUserData.backup();
3106 mUserData->s.strTeleporterPassword = aT;
3107 }
3108
3109 return hrc;
3110}
3111
3112HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
3113{
3114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3115
3116 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
3117 return S_OK;
3118}
3119
3120HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
3121{
3122 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3123
3124 /** @todo deal with running state change. */
3125 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
3126 if (FAILED(rc)) return rc;
3127
3128 i_setModified(IsModified_MachineData);
3129 mUserData.backup();
3130 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
3131 return S_OK;
3132}
3133
3134HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3135{
3136 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3139 return S_OK;
3140}
3141
3142HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3143{
3144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3145
3146 /** @todo deal with running state change. */
3147 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3148 if (FAILED(rc)) return rc;
3149
3150 i_setModified(IsModified_MachineData);
3151 mUserData.backup();
3152 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3153 return S_OK;
3154}
3155
3156HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3157{
3158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3159
3160 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3161 return S_OK;
3162}
3163
3164HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3165{
3166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3167
3168 /** @todo deal with running state change. */
3169 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3170 if (FAILED(rc)) return rc;
3171
3172 i_setModified(IsModified_MachineData);
3173 mUserData.backup();
3174 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3175 return S_OK;
3176}
3177
3178HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3179{
3180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3181
3182 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3183
3184 return S_OK;
3185}
3186
3187HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3188{
3189 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3190
3191 /** @todo deal with running state change. */
3192 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3193 if (FAILED(rc)) return rc;
3194
3195 i_setModified(IsModified_MachineData);
3196 mUserData.backup();
3197 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3198
3199 return S_OK;
3200}
3201
3202HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3203{
3204 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3205
3206 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3207 return S_OK;
3208}
3209
3210HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3211{
3212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3213
3214 /** @todo deal with running state change. */
3215 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3216 if (FAILED(rc)) return rc;
3217
3218 i_setModified(IsModified_MachineData);
3219 mUserData.backup();
3220 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3221 return S_OK;
3222}
3223
3224HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3225{
3226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3227
3228 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3229
3230 return S_OK;
3231}
3232
3233HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3234{
3235 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3236
3237 /* Only allow it to be set to true when PoweredOff or Aborted.
3238 (Clearing it is always permitted.) */
3239 if ( aRTCUseUTC
3240 && mData->mRegistered
3241 && ( !i_isSessionMachine()
3242 || ( mData->mMachineState != MachineState_PoweredOff
3243 && mData->mMachineState != MachineState_Teleported
3244 && mData->mMachineState != MachineState_Aborted
3245 )
3246 )
3247 )
3248 return setError(VBOX_E_INVALID_VM_STATE,
3249 tr("The machine is not powered off (state is %s)"),
3250 Global::stringifyMachineState(mData->mMachineState));
3251
3252 i_setModified(IsModified_MachineData);
3253 mUserData.backup();
3254 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3255
3256 return S_OK;
3257}
3258
3259HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3260{
3261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3262
3263 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3264
3265 return S_OK;
3266}
3267
3268HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3269{
3270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3271
3272 HRESULT rc = i_checkStateDependency(MutableStateDep);
3273 if (FAILED(rc)) return rc;
3274
3275 i_setModified(IsModified_MachineData);
3276 mHWData.backup();
3277 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3278
3279 return S_OK;
3280}
3281
3282HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3283{
3284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3285
3286 *aIOCacheSize = mHWData->mIOCacheSize;
3287
3288 return S_OK;
3289}
3290
3291HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3292{
3293 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3294
3295 HRESULT rc = i_checkStateDependency(MutableStateDep);
3296 if (FAILED(rc)) return rc;
3297
3298 i_setModified(IsModified_MachineData);
3299 mHWData.backup();
3300 mHWData->mIOCacheSize = aIOCacheSize;
3301
3302 return S_OK;
3303}
3304
3305
3306/**
3307 * @note Locks objects!
3308 */
3309HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3310 LockType_T aLockType)
3311{
3312 /* check the session state */
3313 SessionState_T state;
3314 HRESULT rc = aSession->COMGETTER(State)(&state);
3315 if (FAILED(rc)) return rc;
3316
3317 if (state != SessionState_Unlocked)
3318 return setError(VBOX_E_INVALID_OBJECT_STATE,
3319 tr("The given session is busy"));
3320
3321 // get the client's IInternalSessionControl interface
3322 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3323 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3324 E_INVALIDARG);
3325
3326 // session name (only used in some code paths)
3327 Utf8Str strSessionName;
3328
3329 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3330
3331 if (!mData->mRegistered)
3332 return setError(E_UNEXPECTED,
3333 tr("The machine '%s' is not registered"),
3334 mUserData->s.strName.c_str());
3335
3336 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3337
3338 SessionState_T oldState = mData->mSession.mState;
3339 /* Hack: in case the session is closing and there is a progress object
3340 * which allows waiting for the session to be closed, take the opportunity
3341 * and do a limited wait (max. 1 second). This helps a lot when the system
3342 * is busy and thus session closing can take a little while. */
3343 if ( mData->mSession.mState == SessionState_Unlocking
3344 && mData->mSession.mProgress)
3345 {
3346 alock.release();
3347 mData->mSession.mProgress->WaitForCompletion(1000);
3348 alock.acquire();
3349 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3350 }
3351
3352 // try again now
3353 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3354 // (i.e. session machine exists)
3355 && (aLockType == LockType_Shared) // caller wants a shared link to the
3356 // existing session that holds the write lock:
3357 )
3358 {
3359 // OK, share the session... we are now dealing with three processes:
3360 // 1) VBoxSVC (where this code runs);
3361 // 2) process C: the caller's client process (who wants a shared session);
3362 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3363
3364 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3365 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3366 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3367 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3368 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3369
3370 /*
3371 * Release the lock before calling the client process. It's safe here
3372 * since the only thing to do after we get the lock again is to add
3373 * the remote control to the list (which doesn't directly influence
3374 * anything).
3375 */
3376 alock.release();
3377
3378 // get the console of the session holding the write lock (this is a remote call)
3379 ComPtr<IConsole> pConsoleW;
3380 if (mData->mSession.mLockType == LockType_VM)
3381 {
3382 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3383 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3384 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3385 if (FAILED(rc))
3386 // the failure may occur w/o any error info (from RPC), so provide one
3387 return setError(VBOX_E_VM_ERROR,
3388 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3389 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3390 }
3391
3392 // share the session machine and W's console with the caller's session
3393 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3394 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3395 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3396
3397 if (FAILED(rc))
3398 // the failure may occur w/o any error info (from RPC), so provide one
3399 return setError(VBOX_E_VM_ERROR,
3400 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3401 alock.acquire();
3402
3403 // need to revalidate the state after acquiring the lock again
3404 if (mData->mSession.mState != SessionState_Locked)
3405 {
3406 pSessionControl->Uninitialize();
3407 return setError(VBOX_E_INVALID_SESSION_STATE,
3408 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3409 mUserData->s.strName.c_str());
3410 }
3411
3412 // add the caller's session to the list
3413 mData->mSession.mRemoteControls.push_back(pSessionControl);
3414 }
3415 else if ( mData->mSession.mState == SessionState_Locked
3416 || mData->mSession.mState == SessionState_Unlocking
3417 )
3418 {
3419 // sharing not permitted, or machine still unlocking:
3420 return setError(VBOX_E_INVALID_OBJECT_STATE,
3421 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3422 mUserData->s.strName.c_str());
3423 }
3424 else
3425 {
3426 // machine is not locked: then write-lock the machine (create the session machine)
3427
3428 // must not be busy
3429 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3430
3431 // get the caller's session PID
3432 RTPROCESS pid = NIL_RTPROCESS;
3433 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3434 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3435 Assert(pid != NIL_RTPROCESS);
3436
3437 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3438
3439 if (fLaunchingVMProcess)
3440 {
3441 if (mData->mSession.mPID == NIL_RTPROCESS)
3442 {
3443 // two or more clients racing for a lock, the one which set the
3444 // session state to Spawning will win, the others will get an
3445 // error as we can't decide here if waiting a little would help
3446 // (only for shared locks this would avoid an error)
3447 return setError(VBOX_E_INVALID_OBJECT_STATE,
3448 tr("The machine '%s' already has a lock request pending"),
3449 mUserData->s.strName.c_str());
3450 }
3451
3452 // this machine is awaiting for a spawning session to be opened:
3453 // then the calling process must be the one that got started by
3454 // LaunchVMProcess()
3455
3456 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3457 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3458
3459#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3460 /* Hardened windows builds spawns three processes when a VM is
3461 launched, the 3rd one is the one that will end up here. */
3462 RTPROCESS ppid;
3463 int rc = RTProcQueryParent(pid, &ppid);
3464 if (RT_SUCCESS(rc))
3465 rc = RTProcQueryParent(ppid, &ppid);
3466 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3467 || rc == VERR_ACCESS_DENIED)
3468 {
3469 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3470 mData->mSession.mPID = pid;
3471 }
3472#endif
3473
3474 if (mData->mSession.mPID != pid)
3475 return setError(E_ACCESSDENIED,
3476 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3477 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3478 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3479 }
3480
3481 // create the mutable SessionMachine from the current machine
3482 ComObjPtr<SessionMachine> sessionMachine;
3483 sessionMachine.createObject();
3484 rc = sessionMachine->init(this);
3485 AssertComRC(rc);
3486
3487 /* NOTE: doing return from this function after this point but
3488 * before the end is forbidden since it may call SessionMachine::uninit()
3489 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3490 * lock while still holding the Machine lock in alock so that a deadlock
3491 * is possible due to the wrong lock order. */
3492
3493 if (SUCCEEDED(rc))
3494 {
3495 /*
3496 * Set the session state to Spawning to protect against subsequent
3497 * attempts to open a session and to unregister the machine after
3498 * we release the lock.
3499 */
3500 SessionState_T origState = mData->mSession.mState;
3501 mData->mSession.mState = SessionState_Spawning;
3502
3503#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3504 /* Get the client token ID to be passed to the client process */
3505 Utf8Str strTokenId;
3506 sessionMachine->i_getTokenId(strTokenId);
3507 Assert(!strTokenId.isEmpty());
3508#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3509 /* Get the client token to be passed to the client process */
3510 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3511 /* The token is now "owned" by pToken, fix refcount */
3512 if (!pToken.isNull())
3513 pToken->Release();
3514#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3515
3516 /*
3517 * Release the lock before calling the client process -- it will call
3518 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3519 * because the state is Spawning, so that LaunchVMProcess() and
3520 * LockMachine() calls will fail. This method, called before we
3521 * acquire the lock again, will fail because of the wrong PID.
3522 *
3523 * Note that mData->mSession.mRemoteControls accessed outside
3524 * the lock may not be modified when state is Spawning, so it's safe.
3525 */
3526 alock.release();
3527
3528 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3529#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3530 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3531#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3532 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3533 /* Now the token is owned by the client process. */
3534 pToken.setNull();
3535#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3536 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3537
3538 /* The failure may occur w/o any error info (from RPC), so provide one */
3539 if (FAILED(rc))
3540 setError(VBOX_E_VM_ERROR,
3541 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3542
3543 // get session name, either to remember or to compare against
3544 // the already known session name.
3545 {
3546 Bstr bstrSessionName;
3547 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3548 if (SUCCEEDED(rc2))
3549 strSessionName = bstrSessionName;
3550 }
3551
3552 if ( SUCCEEDED(rc)
3553 && fLaunchingVMProcess
3554 )
3555 {
3556 /* complete the remote session initialization */
3557
3558 /* get the console from the direct session */
3559 ComPtr<IConsole> console;
3560 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3561 ComAssertComRC(rc);
3562
3563 if (SUCCEEDED(rc) && !console)
3564 {
3565 ComAssert(!!console);
3566 rc = E_FAIL;
3567 }
3568
3569 /* assign machine & console to the remote session */
3570 if (SUCCEEDED(rc))
3571 {
3572 /*
3573 * after LaunchVMProcess(), the first and the only
3574 * entry in remoteControls is that remote session
3575 */
3576 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3577 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3578 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3579
3580 /* The failure may occur w/o any error info (from RPC), so provide one */
3581 if (FAILED(rc))
3582 setError(VBOX_E_VM_ERROR,
3583 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3584 }
3585
3586 if (FAILED(rc))
3587 pSessionControl->Uninitialize();
3588 }
3589
3590 /* acquire the lock again */
3591 alock.acquire();
3592
3593 /* Restore the session state */
3594 mData->mSession.mState = origState;
3595 }
3596
3597 // finalize spawning anyway (this is why we don't return on errors above)
3598 if (fLaunchingVMProcess)
3599 {
3600 Assert(mData->mSession.mName == strSessionName);
3601 /* Note that the progress object is finalized later */
3602 /** @todo Consider checking mData->mSession.mProgress for cancellation
3603 * around here. */
3604
3605 /* We don't reset mSession.mPID here because it is necessary for
3606 * SessionMachine::uninit() to reap the child process later. */
3607
3608 if (FAILED(rc))
3609 {
3610 /* Close the remote session, remove the remote control from the list
3611 * and reset session state to Closed (@note keep the code in sync
3612 * with the relevant part in checkForSpawnFailure()). */
3613
3614 Assert(mData->mSession.mRemoteControls.size() == 1);
3615 if (mData->mSession.mRemoteControls.size() == 1)
3616 {
3617 ErrorInfoKeeper eik;
3618 mData->mSession.mRemoteControls.front()->Uninitialize();
3619 }
3620
3621 mData->mSession.mRemoteControls.clear();
3622 mData->mSession.mState = SessionState_Unlocked;
3623 }
3624 }
3625 else
3626 {
3627 /* memorize PID of the directly opened session */
3628 if (SUCCEEDED(rc))
3629 mData->mSession.mPID = pid;
3630 }
3631
3632 if (SUCCEEDED(rc))
3633 {
3634 mData->mSession.mLockType = aLockType;
3635 /* memorize the direct session control and cache IUnknown for it */
3636 mData->mSession.mDirectControl = pSessionControl;
3637 mData->mSession.mState = SessionState_Locked;
3638 if (!fLaunchingVMProcess)
3639 mData->mSession.mName = strSessionName;
3640 /* associate the SessionMachine with this Machine */
3641 mData->mSession.mMachine = sessionMachine;
3642
3643 /* request an IUnknown pointer early from the remote party for later
3644 * identity checks (it will be internally cached within mDirectControl
3645 * at least on XPCOM) */
3646 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3647 NOREF(unk);
3648 }
3649
3650 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3651 * would break the lock order */
3652 alock.release();
3653
3654 /* uninitialize the created session machine on failure */
3655 if (FAILED(rc))
3656 sessionMachine->uninit();
3657 }
3658
3659 if (SUCCEEDED(rc))
3660 {
3661 /*
3662 * tell the client watcher thread to update the set of
3663 * machines that have open sessions
3664 */
3665 mParent->i_updateClientWatcher();
3666
3667 if (oldState != SessionState_Locked)
3668 /* fire an event */
3669 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3670 }
3671
3672 return rc;
3673}
3674
3675/**
3676 * @note Locks objects!
3677 */
3678HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3679 const com::Utf8Str &aName,
3680 const com::Utf8Str &aEnvironment,
3681 ComPtr<IProgress> &aProgress)
3682{
3683 Utf8Str strFrontend(aName);
3684 /* "emergencystop" doesn't need the session, so skip the checks/interface
3685 * retrieval. This code doesn't quite fit in here, but introducing a
3686 * special API method would be even more effort, and would require explicit
3687 * support by every API client. It's better to hide the feature a bit. */
3688 if (strFrontend != "emergencystop")
3689 CheckComArgNotNull(aSession);
3690
3691 HRESULT rc = S_OK;
3692 if (strFrontend.isEmpty())
3693 {
3694 Bstr bstrFrontend;
3695 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3696 if (FAILED(rc))
3697 return rc;
3698 strFrontend = bstrFrontend;
3699 if (strFrontend.isEmpty())
3700 {
3701 ComPtr<ISystemProperties> systemProperties;
3702 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3703 if (FAILED(rc))
3704 return rc;
3705 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3706 if (FAILED(rc))
3707 return rc;
3708 strFrontend = bstrFrontend;
3709 }
3710 /* paranoia - emergencystop is not a valid default */
3711 if (strFrontend == "emergencystop")
3712 strFrontend = Utf8Str::Empty;
3713 }
3714 /* default frontend: Qt GUI */
3715 if (strFrontend.isEmpty())
3716 strFrontend = "GUI/Qt";
3717
3718 if (strFrontend != "emergencystop")
3719 {
3720 /* check the session state */
3721 SessionState_T state;
3722 rc = aSession->COMGETTER(State)(&state);
3723 if (FAILED(rc))
3724 return rc;
3725
3726 if (state != SessionState_Unlocked)
3727 return setError(VBOX_E_INVALID_OBJECT_STATE,
3728 tr("The given session is busy"));
3729
3730 /* get the IInternalSessionControl interface */
3731 ComPtr<IInternalSessionControl> control(aSession);
3732 ComAssertMsgRet(!control.isNull(),
3733 ("No IInternalSessionControl interface"),
3734 E_INVALIDARG);
3735
3736 /* get the teleporter enable state for the progress object init. */
3737 BOOL fTeleporterEnabled;
3738 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3739 if (FAILED(rc))
3740 return rc;
3741
3742 /* create a progress object */
3743 ComObjPtr<ProgressProxy> progress;
3744 progress.createObject();
3745 rc = progress->init(mParent,
3746 static_cast<IMachine*>(this),
3747 Bstr(tr("Starting VM")).raw(),
3748 TRUE /* aCancelable */,
3749 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3750 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3751 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3752 2 /* uFirstOperationWeight */,
3753 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3754
3755 if (SUCCEEDED(rc))
3756 {
3757 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3758 if (SUCCEEDED(rc))
3759 {
3760 aProgress = progress;
3761
3762 /* signal the client watcher thread */
3763 mParent->i_updateClientWatcher();
3764
3765 /* fire an event */
3766 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3767 }
3768 }
3769 }
3770 else
3771 {
3772 /* no progress object - either instant success or failure */
3773 aProgress = NULL;
3774
3775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3776
3777 if (mData->mSession.mState != SessionState_Locked)
3778 return setError(VBOX_E_INVALID_OBJECT_STATE,
3779 tr("The machine '%s' is not locked by a session"),
3780 mUserData->s.strName.c_str());
3781
3782 /* must have a VM process associated - do not kill normal API clients
3783 * with an open session */
3784 if (!Global::IsOnline(mData->mMachineState))
3785 return setError(VBOX_E_INVALID_OBJECT_STATE,
3786 tr("The machine '%s' does not have a VM process"),
3787 mUserData->s.strName.c_str());
3788
3789 /* forcibly terminate the VM process */
3790 if (mData->mSession.mPID != NIL_RTPROCESS)
3791 RTProcTerminate(mData->mSession.mPID);
3792
3793 /* signal the client watcher thread, as most likely the client has
3794 * been terminated */
3795 mParent->i_updateClientWatcher();
3796 }
3797
3798 return rc;
3799}
3800
3801HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3802{
3803 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3804 return setError(E_INVALIDARG,
3805 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3806 aPosition, SchemaDefs::MaxBootPosition);
3807
3808 if (aDevice == DeviceType_USB)
3809 return setError(E_NOTIMPL,
3810 tr("Booting from USB device is currently not supported"));
3811
3812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3813
3814 HRESULT rc = i_checkStateDependency(MutableStateDep);
3815 if (FAILED(rc)) return rc;
3816
3817 i_setModified(IsModified_MachineData);
3818 mHWData.backup();
3819 mHWData->mBootOrder[aPosition - 1] = aDevice;
3820
3821 return S_OK;
3822}
3823
3824HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3825{
3826 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3827 return setError(E_INVALIDARG,
3828 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3829 aPosition, SchemaDefs::MaxBootPosition);
3830
3831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3832
3833 *aDevice = mHWData->mBootOrder[aPosition - 1];
3834
3835 return S_OK;
3836}
3837
3838HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3839 LONG aControllerPort,
3840 LONG aDevice,
3841 DeviceType_T aType,
3842 const ComPtr<IMedium> &aMedium)
3843{
3844 IMedium *aM = aMedium;
3845 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3846 aName.c_str(), aControllerPort, aDevice, aType, aM));
3847
3848 // request the host lock first, since might be calling Host methods for getting host drives;
3849 // next, protect the media tree all the while we're in here, as well as our member variables
3850 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3851 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3852
3853 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3854 if (FAILED(rc)) return rc;
3855
3856 /// @todo NEWMEDIA implicit machine registration
3857 if (!mData->mRegistered)
3858 return setError(VBOX_E_INVALID_OBJECT_STATE,
3859 tr("Cannot attach storage devices to an unregistered machine"));
3860
3861 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3862
3863 /* Check for an existing controller. */
3864 ComObjPtr<StorageController> ctl;
3865 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3866 if (FAILED(rc)) return rc;
3867
3868 StorageControllerType_T ctrlType;
3869 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3870 if (FAILED(rc))
3871 return setError(E_FAIL,
3872 tr("Could not get type of controller '%s'"),
3873 aName.c_str());
3874
3875 bool fSilent = false;
3876 Utf8Str strReconfig;
3877
3878 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3879 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3880 if ( mData->mMachineState == MachineState_Paused
3881 && strReconfig == "1")
3882 fSilent = true;
3883
3884 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3885 bool fHotplug = false;
3886 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3887 fHotplug = true;
3888
3889 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3890 return setError(VBOX_E_INVALID_VM_STATE,
3891 tr("Controller '%s' does not support hotplugging"),
3892 aName.c_str());
3893
3894 // check that the port and device are not out of range
3895 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3896 if (FAILED(rc)) return rc;
3897
3898 /* check if the device slot is already busy */
3899 MediumAttachment *pAttachTemp;
3900 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3901 aName,
3902 aControllerPort,
3903 aDevice)))
3904 {
3905 Medium *pMedium = pAttachTemp->i_getMedium();
3906 if (pMedium)
3907 {
3908 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3909 return setError(VBOX_E_OBJECT_IN_USE,
3910 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3911 pMedium->i_getLocationFull().c_str(),
3912 aControllerPort,
3913 aDevice,
3914 aName.c_str());
3915 }
3916 else
3917 return setError(VBOX_E_OBJECT_IN_USE,
3918 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3919 aControllerPort, aDevice, aName.c_str());
3920 }
3921
3922 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3923 if (aMedium && medium.isNull())
3924 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3925
3926 AutoCaller mediumCaller(medium);
3927 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3928
3929 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3930
3931 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3932 && !medium.isNull()
3933 )
3934 return setError(VBOX_E_OBJECT_IN_USE,
3935 tr("Medium '%s' is already attached to this virtual machine"),
3936 medium->i_getLocationFull().c_str());
3937
3938 if (!medium.isNull())
3939 {
3940 MediumType_T mtype = medium->i_getType();
3941 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3942 // For DVDs it's not written to the config file, so needs no global config
3943 // version bump. For floppies it's a new attribute "type", which is ignored
3944 // by older VirtualBox version, so needs no global config version bump either.
3945 // For hard disks this type is not accepted.
3946 if (mtype == MediumType_MultiAttach)
3947 {
3948 // This type is new with VirtualBox 4.0 and therefore requires settings
3949 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3950 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3951 // two reasons: The medium type is a property of the media registry tree, which
3952 // can reside in the global config file (for pre-4.0 media); we would therefore
3953 // possibly need to bump the global config version. We don't want to do that though
3954 // because that might make downgrading to pre-4.0 impossible.
3955 // As a result, we can only use these two new types if the medium is NOT in the
3956 // global registry:
3957 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3958 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3959 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3960 )
3961 return setError(VBOX_E_INVALID_OBJECT_STATE,
3962 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3963 "to machines that were created with VirtualBox 4.0 or later"),
3964 medium->i_getLocationFull().c_str());
3965 }
3966 }
3967
3968 bool fIndirect = false;
3969 if (!medium.isNull())
3970 fIndirect = medium->i_isReadOnly();
3971 bool associate = true;
3972
3973 do
3974 {
3975 if ( aType == DeviceType_HardDisk
3976 && mMediumAttachments.isBackedUp())
3977 {
3978 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3979
3980 /* check if the medium was attached to the VM before we started
3981 * changing attachments in which case the attachment just needs to
3982 * be restored */
3983 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3984 {
3985 AssertReturn(!fIndirect, E_FAIL);
3986
3987 /* see if it's the same bus/channel/device */
3988 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3989 {
3990 /* the simplest case: restore the whole attachment
3991 * and return, nothing else to do */
3992 mMediumAttachments->push_back(pAttachTemp);
3993
3994 /* Reattach the medium to the VM. */
3995 if (fHotplug || fSilent)
3996 {
3997 mediumLock.release();
3998 treeLock.release();
3999 alock.release();
4000
4001 MediumLockList *pMediumLockList(new MediumLockList());
4002
4003 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4004 medium /* pToLockWrite */,
4005 false /* fMediumLockWriteAll */,
4006 NULL,
4007 *pMediumLockList);
4008 alock.acquire();
4009 if (FAILED(rc))
4010 delete pMediumLockList;
4011 else
4012 {
4013 mData->mSession.mLockedMedia.Unlock();
4014 alock.release();
4015 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4016 mData->mSession.mLockedMedia.Lock();
4017 alock.acquire();
4018 }
4019 alock.release();
4020
4021 if (SUCCEEDED(rc))
4022 {
4023 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4024 /* Remove lock list in case of error. */
4025 if (FAILED(rc))
4026 {
4027 mData->mSession.mLockedMedia.Unlock();
4028 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4029 mData->mSession.mLockedMedia.Lock();
4030 }
4031 }
4032 }
4033
4034 return S_OK;
4035 }
4036
4037 /* bus/channel/device differ; we need a new attachment object,
4038 * but don't try to associate it again */
4039 associate = false;
4040 break;
4041 }
4042 }
4043
4044 /* go further only if the attachment is to be indirect */
4045 if (!fIndirect)
4046 break;
4047
4048 /* perform the so called smart attachment logic for indirect
4049 * attachments. Note that smart attachment is only applicable to base
4050 * hard disks. */
4051
4052 if (medium->i_getParent().isNull())
4053 {
4054 /* first, investigate the backup copy of the current hard disk
4055 * attachments to make it possible to re-attach existing diffs to
4056 * another device slot w/o losing their contents */
4057 if (mMediumAttachments.isBackedUp())
4058 {
4059 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
4060
4061 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
4062 uint32_t foundLevel = 0;
4063
4064 for (MediumAttachmentList::const_iterator
4065 it = oldAtts.begin();
4066 it != oldAtts.end();
4067 ++it)
4068 {
4069 uint32_t level = 0;
4070 MediumAttachment *pAttach = *it;
4071 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4072 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4073 if (pMedium.isNull())
4074 continue;
4075
4076 if (pMedium->i_getBase(&level) == medium)
4077 {
4078 /* skip the hard disk if its currently attached (we
4079 * cannot attach the same hard disk twice) */
4080 if (i_findAttachment(*mMediumAttachments.data(),
4081 pMedium))
4082 continue;
4083
4084 /* matched device, channel and bus (i.e. attached to the
4085 * same place) will win and immediately stop the search;
4086 * otherwise the attachment that has the youngest
4087 * descendant of medium will be used
4088 */
4089 if (pAttach->i_matches(aName, aControllerPort, aDevice))
4090 {
4091 /* the simplest case: restore the whole attachment
4092 * and return, nothing else to do */
4093 mMediumAttachments->push_back(*it);
4094
4095 /* Reattach the medium to the VM. */
4096 if (fHotplug || fSilent)
4097 {
4098 mediumLock.release();
4099 treeLock.release();
4100 alock.release();
4101
4102 MediumLockList *pMediumLockList(new MediumLockList());
4103
4104 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4105 medium /* pToLockWrite */,
4106 false /* fMediumLockWriteAll */,
4107 NULL,
4108 *pMediumLockList);
4109 alock.acquire();
4110 if (FAILED(rc))
4111 delete pMediumLockList;
4112 else
4113 {
4114 mData->mSession.mLockedMedia.Unlock();
4115 alock.release();
4116 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
4117 mData->mSession.mLockedMedia.Lock();
4118 alock.acquire();
4119 }
4120 alock.release();
4121
4122 if (SUCCEEDED(rc))
4123 {
4124 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
4125 /* Remove lock list in case of error. */
4126 if (FAILED(rc))
4127 {
4128 mData->mSession.mLockedMedia.Unlock();
4129 mData->mSession.mLockedMedia.Remove(pAttachTemp);
4130 mData->mSession.mLockedMedia.Lock();
4131 }
4132 }
4133 }
4134
4135 return S_OK;
4136 }
4137 else if ( foundIt == oldAtts.end()
4138 || level > foundLevel /* prefer younger */
4139 )
4140 {
4141 foundIt = it;
4142 foundLevel = level;
4143 }
4144 }
4145 }
4146
4147 if (foundIt != oldAtts.end())
4148 {
4149 /* use the previously attached hard disk */
4150 medium = (*foundIt)->i_getMedium();
4151 mediumCaller.attach(medium);
4152 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4153 mediumLock.attach(medium);
4154 /* not implicit, doesn't require association with this VM */
4155 fIndirect = false;
4156 associate = false;
4157 /* go right to the MediumAttachment creation */
4158 break;
4159 }
4160 }
4161
4162 /* must give up the medium lock and medium tree lock as below we
4163 * go over snapshots, which needs a lock with higher lock order. */
4164 mediumLock.release();
4165 treeLock.release();
4166
4167 /* then, search through snapshots for the best diff in the given
4168 * hard disk's chain to base the new diff on */
4169
4170 ComObjPtr<Medium> base;
4171 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4172 while (snap)
4173 {
4174 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4175
4176 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
4177
4178 MediumAttachment *pAttachFound = NULL;
4179 uint32_t foundLevel = 0;
4180
4181 for (MediumAttachmentList::const_iterator
4182 it = snapAtts.begin();
4183 it != snapAtts.end();
4184 ++it)
4185 {
4186 MediumAttachment *pAttach = *it;
4187 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4188 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4189 if (pMedium.isNull())
4190 continue;
4191
4192 uint32_t level = 0;
4193 if (pMedium->i_getBase(&level) == medium)
4194 {
4195 /* matched device, channel and bus (i.e. attached to the
4196 * same place) will win and immediately stop the search;
4197 * otherwise the attachment that has the youngest
4198 * descendant of medium will be used
4199 */
4200 if ( pAttach->i_getDevice() == aDevice
4201 && pAttach->i_getPort() == aControllerPort
4202 && pAttach->i_getControllerName() == aName
4203 )
4204 {
4205 pAttachFound = pAttach;
4206 break;
4207 }
4208 else if ( !pAttachFound
4209 || level > foundLevel /* prefer younger */
4210 )
4211 {
4212 pAttachFound = pAttach;
4213 foundLevel = level;
4214 }
4215 }
4216 }
4217
4218 if (pAttachFound)
4219 {
4220 base = pAttachFound->i_getMedium();
4221 break;
4222 }
4223
4224 snap = snap->i_getParent();
4225 }
4226
4227 /* re-lock medium tree and the medium, as we need it below */
4228 treeLock.acquire();
4229 mediumLock.acquire();
4230
4231 /* found a suitable diff, use it as a base */
4232 if (!base.isNull())
4233 {
4234 medium = base;
4235 mediumCaller.attach(medium);
4236 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4237 mediumLock.attach(medium);
4238 }
4239 }
4240
4241 Utf8Str strFullSnapshotFolder;
4242 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4243
4244 ComObjPtr<Medium> diff;
4245 diff.createObject();
4246 // store this diff in the same registry as the parent
4247 Guid uuidRegistryParent;
4248 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4249 {
4250 // parent image has no registry: this can happen if we're attaching a new immutable
4251 // image that has not yet been attached (medium then points to the base and we're
4252 // creating the diff image for the immutable, and the parent is not yet registered);
4253 // put the parent in the machine registry then
4254 mediumLock.release();
4255 treeLock.release();
4256 alock.release();
4257 i_addMediumToRegistry(medium);
4258 alock.acquire();
4259 treeLock.acquire();
4260 mediumLock.acquire();
4261 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4262 }
4263 rc = diff->init(mParent,
4264 medium->i_getPreferredDiffFormat(),
4265 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4266 uuidRegistryParent,
4267 DeviceType_HardDisk);
4268 if (FAILED(rc)) return rc;
4269
4270 /* Apply the normal locking logic to the entire chain. */
4271 MediumLockList *pMediumLockList(new MediumLockList());
4272 mediumLock.release();
4273 treeLock.release();
4274 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4275 diff /* pToLockWrite */,
4276 false /* fMediumLockWriteAll */,
4277 medium,
4278 *pMediumLockList);
4279 treeLock.acquire();
4280 mediumLock.acquire();
4281 if (SUCCEEDED(rc))
4282 {
4283 mediumLock.release();
4284 treeLock.release();
4285 rc = pMediumLockList->Lock();
4286 treeLock.acquire();
4287 mediumLock.acquire();
4288 if (FAILED(rc))
4289 setError(rc,
4290 tr("Could not lock medium when creating diff '%s'"),
4291 diff->i_getLocationFull().c_str());
4292 else
4293 {
4294 /* will release the lock before the potentially lengthy
4295 * operation, so protect with the special state */
4296 MachineState_T oldState = mData->mMachineState;
4297 i_setMachineState(MachineState_SettingUp);
4298
4299 mediumLock.release();
4300 treeLock.release();
4301 alock.release();
4302
4303 rc = medium->i_createDiffStorage(diff,
4304 medium->i_getPreferredDiffVariant(),
4305 pMediumLockList,
4306 NULL /* aProgress */,
4307 true /* aWait */);
4308
4309 alock.acquire();
4310 treeLock.acquire();
4311 mediumLock.acquire();
4312
4313 i_setMachineState(oldState);
4314 }
4315 }
4316
4317 /* Unlock the media and free the associated memory. */
4318 delete pMediumLockList;
4319
4320 if (FAILED(rc)) return rc;
4321
4322 /* use the created diff for the actual attachment */
4323 medium = diff;
4324 mediumCaller.attach(medium);
4325 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4326 mediumLock.attach(medium);
4327 }
4328 while (0);
4329
4330 ComObjPtr<MediumAttachment> attachment;
4331 attachment.createObject();
4332 rc = attachment->init(this,
4333 medium,
4334 aName,
4335 aControllerPort,
4336 aDevice,
4337 aType,
4338 fIndirect,
4339 false /* fPassthrough */,
4340 false /* fTempEject */,
4341 false /* fNonRotational */,
4342 false /* fDiscard */,
4343 fHotplug /* fHotPluggable */,
4344 Utf8Str::Empty);
4345 if (FAILED(rc)) return rc;
4346
4347 if (associate && !medium.isNull())
4348 {
4349 // as the last step, associate the medium to the VM
4350 rc = medium->i_addBackReference(mData->mUuid);
4351 // here we can fail because of Deleting, or being in process of creating a Diff
4352 if (FAILED(rc)) return rc;
4353
4354 mediumLock.release();
4355 treeLock.release();
4356 alock.release();
4357 i_addMediumToRegistry(medium);
4358 alock.acquire();
4359 treeLock.acquire();
4360 mediumLock.acquire();
4361 }
4362
4363 /* success: finally remember the attachment */
4364 i_setModified(IsModified_Storage);
4365 mMediumAttachments.backup();
4366 mMediumAttachments->push_back(attachment);
4367
4368 mediumLock.release();
4369 treeLock.release();
4370 alock.release();
4371
4372 if (fHotplug || fSilent)
4373 {
4374 if (!medium.isNull())
4375 {
4376 MediumLockList *pMediumLockList(new MediumLockList());
4377
4378 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4379 medium /* pToLockWrite */,
4380 false /* fMediumLockWriteAll */,
4381 NULL,
4382 *pMediumLockList);
4383 alock.acquire();
4384 if (FAILED(rc))
4385 delete pMediumLockList;
4386 else
4387 {
4388 mData->mSession.mLockedMedia.Unlock();
4389 alock.release();
4390 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4391 mData->mSession.mLockedMedia.Lock();
4392 alock.acquire();
4393 }
4394 alock.release();
4395 }
4396
4397 if (SUCCEEDED(rc))
4398 {
4399 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4400 /* Remove lock list in case of error. */
4401 if (FAILED(rc))
4402 {
4403 mData->mSession.mLockedMedia.Unlock();
4404 mData->mSession.mLockedMedia.Remove(attachment);
4405 mData->mSession.mLockedMedia.Lock();
4406 }
4407 }
4408 }
4409
4410 /* Save modified registries, but skip this machine as it's the caller's
4411 * job to save its settings like all other settings changes. */
4412 mParent->i_unmarkRegistryModified(i_getId());
4413 mParent->i_saveModifiedRegistries();
4414
4415 return rc;
4416}
4417
4418HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4419 LONG aDevice)
4420{
4421 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4422 aName.c_str(), aControllerPort, aDevice));
4423
4424 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4425
4426 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4427 if (FAILED(rc)) return rc;
4428
4429 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4430
4431 /* Check for an existing controller. */
4432 ComObjPtr<StorageController> ctl;
4433 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4434 if (FAILED(rc)) return rc;
4435
4436 StorageControllerType_T ctrlType;
4437 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4438 if (FAILED(rc))
4439 return setError(E_FAIL,
4440 tr("Could not get type of controller '%s'"),
4441 aName.c_str());
4442
4443 bool fSilent = false;
4444 Utf8Str strReconfig;
4445
4446 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4447 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4448 if ( mData->mMachineState == MachineState_Paused
4449 && strReconfig == "1")
4450 fSilent = true;
4451
4452 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4453 bool fHotplug = false;
4454 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4455 fHotplug = true;
4456
4457 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4458 return setError(VBOX_E_INVALID_VM_STATE,
4459 tr("Controller '%s' does not support hotplugging"),
4460 aName.c_str());
4461
4462 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4463 aName,
4464 aControllerPort,
4465 aDevice);
4466 if (!pAttach)
4467 return setError(VBOX_E_OBJECT_NOT_FOUND,
4468 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4469 aDevice, aControllerPort, aName.c_str());
4470
4471 if (fHotplug && !pAttach->i_getHotPluggable())
4472 return setError(VBOX_E_NOT_SUPPORTED,
4473 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4474 aDevice, aControllerPort, aName.c_str());
4475
4476 /*
4477 * The VM has to detach the device before we delete any implicit diffs.
4478 * If this fails we can roll back without loosing data.
4479 */
4480 if (fHotplug || fSilent)
4481 {
4482 alock.release();
4483 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4484 alock.acquire();
4485 }
4486 if (FAILED(rc)) return rc;
4487
4488 /* If we are here everything went well and we can delete the implicit now. */
4489 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4490
4491 alock.release();
4492
4493 /* Save modified registries, but skip this machine as it's the caller's
4494 * job to save its settings like all other settings changes. */
4495 mParent->i_unmarkRegistryModified(i_getId());
4496 mParent->i_saveModifiedRegistries();
4497
4498 return rc;
4499}
4500
4501HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4502 LONG aDevice, BOOL aPassthrough)
4503{
4504 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4505 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4506
4507 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4508
4509 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4510 if (FAILED(rc)) return rc;
4511
4512 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4513
4514 /* Check for an existing controller. */
4515 ComObjPtr<StorageController> ctl;
4516 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4517 if (FAILED(rc)) return rc;
4518
4519 StorageControllerType_T ctrlType;
4520 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4521 if (FAILED(rc))
4522 return setError(E_FAIL,
4523 tr("Could not get type of controller '%s'"),
4524 aName.c_str());
4525
4526 bool fSilent = false;
4527 Utf8Str strReconfig;
4528
4529 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4530 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4531 if ( mData->mMachineState == MachineState_Paused
4532 && strReconfig == "1")
4533 fSilent = true;
4534
4535 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4536 bool fHotplug = false;
4537 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4538 fHotplug = true;
4539
4540 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4541 return setError(VBOX_E_INVALID_VM_STATE,
4542 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4543 aName.c_str());
4544
4545 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4546 aName,
4547 aControllerPort,
4548 aDevice);
4549 if (!pAttach)
4550 return setError(VBOX_E_OBJECT_NOT_FOUND,
4551 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4552 aDevice, aControllerPort, aName.c_str());
4553
4554
4555 i_setModified(IsModified_Storage);
4556 mMediumAttachments.backup();
4557
4558 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4559
4560 if (pAttach->i_getType() != DeviceType_DVD)
4561 return setError(E_INVALIDARG,
4562 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4563 aDevice, aControllerPort, aName.c_str());
4564 pAttach->i_updatePassthrough(!!aPassthrough);
4565
4566 attLock.release();
4567 alock.release();
4568 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4569
4570 return rc;
4571}
4572
4573HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4574 LONG aDevice, BOOL aTemporaryEject)
4575{
4576
4577 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4578 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4579
4580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4581
4582 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4583 if (FAILED(rc)) return rc;
4584
4585 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4586 aName,
4587 aControllerPort,
4588 aDevice);
4589 if (!pAttach)
4590 return setError(VBOX_E_OBJECT_NOT_FOUND,
4591 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4592 aDevice, aControllerPort, aName.c_str());
4593
4594
4595 i_setModified(IsModified_Storage);
4596 mMediumAttachments.backup();
4597
4598 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4599
4600 if (pAttach->i_getType() != DeviceType_DVD)
4601 return setError(E_INVALIDARG,
4602 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4603 aDevice, aControllerPort, aName.c_str());
4604 pAttach->i_updateTempEject(!!aTemporaryEject);
4605
4606 return S_OK;
4607}
4608
4609HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4610 LONG aDevice, BOOL aNonRotational)
4611{
4612
4613 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4614 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4615
4616 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4617
4618 HRESULT rc = i_checkStateDependency(MutableStateDep);
4619 if (FAILED(rc)) return rc;
4620
4621 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4622
4623 if (Global::IsOnlineOrTransient(mData->mMachineState))
4624 return setError(VBOX_E_INVALID_VM_STATE,
4625 tr("Invalid machine state: %s"),
4626 Global::stringifyMachineState(mData->mMachineState));
4627
4628 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4629 aName,
4630 aControllerPort,
4631 aDevice);
4632 if (!pAttach)
4633 return setError(VBOX_E_OBJECT_NOT_FOUND,
4634 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4635 aDevice, aControllerPort, aName.c_str());
4636
4637
4638 i_setModified(IsModified_Storage);
4639 mMediumAttachments.backup();
4640
4641 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4642
4643 if (pAttach->i_getType() != DeviceType_HardDisk)
4644 return setError(E_INVALIDARG,
4645 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"),
4646 aDevice, aControllerPort, aName.c_str());
4647 pAttach->i_updateNonRotational(!!aNonRotational);
4648
4649 return S_OK;
4650}
4651
4652HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4653 LONG aDevice, BOOL aDiscard)
4654{
4655
4656 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4657 aName.c_str(), aControllerPort, aDevice, aDiscard));
4658
4659 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4660
4661 HRESULT rc = i_checkStateDependency(MutableStateDep);
4662 if (FAILED(rc)) return rc;
4663
4664 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4665
4666 if (Global::IsOnlineOrTransient(mData->mMachineState))
4667 return setError(VBOX_E_INVALID_VM_STATE,
4668 tr("Invalid machine state: %s"),
4669 Global::stringifyMachineState(mData->mMachineState));
4670
4671 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4672 aName,
4673 aControllerPort,
4674 aDevice);
4675 if (!pAttach)
4676 return setError(VBOX_E_OBJECT_NOT_FOUND,
4677 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4678 aDevice, aControllerPort, aName.c_str());
4679
4680
4681 i_setModified(IsModified_Storage);
4682 mMediumAttachments.backup();
4683
4684 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4685
4686 if (pAttach->i_getType() != DeviceType_HardDisk)
4687 return setError(E_INVALIDARG,
4688 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"),
4689 aDevice, aControllerPort, aName.c_str());
4690 pAttach->i_updateDiscard(!!aDiscard);
4691
4692 return S_OK;
4693}
4694
4695HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4696 LONG aDevice, BOOL aHotPluggable)
4697{
4698 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4699 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4700
4701 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 HRESULT rc = i_checkStateDependency(MutableStateDep);
4704 if (FAILED(rc)) return rc;
4705
4706 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4707
4708 if (Global::IsOnlineOrTransient(mData->mMachineState))
4709 return setError(VBOX_E_INVALID_VM_STATE,
4710 tr("Invalid machine state: %s"),
4711 Global::stringifyMachineState(mData->mMachineState));
4712
4713 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4714 aName,
4715 aControllerPort,
4716 aDevice);
4717 if (!pAttach)
4718 return setError(VBOX_E_OBJECT_NOT_FOUND,
4719 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4720 aDevice, aControllerPort, aName.c_str());
4721
4722 /* Check for an existing controller. */
4723 ComObjPtr<StorageController> ctl;
4724 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4725 if (FAILED(rc)) return rc;
4726
4727 StorageControllerType_T ctrlType;
4728 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4729 if (FAILED(rc))
4730 return setError(E_FAIL,
4731 tr("Could not get type of controller '%s'"),
4732 aName.c_str());
4733
4734 if (!i_isControllerHotplugCapable(ctrlType))
4735 return setError(VBOX_E_NOT_SUPPORTED,
4736 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4737 aName.c_str());
4738
4739 i_setModified(IsModified_Storage);
4740 mMediumAttachments.backup();
4741
4742 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4743
4744 if (pAttach->i_getType() == DeviceType_Floppy)
4745 return setError(E_INVALIDARG,
4746 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"),
4747 aDevice, aControllerPort, aName.c_str());
4748 pAttach->i_updateHotPluggable(!!aHotPluggable);
4749
4750 return S_OK;
4751}
4752
4753HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4754 LONG aDevice)
4755{
4756 int rc = S_OK;
4757 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4758 aName.c_str(), aControllerPort, aDevice));
4759
4760 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4761
4762 return rc;
4763}
4764
4765HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4766 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4767{
4768 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4769 aName.c_str(), aControllerPort, aDevice));
4770
4771 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4772
4773 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4774 if (FAILED(rc)) return rc;
4775
4776 if (Global::IsOnlineOrTransient(mData->mMachineState))
4777 return setError(VBOX_E_INVALID_VM_STATE,
4778 tr("Invalid machine state: %s"),
4779 Global::stringifyMachineState(mData->mMachineState));
4780
4781 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4782 aName,
4783 aControllerPort,
4784 aDevice);
4785 if (!pAttach)
4786 return setError(VBOX_E_OBJECT_NOT_FOUND,
4787 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4788 aDevice, aControllerPort, aName.c_str());
4789
4790
4791 i_setModified(IsModified_Storage);
4792 mMediumAttachments.backup();
4793
4794 IBandwidthGroup *iB = aBandwidthGroup;
4795 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4796 if (aBandwidthGroup && group.isNull())
4797 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4798
4799 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4800
4801 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4802 if (strBandwidthGroupOld.isNotEmpty())
4803 {
4804 /* Get the bandwidth group object and release it - this must not fail. */
4805 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4806 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4807 Assert(SUCCEEDED(rc));
4808
4809 pBandwidthGroupOld->i_release();
4810 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4811 }
4812
4813 if (!group.isNull())
4814 {
4815 group->i_reference();
4816 pAttach->i_updateBandwidthGroup(group->i_getName());
4817 }
4818
4819 return S_OK;
4820}
4821
4822HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4823 LONG aControllerPort,
4824 LONG aDevice,
4825 DeviceType_T aType)
4826{
4827 HRESULT rc = S_OK;
4828
4829 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4830 aName.c_str(), aControllerPort, aDevice, aType));
4831
4832 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4833
4834 return rc;
4835}
4836
4837
4838HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4839 LONG aControllerPort,
4840 LONG aDevice,
4841 BOOL aForce)
4842{
4843 int rc = S_OK;
4844 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4845 aName.c_str(), aControllerPort, aForce));
4846
4847 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4848
4849 return rc;
4850}
4851
4852HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4853 LONG aControllerPort,
4854 LONG aDevice,
4855 const ComPtr<IMedium> &aMedium,
4856 BOOL aForce)
4857{
4858 int rc = S_OK;
4859 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4860 aName.c_str(), aControllerPort, aDevice, aForce));
4861
4862 // request the host lock first, since might be calling Host methods for getting host drives;
4863 // next, protect the media tree all the while we're in here, as well as our member variables
4864 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4865 this->lockHandle(),
4866 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4867
4868 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4869 aName,
4870 aControllerPort,
4871 aDevice);
4872 if (pAttach.isNull())
4873 return setError(VBOX_E_OBJECT_NOT_FOUND,
4874 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4875 aDevice, aControllerPort, aName.c_str());
4876
4877 /* Remember previously mounted medium. The medium before taking the
4878 * backup is not necessarily the same thing. */
4879 ComObjPtr<Medium> oldmedium;
4880 oldmedium = pAttach->i_getMedium();
4881
4882 IMedium *iM = aMedium;
4883 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4884 if (aMedium && pMedium.isNull())
4885 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4886
4887 AutoCaller mediumCaller(pMedium);
4888 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4889
4890 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4891 if (pMedium)
4892 {
4893 DeviceType_T mediumType = pAttach->i_getType();
4894 switch (mediumType)
4895 {
4896 case DeviceType_DVD:
4897 case DeviceType_Floppy:
4898 break;
4899
4900 default:
4901 return setError(VBOX_E_INVALID_OBJECT_STATE,
4902 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4903 aControllerPort,
4904 aDevice,
4905 aName.c_str());
4906 }
4907 }
4908
4909 i_setModified(IsModified_Storage);
4910 mMediumAttachments.backup();
4911
4912 {
4913 // The backup operation makes the pAttach reference point to the
4914 // old settings. Re-get the correct reference.
4915 pAttach = i_findAttachment(*mMediumAttachments.data(),
4916 aName,
4917 aControllerPort,
4918 aDevice);
4919 if (!oldmedium.isNull())
4920 oldmedium->i_removeBackReference(mData->mUuid);
4921 if (!pMedium.isNull())
4922 {
4923 pMedium->i_addBackReference(mData->mUuid);
4924
4925 mediumLock.release();
4926 multiLock.release();
4927 i_addMediumToRegistry(pMedium);
4928 multiLock.acquire();
4929 mediumLock.acquire();
4930 }
4931
4932 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4933 pAttach->i_updateMedium(pMedium);
4934 }
4935
4936 i_setModified(IsModified_Storage);
4937
4938 mediumLock.release();
4939 multiLock.release();
4940 rc = i_onMediumChange(pAttach, aForce);
4941 multiLock.acquire();
4942 mediumLock.acquire();
4943
4944 /* On error roll back this change only. */
4945 if (FAILED(rc))
4946 {
4947 if (!pMedium.isNull())
4948 pMedium->i_removeBackReference(mData->mUuid);
4949 pAttach = i_findAttachment(*mMediumAttachments.data(),
4950 aName,
4951 aControllerPort,
4952 aDevice);
4953 /* If the attachment is gone in the meantime, bail out. */
4954 if (pAttach.isNull())
4955 return rc;
4956 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4957 if (!oldmedium.isNull())
4958 oldmedium->i_addBackReference(mData->mUuid);
4959 pAttach->i_updateMedium(oldmedium);
4960 }
4961
4962 mediumLock.release();
4963 multiLock.release();
4964
4965 /* Save modified registries, but skip this machine as it's the caller's
4966 * job to save its settings like all other settings changes. */
4967 mParent->i_unmarkRegistryModified(i_getId());
4968 mParent->i_saveModifiedRegistries();
4969
4970 return rc;
4971}
4972HRESULT Machine::getMedium(const com::Utf8Str &aName,
4973 LONG aControllerPort,
4974 LONG aDevice,
4975 ComPtr<IMedium> &aMedium)
4976{
4977 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4978 aName.c_str(), aControllerPort, aDevice));
4979
4980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4981
4982 aMedium = NULL;
4983
4984 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4985 aName,
4986 aControllerPort,
4987 aDevice);
4988 if (pAttach.isNull())
4989 return setError(VBOX_E_OBJECT_NOT_FOUND,
4990 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4991 aDevice, aControllerPort, aName.c_str());
4992
4993 aMedium = pAttach->i_getMedium();
4994
4995 return S_OK;
4996}
4997
4998HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4999{
5000
5001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5002
5003 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5004
5005 return S_OK;
5006}
5007
5008HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
5009{
5010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5011
5012 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
5013
5014 return S_OK;
5015}
5016
5017HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
5018{
5019 /* Do not assert if slot is out of range, just return the advertised
5020 status. testdriver/vbox.py triggers this in logVmInfo. */
5021 if (aSlot >= mNetworkAdapters.size())
5022 return setError(E_INVALIDARG,
5023 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
5024 aSlot, mNetworkAdapters.size());
5025
5026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5027
5028 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
5029
5030 return S_OK;
5031}
5032
5033HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
5034{
5035 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5036
5037 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
5038 size_t i = 0;
5039 for (settings::StringsMap::const_iterator
5040 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
5041 it != mData->pMachineConfigFile->mapExtraDataItems.end();
5042 ++it, ++i)
5043 aKeys[i] = it->first;
5044
5045 return S_OK;
5046}
5047
5048 /**
5049 * @note Locks this object for reading.
5050 */
5051HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
5052 com::Utf8Str &aValue)
5053{
5054 /* start with nothing found */
5055 aValue = "";
5056
5057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5058
5059 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5060 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5061 // found:
5062 aValue = it->second; // source is a Utf8Str
5063
5064 /* return the result to caller (may be empty) */
5065 return S_OK;
5066}
5067
5068 /**
5069 * @note Locks mParent for writing + this object for writing.
5070 */
5071HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
5072{
5073 Utf8Str strOldValue; // empty
5074
5075 // locking note: we only hold the read lock briefly to look up the old value,
5076 // then release it and call the onExtraCanChange callbacks. There is a small
5077 // chance of a race insofar as the callback might be called twice if two callers
5078 // change the same key at the same time, but that's a much better solution
5079 // than the deadlock we had here before. The actual changing of the extradata
5080 // is then performed under the write lock and race-free.
5081
5082 // look up the old value first; if nothing has changed then we need not do anything
5083 {
5084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
5085
5086 // For snapshots don't even think about allowing changes, extradata
5087 // is global for a machine, so there is nothing snapshot specific.
5088 if (i_isSnapshotMachine())
5089 return setError(VBOX_E_INVALID_VM_STATE,
5090 tr("Cannot set extradata for a snapshot"));
5091
5092 // check if the right IMachine instance is used
5093 if (mData->mRegistered && !i_isSessionMachine())
5094 return setError(VBOX_E_INVALID_VM_STATE,
5095 tr("Cannot set extradata for an immutable machine"));
5096
5097 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
5098 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
5099 strOldValue = it->second;
5100 }
5101
5102 bool fChanged;
5103 if ((fChanged = (strOldValue != aValue)))
5104 {
5105 // ask for permission from all listeners outside the locks;
5106 // i_onExtraDataCanChange() only briefly requests the VirtualBox
5107 // lock to copy the list of callbacks to invoke
5108 Bstr error;
5109 Bstr bstrValue(aValue);
5110
5111 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
5112 {
5113 const char *sep = error.isEmpty() ? "" : ": ";
5114 CBSTR err = error.raw();
5115 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
5116 return setError(E_ACCESSDENIED,
5117 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
5118 aKey.c_str(),
5119 aValue.c_str(),
5120 sep,
5121 err);
5122 }
5123
5124 // data is changing and change not vetoed: then write it out under the lock
5125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5126
5127 if (aValue.isEmpty())
5128 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
5129 else
5130 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
5131 // creates a new key if needed
5132
5133 bool fNeedsGlobalSaveSettings = false;
5134 // This saving of settings is tricky: there is no "old state" for the
5135 // extradata items at all (unlike all other settings), so the old/new
5136 // settings comparison would give a wrong result!
5137 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
5138
5139 if (fNeedsGlobalSaveSettings)
5140 {
5141 // save the global settings; for that we should hold only the VirtualBox lock
5142 alock.release();
5143 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
5144 mParent->i_saveSettings();
5145 }
5146 }
5147
5148 // fire notification outside the lock
5149 if (fChanged)
5150 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
5151
5152 return S_OK;
5153}
5154
5155HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
5156{
5157 aProgress = NULL;
5158 NOREF(aSettingsFilePath);
5159 ReturnComNotImplemented();
5160}
5161
5162HRESULT Machine::saveSettings()
5163{
5164 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
5165
5166 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5167 if (FAILED(rc)) return rc;
5168
5169 /* the settings file path may never be null */
5170 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5171
5172 /* save all VM data excluding snapshots */
5173 bool fNeedsGlobalSaveSettings = false;
5174 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5175 mlock.release();
5176
5177 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5178 {
5179 // save the global settings; for that we should hold only the VirtualBox lock
5180 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5181 rc = mParent->i_saveSettings();
5182 }
5183
5184 return rc;
5185}
5186
5187
5188HRESULT Machine::discardSettings()
5189{
5190 /*
5191 * We need to take the machine list lock here as well as the machine one
5192 * or we'll get into trouble should any media stuff require rolling back.
5193 *
5194 * Details:
5195 *
5196 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
5197 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
5198 * 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]
5199 * 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
5200 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
5201 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
5202 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
5203 * 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
5204 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
5205 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
5206 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
5207 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
5208 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
5209 * 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]
5210 * 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] (*)
5211 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
5212 * 0:005> k
5213 * # Child-SP RetAddr Call Site
5214 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
5215 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
5216 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
5217 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
5218 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
5219 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
5220 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
5221 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
5222 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
5223 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
5224 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
5225 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
5226 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
5227 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5228 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5229 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5230 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5231 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5232 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5233 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5234 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5235 *
5236 */
5237 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5239
5240 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5241 if (FAILED(rc)) return rc;
5242
5243 /*
5244 * during this rollback, the session will be notified if data has
5245 * been actually changed
5246 */
5247 i_rollback(true /* aNotify */);
5248
5249 return S_OK;
5250}
5251
5252/** @note Locks objects! */
5253HRESULT Machine::unregister(AutoCaller &autoCaller,
5254 CleanupMode_T aCleanupMode,
5255 std::vector<ComPtr<IMedium> > &aMedia)
5256{
5257 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5258
5259 Guid id(i_getId());
5260
5261 if (mData->mSession.mState != SessionState_Unlocked)
5262 return setError(VBOX_E_INVALID_OBJECT_STATE,
5263 tr("Cannot unregister the machine '%s' while it is locked"),
5264 mUserData->s.strName.c_str());
5265
5266 // wait for state dependents to drop to zero
5267 i_ensureNoStateDependencies();
5268
5269 if (!mData->mAccessible)
5270 {
5271 // inaccessible maschines can only be unregistered; uninitialize ourselves
5272 // here because currently there may be no unregistered that are inaccessible
5273 // (this state combination is not supported). Note releasing the caller and
5274 // leaving the lock before calling uninit()
5275 alock.release();
5276 autoCaller.release();
5277
5278 uninit();
5279
5280 mParent->i_unregisterMachine(this, id);
5281 // calls VirtualBox::i_saveSettings()
5282
5283 return S_OK;
5284 }
5285
5286 HRESULT rc = S_OK;
5287
5288 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5289 // discard saved state
5290 if (mData->mMachineState == MachineState_Saved)
5291 {
5292 // add the saved state file to the list of files the caller should delete
5293 Assert(!mSSData->strStateFilePath.isEmpty());
5294 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5295
5296 mSSData->strStateFilePath.setNull();
5297
5298 // unconditionally set the machine state to powered off, we now
5299 // know no session has locked the machine
5300 mData->mMachineState = MachineState_PoweredOff;
5301 }
5302
5303 size_t cSnapshots = 0;
5304 if (mData->mFirstSnapshot)
5305 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5306 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5307 // fail now before we start detaching media
5308 return setError(VBOX_E_INVALID_OBJECT_STATE,
5309 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5310 mUserData->s.strName.c_str(), cSnapshots);
5311
5312 // This list collects the medium objects from all medium attachments
5313 // which we will detach from the machine and its snapshots, in a specific
5314 // order which allows for closing all media without getting "media in use"
5315 // errors, simply by going through the list from the front to the back:
5316 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5317 // and must be closed before the parent media from the snapshots, or closing the parents
5318 // will fail because they still have children);
5319 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5320 // the root ("first") snapshot of the machine.
5321 MediaList llMedia;
5322
5323 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5324 && mMediumAttachments->size()
5325 )
5326 {
5327 // we have media attachments: detach them all and add the Medium objects to our list
5328 if (aCleanupMode != CleanupMode_UnregisterOnly)
5329 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5330 else
5331 return setError(VBOX_E_INVALID_OBJECT_STATE,
5332 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5333 mUserData->s.strName.c_str(), mMediumAttachments->size());
5334 }
5335
5336 if (cSnapshots)
5337 {
5338 // add the media from the medium attachments of the snapshots to llMedia
5339 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5340 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5341 // into the children first
5342
5343 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5344 MachineState_T oldState = mData->mMachineState;
5345 mData->mMachineState = MachineState_DeletingSnapshot;
5346
5347 // make a copy of the first snapshot so the refcount does not drop to 0
5348 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5349 // because of the AutoCaller voodoo)
5350 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5351
5352 // GO!
5353 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5354
5355 mData->mMachineState = oldState;
5356 }
5357
5358 if (FAILED(rc))
5359 {
5360 i_rollbackMedia();
5361 return rc;
5362 }
5363
5364 // commit all the media changes made above
5365 i_commitMedia();
5366
5367 mData->mRegistered = false;
5368
5369 // machine lock no longer needed
5370 alock.release();
5371
5372 // return media to caller
5373 aMedia.resize(llMedia.size());
5374 size_t i = 0;
5375 for (MediaList::const_iterator
5376 it = llMedia.begin();
5377 it != llMedia.end();
5378 ++it, ++i)
5379 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5380
5381 mParent->i_unregisterMachine(this, id);
5382 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5383
5384 return S_OK;
5385}
5386
5387/**
5388 * Task record for deleting a machine config.
5389 */
5390class Machine::DeleteConfigTask
5391 : public Machine::Task
5392{
5393public:
5394 DeleteConfigTask(Machine *m,
5395 Progress *p,
5396 const Utf8Str &t,
5397 const RTCList<ComPtr<IMedium> > &llMediums,
5398 const StringsList &llFilesToDelete)
5399 : Task(m, p, t),
5400 m_llMediums(llMediums),
5401 m_llFilesToDelete(llFilesToDelete)
5402 {}
5403
5404private:
5405 void handler()
5406 {
5407 try
5408 {
5409 m_pMachine->i_deleteConfigHandler(*this);
5410 }
5411 catch (...)
5412 {
5413 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5414 }
5415 }
5416
5417 RTCList<ComPtr<IMedium> > m_llMediums;
5418 StringsList m_llFilesToDelete;
5419
5420 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5421};
5422
5423/**
5424 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5425 * SessionMachine::taskHandler().
5426 *
5427 * @note Locks this object for writing.
5428 *
5429 * @param task
5430 * @return
5431 */
5432void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5433{
5434 LogFlowThisFuncEnter();
5435
5436 AutoCaller autoCaller(this);
5437 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5438 if (FAILED(autoCaller.rc()))
5439 {
5440 /* we might have been uninitialized because the session was accidentally
5441 * closed by the client, so don't assert */
5442 HRESULT rc = setError(E_FAIL,
5443 tr("The session has been accidentally closed"));
5444 task.m_pProgress->i_notifyComplete(rc);
5445 LogFlowThisFuncLeave();
5446 return;
5447 }
5448
5449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5450
5451 HRESULT rc = S_OK;
5452
5453 try
5454 {
5455 ULONG uLogHistoryCount = 3;
5456 ComPtr<ISystemProperties> systemProperties;
5457 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5458 if (FAILED(rc)) throw rc;
5459
5460 if (!systemProperties.isNull())
5461 {
5462 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5463 if (FAILED(rc)) throw rc;
5464 }
5465
5466 MachineState_T oldState = mData->mMachineState;
5467 i_setMachineState(MachineState_SettingUp);
5468 alock.release();
5469 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5470 {
5471 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5472 {
5473 AutoCaller mac(pMedium);
5474 if (FAILED(mac.rc())) throw mac.rc();
5475 Utf8Str strLocation = pMedium->i_getLocationFull();
5476 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5477 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5478 if (FAILED(rc)) throw rc;
5479 }
5480 if (pMedium->i_isMediumFormatFile())
5481 {
5482 ComPtr<IProgress> pProgress2;
5483 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5484 if (FAILED(rc)) throw rc;
5485 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5486 if (FAILED(rc)) throw rc;
5487 /* Check the result of the asynchronous process. */
5488 LONG iRc;
5489 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5490 if (FAILED(rc)) throw rc;
5491 /* If the thread of the progress object has an error, then
5492 * retrieve the error info from there, or it'll be lost. */
5493 if (FAILED(iRc))
5494 throw setError(ProgressErrorInfo(pProgress2));
5495 }
5496
5497 /* Close the medium, deliberately without checking the return
5498 * code, and without leaving any trace in the error info, as
5499 * a failure here is a very minor issue, which shouldn't happen
5500 * as above we even managed to delete the medium. */
5501 {
5502 ErrorInfoKeeper eik;
5503 pMedium->Close();
5504 }
5505 }
5506 i_setMachineState(oldState);
5507 alock.acquire();
5508
5509 // delete the files pushed on the task list by Machine::Delete()
5510 // (this includes saved states of the machine and snapshots and
5511 // medium storage files from the IMedium list passed in, and the
5512 // machine XML file)
5513 for (StringsList::const_iterator
5514 it = task.m_llFilesToDelete.begin();
5515 it != task.m_llFilesToDelete.end();
5516 ++it)
5517 {
5518 const Utf8Str &strFile = *it;
5519 LogFunc(("Deleting file %s\n", strFile.c_str()));
5520 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5521 if (FAILED(rc)) throw rc;
5522
5523 int vrc = RTFileDelete(strFile.c_str());
5524 if (RT_FAILURE(vrc))
5525 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5526 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5527 }
5528
5529 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5530 if (FAILED(rc)) throw rc;
5531
5532 /* delete the settings only when the file actually exists */
5533 if (mData->pMachineConfigFile->fileExists())
5534 {
5535 /* Delete any backup or uncommitted XML files. Ignore failures.
5536 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5537 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5538 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5539 RTFileDelete(otherXml.c_str());
5540 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5541 RTFileDelete(otherXml.c_str());
5542
5543 /* delete the Logs folder, nothing important should be left
5544 * there (we don't check for errors because the user might have
5545 * some private files there that we don't want to delete) */
5546 Utf8Str logFolder;
5547 getLogFolder(logFolder);
5548 Assert(logFolder.length());
5549 if (RTDirExists(logFolder.c_str()))
5550 {
5551 /* Delete all VBox.log[.N] files from the Logs folder
5552 * (this must be in sync with the rotation logic in
5553 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5554 * files that may have been created by the GUI. */
5555 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5556 logFolder.c_str(), RTPATH_DELIMITER);
5557 RTFileDelete(log.c_str());
5558 log = Utf8StrFmt("%s%cVBox.png",
5559 logFolder.c_str(), RTPATH_DELIMITER);
5560 RTFileDelete(log.c_str());
5561 for (int i = uLogHistoryCount; i > 0; i--)
5562 {
5563 log = Utf8StrFmt("%s%cVBox.log.%d",
5564 logFolder.c_str(), RTPATH_DELIMITER, i);
5565 RTFileDelete(log.c_str());
5566 log = Utf8StrFmt("%s%cVBox.png.%d",
5567 logFolder.c_str(), RTPATH_DELIMITER, i);
5568 RTFileDelete(log.c_str());
5569 }
5570#if defined(RT_OS_WINDOWS)
5571 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5572 RTFileDelete(log.c_str());
5573 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5574 RTFileDelete(log.c_str());
5575#endif
5576
5577 RTDirRemove(logFolder.c_str());
5578 }
5579
5580 /* delete the Snapshots folder, nothing important should be left
5581 * there (we don't check for errors because the user might have
5582 * some private files there that we don't want to delete) */
5583 Utf8Str strFullSnapshotFolder;
5584 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5585 Assert(!strFullSnapshotFolder.isEmpty());
5586 if (RTDirExists(strFullSnapshotFolder.c_str()))
5587 RTDirRemove(strFullSnapshotFolder.c_str());
5588
5589 // delete the directory that contains the settings file, but only
5590 // if it matches the VM name
5591 Utf8Str settingsDir;
5592 if (i_isInOwnDir(&settingsDir))
5593 RTDirRemove(settingsDir.c_str());
5594 }
5595
5596 alock.release();
5597
5598 mParent->i_saveModifiedRegistries();
5599 }
5600 catch (HRESULT aRC) { rc = aRC; }
5601
5602 task.m_pProgress->i_notifyComplete(rc);
5603
5604 LogFlowThisFuncLeave();
5605}
5606
5607HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5608{
5609 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5610
5611 HRESULT rc = i_checkStateDependency(MutableStateDep);
5612 if (FAILED(rc)) return rc;
5613
5614 if (mData->mRegistered)
5615 return setError(VBOX_E_INVALID_VM_STATE,
5616 tr("Cannot delete settings of a registered machine"));
5617
5618 // collect files to delete
5619 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5620 if (mData->pMachineConfigFile->fileExists())
5621 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5622
5623 RTCList<ComPtr<IMedium> > llMediums;
5624 for (size_t i = 0; i < aMedia.size(); ++i)
5625 {
5626 IMedium *pIMedium(aMedia[i]);
5627 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5628 if (pMedium.isNull())
5629 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5630 SafeArray<BSTR> ids;
5631 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5632 if (FAILED(rc)) return rc;
5633 /* At this point the medium should not have any back references
5634 * anymore. If it has it is attached to another VM and *must* not
5635 * deleted. */
5636 if (ids.size() < 1)
5637 llMediums.append(pMedium);
5638 }
5639
5640 ComObjPtr<Progress> pProgress;
5641 pProgress.createObject();
5642 rc = pProgress->init(i_getVirtualBox(),
5643 static_cast<IMachine*>(this) /* aInitiator */,
5644 tr("Deleting files"),
5645 true /* fCancellable */,
5646 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5647 tr("Collecting file inventory"));
5648 if (FAILED(rc))
5649 return rc;
5650
5651 /* create and start the task on a separate thread (note that it will not
5652 * start working until we release alock) */
5653 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5654 rc = pTask->createThread();
5655 if (FAILED(rc))
5656 return rc;
5657
5658 pProgress.queryInterfaceTo(aProgress.asOutParam());
5659
5660 LogFlowFuncLeave();
5661
5662 return S_OK;
5663}
5664
5665HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5666{
5667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5668
5669 ComObjPtr<Snapshot> pSnapshot;
5670 HRESULT rc;
5671
5672 if (aNameOrId.isEmpty())
5673 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5674 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5675 else
5676 {
5677 Guid uuid(aNameOrId);
5678 if (uuid.isValid())
5679 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5680 else
5681 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5682 }
5683 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5684
5685 return rc;
5686}
5687
5688HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5689{
5690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5691
5692 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5693 if (FAILED(rc)) return rc;
5694
5695 ComObjPtr<SharedFolder> sharedFolder;
5696 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5697 if (SUCCEEDED(rc))
5698 return setError(VBOX_E_OBJECT_IN_USE,
5699 tr("Shared folder named '%s' already exists"),
5700 aName.c_str());
5701
5702 sharedFolder.createObject();
5703 rc = sharedFolder->init(i_getMachine(),
5704 aName,
5705 aHostPath,
5706 !!aWritable,
5707 !!aAutomount,
5708 true /* fFailOnError */);
5709 if (FAILED(rc)) return rc;
5710
5711 i_setModified(IsModified_SharedFolders);
5712 mHWData.backup();
5713 mHWData->mSharedFolders.push_back(sharedFolder);
5714
5715 /* inform the direct session if any */
5716 alock.release();
5717 i_onSharedFolderChange();
5718
5719 return S_OK;
5720}
5721
5722HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5723{
5724 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5725
5726 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5727 if (FAILED(rc)) return rc;
5728
5729 ComObjPtr<SharedFolder> sharedFolder;
5730 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5731 if (FAILED(rc)) return rc;
5732
5733 i_setModified(IsModified_SharedFolders);
5734 mHWData.backup();
5735 mHWData->mSharedFolders.remove(sharedFolder);
5736
5737 /* inform the direct session if any */
5738 alock.release();
5739 i_onSharedFolderChange();
5740
5741 return S_OK;
5742}
5743
5744HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5745{
5746 /* start with No */
5747 *aCanShow = FALSE;
5748
5749 ComPtr<IInternalSessionControl> directControl;
5750 {
5751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5752
5753 if (mData->mSession.mState != SessionState_Locked)
5754 return setError(VBOX_E_INVALID_VM_STATE,
5755 tr("Machine is not locked for session (session state: %s)"),
5756 Global::stringifySessionState(mData->mSession.mState));
5757
5758 if (mData->mSession.mLockType == LockType_VM)
5759 directControl = mData->mSession.mDirectControl;
5760 }
5761
5762 /* ignore calls made after #OnSessionEnd() is called */
5763 if (!directControl)
5764 return S_OK;
5765
5766 LONG64 dummy;
5767 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5768}
5769
5770HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5771{
5772 ComPtr<IInternalSessionControl> directControl;
5773 {
5774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5775
5776 if (mData->mSession.mState != SessionState_Locked)
5777 return setError(E_FAIL,
5778 tr("Machine is not locked for session (session state: %s)"),
5779 Global::stringifySessionState(mData->mSession.mState));
5780
5781 if (mData->mSession.mLockType == LockType_VM)
5782 directControl = mData->mSession.mDirectControl;
5783 }
5784
5785 /* ignore calls made after #OnSessionEnd() is called */
5786 if (!directControl)
5787 return S_OK;
5788
5789 BOOL dummy;
5790 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5791}
5792
5793#ifdef VBOX_WITH_GUEST_PROPS
5794/**
5795 * Look up a guest property in VBoxSVC's internal structures.
5796 */
5797HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5798 com::Utf8Str &aValue,
5799 LONG64 *aTimestamp,
5800 com::Utf8Str &aFlags) const
5801{
5802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5803
5804 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5805 if (it != mHWData->mGuestProperties.end())
5806 {
5807 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5808 aValue = it->second.strValue;
5809 *aTimestamp = it->second.mTimestamp;
5810 GuestPropWriteFlags(it->second.mFlags, szFlags);
5811 aFlags = Utf8Str(szFlags);
5812 }
5813
5814 return S_OK;
5815}
5816
5817/**
5818 * Query the VM that a guest property belongs to for the property.
5819 * @returns E_ACCESSDENIED if the VM process is not available or not
5820 * currently handling queries and the lookup should then be done in
5821 * VBoxSVC.
5822 */
5823HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5824 com::Utf8Str &aValue,
5825 LONG64 *aTimestamp,
5826 com::Utf8Str &aFlags) const
5827{
5828 HRESULT rc = S_OK;
5829 BSTR bValue = NULL;
5830 BSTR bFlags = NULL;
5831
5832 ComPtr<IInternalSessionControl> directControl;
5833 {
5834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5835 if (mData->mSession.mLockType == LockType_VM)
5836 directControl = mData->mSession.mDirectControl;
5837 }
5838
5839 /* ignore calls made after #OnSessionEnd() is called */
5840 if (!directControl)
5841 rc = E_ACCESSDENIED;
5842 else
5843 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5844 0 /* accessMode */,
5845 &bValue, aTimestamp, &bFlags);
5846
5847 aValue = bValue;
5848 aFlags = bFlags;
5849
5850 return rc;
5851}
5852#endif // VBOX_WITH_GUEST_PROPS
5853
5854HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5855 com::Utf8Str &aValue,
5856 LONG64 *aTimestamp,
5857 com::Utf8Str &aFlags)
5858{
5859#ifndef VBOX_WITH_GUEST_PROPS
5860 ReturnComNotImplemented();
5861#else // VBOX_WITH_GUEST_PROPS
5862
5863 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5864
5865 if (rc == E_ACCESSDENIED)
5866 /* The VM is not running or the service is not (yet) accessible */
5867 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5868 return rc;
5869#endif // VBOX_WITH_GUEST_PROPS
5870}
5871
5872HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5873{
5874 LONG64 dummyTimestamp;
5875 com::Utf8Str dummyFlags;
5876 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5877 return rc;
5878
5879}
5880HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5881{
5882 com::Utf8Str dummyFlags;
5883 com::Utf8Str dummyValue;
5884 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5885 return rc;
5886}
5887
5888#ifdef VBOX_WITH_GUEST_PROPS
5889/**
5890 * Set a guest property in VBoxSVC's internal structures.
5891 */
5892HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5893 const com::Utf8Str &aFlags, bool fDelete)
5894{
5895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5896 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5897 if (FAILED(rc)) return rc;
5898
5899 try
5900 {
5901 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5902 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5903 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5904
5905 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5906 if (it == mHWData->mGuestProperties.end())
5907 {
5908 if (!fDelete)
5909 {
5910 i_setModified(IsModified_MachineData);
5911 mHWData.backupEx();
5912
5913 RTTIMESPEC time;
5914 HWData::GuestProperty prop;
5915 prop.strValue = Bstr(aValue).raw();
5916 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5917 prop.mFlags = fFlags;
5918 mHWData->mGuestProperties[aName] = prop;
5919 }
5920 }
5921 else
5922 {
5923 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5924 {
5925 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5926 }
5927 else
5928 {
5929 i_setModified(IsModified_MachineData);
5930 mHWData.backupEx();
5931
5932 /* The backupEx() operation invalidates our iterator,
5933 * so get a new one. */
5934 it = mHWData->mGuestProperties.find(aName);
5935 Assert(it != mHWData->mGuestProperties.end());
5936
5937 if (!fDelete)
5938 {
5939 RTTIMESPEC time;
5940 it->second.strValue = aValue;
5941 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5942 it->second.mFlags = fFlags;
5943 }
5944 else
5945 mHWData->mGuestProperties.erase(it);
5946 }
5947 }
5948
5949 if (SUCCEEDED(rc))
5950 {
5951 alock.release();
5952
5953 mParent->i_onGuestPropertyChange(mData->mUuid,
5954 Bstr(aName).raw(),
5955 Bstr(aValue).raw(),
5956 Bstr(aFlags).raw());
5957 }
5958 }
5959 catch (std::bad_alloc &)
5960 {
5961 rc = E_OUTOFMEMORY;
5962 }
5963
5964 return rc;
5965}
5966
5967/**
5968 * Set a property on the VM that that property belongs to.
5969 * @returns E_ACCESSDENIED if the VM process is not available or not
5970 * currently handling queries and the setting should then be done in
5971 * VBoxSVC.
5972 */
5973HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5974 const com::Utf8Str &aFlags, bool fDelete)
5975{
5976 HRESULT rc;
5977
5978 try
5979 {
5980 ComPtr<IInternalSessionControl> directControl;
5981 {
5982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5983 if (mData->mSession.mLockType == LockType_VM)
5984 directControl = mData->mSession.mDirectControl;
5985 }
5986
5987 BSTR dummy = NULL; /* will not be changed (setter) */
5988 LONG64 dummy64;
5989 if (!directControl)
5990 rc = E_ACCESSDENIED;
5991 else
5992 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5993 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5994 fDelete? 2: 1 /* accessMode */,
5995 &dummy, &dummy64, &dummy);
5996 }
5997 catch (std::bad_alloc &)
5998 {
5999 rc = E_OUTOFMEMORY;
6000 }
6001
6002 return rc;
6003}
6004#endif // VBOX_WITH_GUEST_PROPS
6005
6006HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
6007 const com::Utf8Str &aFlags)
6008{
6009#ifndef VBOX_WITH_GUEST_PROPS
6010 ReturnComNotImplemented();
6011#else // VBOX_WITH_GUEST_PROPS
6012 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
6013 if (rc == E_ACCESSDENIED)
6014 /* The VM is not running or the service is not (yet) accessible */
6015 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
6016 return rc;
6017#endif // VBOX_WITH_GUEST_PROPS
6018}
6019
6020HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
6021{
6022 return setGuestProperty(aProperty, aValue, "");
6023}
6024
6025HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
6026{
6027#ifndef VBOX_WITH_GUEST_PROPS
6028 ReturnComNotImplemented();
6029#else // VBOX_WITH_GUEST_PROPS
6030 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
6031 if (rc == E_ACCESSDENIED)
6032 /* The VM is not running or the service is not (yet) accessible */
6033 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
6034 return rc;
6035#endif // VBOX_WITH_GUEST_PROPS
6036}
6037
6038#ifdef VBOX_WITH_GUEST_PROPS
6039/**
6040 * Enumerate the guest properties in VBoxSVC's internal structures.
6041 */
6042HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
6043 std::vector<com::Utf8Str> &aNames,
6044 std::vector<com::Utf8Str> &aValues,
6045 std::vector<LONG64> &aTimestamps,
6046 std::vector<com::Utf8Str> &aFlags)
6047{
6048 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6049 Utf8Str strPatterns(aPatterns);
6050
6051 /*
6052 * Look for matching patterns and build up a list.
6053 */
6054 HWData::GuestPropertyMap propMap;
6055 for (HWData::GuestPropertyMap::const_iterator
6056 it = mHWData->mGuestProperties.begin();
6057 it != mHWData->mGuestProperties.end();
6058 ++it)
6059 {
6060 if ( strPatterns.isEmpty()
6061 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
6062 RTSTR_MAX,
6063 it->first.c_str(),
6064 RTSTR_MAX,
6065 NULL)
6066 )
6067 propMap.insert(*it);
6068 }
6069
6070 alock.release();
6071
6072 /*
6073 * And build up the arrays for returning the property information.
6074 */
6075 size_t cEntries = propMap.size();
6076
6077 aNames.resize(cEntries);
6078 aValues.resize(cEntries);
6079 aTimestamps.resize(cEntries);
6080 aFlags.resize(cEntries);
6081
6082 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
6083 size_t i = 0;
6084 for (HWData::GuestPropertyMap::const_iterator
6085 it = propMap.begin();
6086 it != propMap.end();
6087 ++it, ++i)
6088 {
6089 aNames[i] = it->first;
6090 aValues[i] = it->second.strValue;
6091 aTimestamps[i] = it->second.mTimestamp;
6092 GuestPropWriteFlags(it->second.mFlags, szFlags);
6093 aFlags[i] = Utf8Str(szFlags);
6094 }
6095
6096 return S_OK;
6097}
6098
6099/**
6100 * Enumerate the properties managed by a VM.
6101 * @returns E_ACCESSDENIED if the VM process is not available or not
6102 * currently handling queries and the setting should then be done in
6103 * VBoxSVC.
6104 */
6105HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
6106 std::vector<com::Utf8Str> &aNames,
6107 std::vector<com::Utf8Str> &aValues,
6108 std::vector<LONG64> &aTimestamps,
6109 std::vector<com::Utf8Str> &aFlags)
6110{
6111 HRESULT rc;
6112 ComPtr<IInternalSessionControl> directControl;
6113 {
6114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6115 if (mData->mSession.mLockType == LockType_VM)
6116 directControl = mData->mSession.mDirectControl;
6117 }
6118
6119 com::SafeArray<BSTR> bNames;
6120 com::SafeArray<BSTR> bValues;
6121 com::SafeArray<LONG64> bTimestamps;
6122 com::SafeArray<BSTR> bFlags;
6123
6124 if (!directControl)
6125 rc = E_ACCESSDENIED;
6126 else
6127 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
6128 ComSafeArrayAsOutParam(bNames),
6129 ComSafeArrayAsOutParam(bValues),
6130 ComSafeArrayAsOutParam(bTimestamps),
6131 ComSafeArrayAsOutParam(bFlags));
6132 size_t i;
6133 aNames.resize(bNames.size());
6134 for (i = 0; i < bNames.size(); ++i)
6135 aNames[i] = Utf8Str(bNames[i]);
6136 aValues.resize(bValues.size());
6137 for (i = 0; i < bValues.size(); ++i)
6138 aValues[i] = Utf8Str(bValues[i]);
6139 aTimestamps.resize(bTimestamps.size());
6140 for (i = 0; i < bTimestamps.size(); ++i)
6141 aTimestamps[i] = bTimestamps[i];
6142 aFlags.resize(bFlags.size());
6143 for (i = 0; i < bFlags.size(); ++i)
6144 aFlags[i] = Utf8Str(bFlags[i]);
6145
6146 return rc;
6147}
6148#endif // VBOX_WITH_GUEST_PROPS
6149HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
6150 std::vector<com::Utf8Str> &aNames,
6151 std::vector<com::Utf8Str> &aValues,
6152 std::vector<LONG64> &aTimestamps,
6153 std::vector<com::Utf8Str> &aFlags)
6154{
6155#ifndef VBOX_WITH_GUEST_PROPS
6156 ReturnComNotImplemented();
6157#else // VBOX_WITH_GUEST_PROPS
6158
6159 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
6160
6161 if (rc == E_ACCESSDENIED)
6162 /* The VM is not running or the service is not (yet) accessible */
6163 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
6164 return rc;
6165#endif // VBOX_WITH_GUEST_PROPS
6166}
6167
6168HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
6169 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
6170{
6171 MediumAttachmentList atts;
6172
6173 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
6174 if (FAILED(rc)) return rc;
6175
6176 aMediumAttachments.resize(atts.size());
6177 size_t i = 0;
6178 for (MediumAttachmentList::const_iterator
6179 it = atts.begin();
6180 it != atts.end();
6181 ++it, ++i)
6182 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
6183
6184 return S_OK;
6185}
6186
6187HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
6188 LONG aControllerPort,
6189 LONG aDevice,
6190 ComPtr<IMediumAttachment> &aAttachment)
6191{
6192 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
6193 aName.c_str(), aControllerPort, aDevice));
6194
6195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6196
6197 aAttachment = NULL;
6198
6199 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
6200 aName,
6201 aControllerPort,
6202 aDevice);
6203 if (pAttach.isNull())
6204 return setError(VBOX_E_OBJECT_NOT_FOUND,
6205 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
6206 aDevice, aControllerPort, aName.c_str());
6207
6208 pAttach.queryInterfaceTo(aAttachment.asOutParam());
6209
6210 return S_OK;
6211}
6212
6213
6214HRESULT Machine::addStorageController(const com::Utf8Str &aName,
6215 StorageBus_T aConnectionType,
6216 ComPtr<IStorageController> &aController)
6217{
6218 if ( (aConnectionType <= StorageBus_Null)
6219 || (aConnectionType > StorageBus_PCIe))
6220 return setError(E_INVALIDARG,
6221 tr("Invalid connection type: %d"),
6222 aConnectionType);
6223
6224 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6225
6226 HRESULT rc = i_checkStateDependency(MutableStateDep);
6227 if (FAILED(rc)) return rc;
6228
6229 /* try to find one with the name first. */
6230 ComObjPtr<StorageController> ctrl;
6231
6232 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6233 if (SUCCEEDED(rc))
6234 return setError(VBOX_E_OBJECT_IN_USE,
6235 tr("Storage controller named '%s' already exists"),
6236 aName.c_str());
6237
6238 ctrl.createObject();
6239
6240 /* get a new instance number for the storage controller */
6241 ULONG ulInstance = 0;
6242 bool fBootable = true;
6243 for (StorageControllerList::const_iterator
6244 it = mStorageControllers->begin();
6245 it != mStorageControllers->end();
6246 ++it)
6247 {
6248 if ((*it)->i_getStorageBus() == aConnectionType)
6249 {
6250 ULONG ulCurInst = (*it)->i_getInstance();
6251
6252 if (ulCurInst >= ulInstance)
6253 ulInstance = ulCurInst + 1;
6254
6255 /* Only one controller of each type can be marked as bootable. */
6256 if ((*it)->i_getBootable())
6257 fBootable = false;
6258 }
6259 }
6260
6261 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6262 if (FAILED(rc)) return rc;
6263
6264 i_setModified(IsModified_Storage);
6265 mStorageControllers.backup();
6266 mStorageControllers->push_back(ctrl);
6267
6268 ctrl.queryInterfaceTo(aController.asOutParam());
6269
6270 /* inform the direct session if any */
6271 alock.release();
6272 i_onStorageControllerChange();
6273
6274 return S_OK;
6275}
6276
6277HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6278 ComPtr<IStorageController> &aStorageController)
6279{
6280 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6281
6282 ComObjPtr<StorageController> ctrl;
6283
6284 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6285 if (SUCCEEDED(rc))
6286 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6287
6288 return rc;
6289}
6290
6291HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6292 ULONG aInstance,
6293 ComPtr<IStorageController> &aStorageController)
6294{
6295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6296
6297 for (StorageControllerList::const_iterator
6298 it = mStorageControllers->begin();
6299 it != mStorageControllers->end();
6300 ++it)
6301 {
6302 if ( (*it)->i_getStorageBus() == aConnectionType
6303 && (*it)->i_getInstance() == aInstance)
6304 {
6305 (*it).queryInterfaceTo(aStorageController.asOutParam());
6306 return S_OK;
6307 }
6308 }
6309
6310 return setError(VBOX_E_OBJECT_NOT_FOUND,
6311 tr("Could not find a storage controller with instance number '%lu'"),
6312 aInstance);
6313}
6314
6315HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6316{
6317 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6318
6319 HRESULT rc = i_checkStateDependency(MutableStateDep);
6320 if (FAILED(rc)) return rc;
6321
6322 ComObjPtr<StorageController> ctrl;
6323
6324 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6325 if (SUCCEEDED(rc))
6326 {
6327 /* Ensure that only one controller of each type is marked as bootable. */
6328 if (aBootable == TRUE)
6329 {
6330 for (StorageControllerList::const_iterator
6331 it = mStorageControllers->begin();
6332 it != mStorageControllers->end();
6333 ++it)
6334 {
6335 ComObjPtr<StorageController> aCtrl = (*it);
6336
6337 if ( (aCtrl->i_getName() != aName)
6338 && aCtrl->i_getBootable() == TRUE
6339 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6340 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6341 {
6342 aCtrl->i_setBootable(FALSE);
6343 break;
6344 }
6345 }
6346 }
6347
6348 if (SUCCEEDED(rc))
6349 {
6350 ctrl->i_setBootable(aBootable);
6351 i_setModified(IsModified_Storage);
6352 }
6353 }
6354
6355 if (SUCCEEDED(rc))
6356 {
6357 /* inform the direct session if any */
6358 alock.release();
6359 i_onStorageControllerChange();
6360 }
6361
6362 return rc;
6363}
6364
6365HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6366{
6367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6368
6369 HRESULT rc = i_checkStateDependency(MutableStateDep);
6370 if (FAILED(rc)) return rc;
6371
6372 ComObjPtr<StorageController> ctrl;
6373 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6374 if (FAILED(rc)) return rc;
6375
6376 {
6377 /* find all attached devices to the appropriate storage controller and detach them all */
6378 // make a temporary list because detachDevice invalidates iterators into
6379 // mMediumAttachments
6380 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6381
6382 for (MediumAttachmentList::const_iterator
6383 it = llAttachments2.begin();
6384 it != llAttachments2.end();
6385 ++it)
6386 {
6387 MediumAttachment *pAttachTemp = *it;
6388
6389 AutoCaller localAutoCaller(pAttachTemp);
6390 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6391
6392 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6393
6394 if (pAttachTemp->i_getControllerName() == aName)
6395 {
6396 rc = i_detachDevice(pAttachTemp, alock, NULL);
6397 if (FAILED(rc)) return rc;
6398 }
6399 }
6400 }
6401
6402 /* We can remove it now. */
6403 i_setModified(IsModified_Storage);
6404 mStorageControllers.backup();
6405
6406 ctrl->i_unshare();
6407
6408 mStorageControllers->remove(ctrl);
6409
6410 /* inform the direct session if any */
6411 alock.release();
6412 i_onStorageControllerChange();
6413
6414 return S_OK;
6415}
6416
6417HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6418 ComPtr<IUSBController> &aController)
6419{
6420 if ( (aType <= USBControllerType_Null)
6421 || (aType >= USBControllerType_Last))
6422 return setError(E_INVALIDARG,
6423 tr("Invalid USB controller type: %d"),
6424 aType);
6425
6426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6427
6428 HRESULT rc = i_checkStateDependency(MutableStateDep);
6429 if (FAILED(rc)) return rc;
6430
6431 /* try to find one with the same type first. */
6432 ComObjPtr<USBController> ctrl;
6433
6434 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6435 if (SUCCEEDED(rc))
6436 return setError(VBOX_E_OBJECT_IN_USE,
6437 tr("USB controller named '%s' already exists"),
6438 aName.c_str());
6439
6440 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6441 ULONG maxInstances;
6442 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6443 if (FAILED(rc))
6444 return rc;
6445
6446 ULONG cInstances = i_getUSBControllerCountByType(aType);
6447 if (cInstances >= maxInstances)
6448 return setError(E_INVALIDARG,
6449 tr("Too many USB controllers of this type"));
6450
6451 ctrl.createObject();
6452
6453 rc = ctrl->init(this, aName, aType);
6454 if (FAILED(rc)) return rc;
6455
6456 i_setModified(IsModified_USB);
6457 mUSBControllers.backup();
6458 mUSBControllers->push_back(ctrl);
6459
6460 ctrl.queryInterfaceTo(aController.asOutParam());
6461
6462 /* inform the direct session if any */
6463 alock.release();
6464 i_onUSBControllerChange();
6465
6466 return S_OK;
6467}
6468
6469HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6470{
6471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6472
6473 ComObjPtr<USBController> ctrl;
6474
6475 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6476 if (SUCCEEDED(rc))
6477 ctrl.queryInterfaceTo(aController.asOutParam());
6478
6479 return rc;
6480}
6481
6482HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6483 ULONG *aControllers)
6484{
6485 if ( (aType <= USBControllerType_Null)
6486 || (aType >= USBControllerType_Last))
6487 return setError(E_INVALIDARG,
6488 tr("Invalid USB controller type: %d"),
6489 aType);
6490
6491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6492
6493 ComObjPtr<USBController> ctrl;
6494
6495 *aControllers = i_getUSBControllerCountByType(aType);
6496
6497 return S_OK;
6498}
6499
6500HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6501{
6502
6503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6504
6505 HRESULT rc = i_checkStateDependency(MutableStateDep);
6506 if (FAILED(rc)) return rc;
6507
6508 ComObjPtr<USBController> ctrl;
6509 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6510 if (FAILED(rc)) return rc;
6511
6512 i_setModified(IsModified_USB);
6513 mUSBControllers.backup();
6514
6515 ctrl->i_unshare();
6516
6517 mUSBControllers->remove(ctrl);
6518
6519 /* inform the direct session if any */
6520 alock.release();
6521 i_onUSBControllerChange();
6522
6523 return S_OK;
6524}
6525
6526HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6527 ULONG *aOriginX,
6528 ULONG *aOriginY,
6529 ULONG *aWidth,
6530 ULONG *aHeight,
6531 BOOL *aEnabled)
6532{
6533 uint32_t u32OriginX= 0;
6534 uint32_t u32OriginY= 0;
6535 uint32_t u32Width = 0;
6536 uint32_t u32Height = 0;
6537 uint16_t u16Flags = 0;
6538
6539 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6540 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6541 if (RT_FAILURE(vrc))
6542 {
6543#ifdef RT_OS_WINDOWS
6544 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6545 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6546 * So just assign fEnable to TRUE again.
6547 * The right fix would be to change GUI API wrappers to make sure that parameters
6548 * are changed only if API succeeds.
6549 */
6550 *aEnabled = TRUE;
6551#endif
6552 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6553 tr("Saved guest size is not available (%Rrc)"),
6554 vrc);
6555 }
6556
6557 *aOriginX = u32OriginX;
6558 *aOriginY = u32OriginY;
6559 *aWidth = u32Width;
6560 *aHeight = u32Height;
6561 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6562
6563 return S_OK;
6564}
6565
6566HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6567 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6568{
6569 if (aScreenId != 0)
6570 return E_NOTIMPL;
6571
6572 if ( aBitmapFormat != BitmapFormat_BGR0
6573 && aBitmapFormat != BitmapFormat_BGRA
6574 && aBitmapFormat != BitmapFormat_RGBA
6575 && aBitmapFormat != BitmapFormat_PNG)
6576 return setError(E_NOTIMPL,
6577 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6578
6579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6580
6581 uint8_t *pu8Data = NULL;
6582 uint32_t cbData = 0;
6583 uint32_t u32Width = 0;
6584 uint32_t u32Height = 0;
6585
6586 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6587
6588 if (RT_FAILURE(vrc))
6589 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6590 tr("Saved thumbnail data is not available (%Rrc)"),
6591 vrc);
6592
6593 HRESULT hr = S_OK;
6594
6595 *aWidth = u32Width;
6596 *aHeight = u32Height;
6597
6598 if (cbData > 0)
6599 {
6600 /* Convert pixels to the format expected by the API caller. */
6601 if (aBitmapFormat == BitmapFormat_BGR0)
6602 {
6603 /* [0] B, [1] G, [2] R, [3] 0. */
6604 aData.resize(cbData);
6605 memcpy(&aData.front(), pu8Data, cbData);
6606 }
6607 else if (aBitmapFormat == BitmapFormat_BGRA)
6608 {
6609 /* [0] B, [1] G, [2] R, [3] A. */
6610 aData.resize(cbData);
6611 for (uint32_t i = 0; i < cbData; i += 4)
6612 {
6613 aData[i] = pu8Data[i];
6614 aData[i + 1] = pu8Data[i + 1];
6615 aData[i + 2] = pu8Data[i + 2];
6616 aData[i + 3] = 0xff;
6617 }
6618 }
6619 else if (aBitmapFormat == BitmapFormat_RGBA)
6620 {
6621 /* [0] R, [1] G, [2] B, [3] A. */
6622 aData.resize(cbData);
6623 for (uint32_t i = 0; i < cbData; i += 4)
6624 {
6625 aData[i] = pu8Data[i + 2];
6626 aData[i + 1] = pu8Data[i + 1];
6627 aData[i + 2] = pu8Data[i];
6628 aData[i + 3] = 0xff;
6629 }
6630 }
6631 else if (aBitmapFormat == BitmapFormat_PNG)
6632 {
6633 uint8_t *pu8PNG = NULL;
6634 uint32_t cbPNG = 0;
6635 uint32_t cxPNG = 0;
6636 uint32_t cyPNG = 0;
6637
6638 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6639
6640 if (RT_SUCCESS(vrc))
6641 {
6642 aData.resize(cbPNG);
6643 if (cbPNG)
6644 memcpy(&aData.front(), pu8PNG, cbPNG);
6645 }
6646 else
6647 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6648 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6649 vrc);
6650
6651 RTMemFree(pu8PNG);
6652 }
6653 }
6654
6655 freeSavedDisplayScreenshot(pu8Data);
6656
6657 return hr;
6658}
6659
6660HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6661 ULONG *aWidth,
6662 ULONG *aHeight,
6663 std::vector<BitmapFormat_T> &aBitmapFormats)
6664{
6665 if (aScreenId != 0)
6666 return E_NOTIMPL;
6667
6668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6669
6670 uint8_t *pu8Data = NULL;
6671 uint32_t cbData = 0;
6672 uint32_t u32Width = 0;
6673 uint32_t u32Height = 0;
6674
6675 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6676
6677 if (RT_FAILURE(vrc))
6678 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6679 tr("Saved screenshot data is not available (%Rrc)"),
6680 vrc);
6681
6682 *aWidth = u32Width;
6683 *aHeight = u32Height;
6684 aBitmapFormats.resize(1);
6685 aBitmapFormats[0] = BitmapFormat_PNG;
6686
6687 freeSavedDisplayScreenshot(pu8Data);
6688
6689 return S_OK;
6690}
6691
6692HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6693 BitmapFormat_T aBitmapFormat,
6694 ULONG *aWidth,
6695 ULONG *aHeight,
6696 std::vector<BYTE> &aData)
6697{
6698 if (aScreenId != 0)
6699 return E_NOTIMPL;
6700
6701 if (aBitmapFormat != BitmapFormat_PNG)
6702 return E_NOTIMPL;
6703
6704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6705
6706 uint8_t *pu8Data = NULL;
6707 uint32_t cbData = 0;
6708 uint32_t u32Width = 0;
6709 uint32_t u32Height = 0;
6710
6711 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6712
6713 if (RT_FAILURE(vrc))
6714 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6715 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6716 vrc);
6717
6718 *aWidth = u32Width;
6719 *aHeight = u32Height;
6720
6721 aData.resize(cbData);
6722 if (cbData)
6723 memcpy(&aData.front(), pu8Data, cbData);
6724
6725 freeSavedDisplayScreenshot(pu8Data);
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::hotPlugCPU(ULONG aCpu)
6731{
6732 HRESULT rc = S_OK;
6733 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6734
6735 if (!mHWData->mCPUHotPlugEnabled)
6736 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6737
6738 if (aCpu >= mHWData->mCPUCount)
6739 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6740
6741 if (mHWData->mCPUAttached[aCpu])
6742 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6743
6744 alock.release();
6745 rc = i_onCPUChange(aCpu, false);
6746 alock.acquire();
6747 if (FAILED(rc)) return rc;
6748
6749 i_setModified(IsModified_MachineData);
6750 mHWData.backup();
6751 mHWData->mCPUAttached[aCpu] = true;
6752
6753 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6754 if (Global::IsOnline(mData->mMachineState))
6755 i_saveSettings(NULL);
6756
6757 return S_OK;
6758}
6759
6760HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6761{
6762 HRESULT rc = S_OK;
6763
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765
6766 if (!mHWData->mCPUHotPlugEnabled)
6767 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6768
6769 if (aCpu >= SchemaDefs::MaxCPUCount)
6770 return setError(E_INVALIDARG,
6771 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6772 SchemaDefs::MaxCPUCount);
6773
6774 if (!mHWData->mCPUAttached[aCpu])
6775 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6776
6777 /* CPU 0 can't be detached */
6778 if (aCpu == 0)
6779 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6780
6781 alock.release();
6782 rc = i_onCPUChange(aCpu, true);
6783 alock.acquire();
6784 if (FAILED(rc)) return rc;
6785
6786 i_setModified(IsModified_MachineData);
6787 mHWData.backup();
6788 mHWData->mCPUAttached[aCpu] = false;
6789
6790 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6791 if (Global::IsOnline(mData->mMachineState))
6792 i_saveSettings(NULL);
6793
6794 return S_OK;
6795}
6796
6797HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6798{
6799 *aAttached = false;
6800
6801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 /* If hotplug is enabled the CPU is always enabled. */
6804 if (!mHWData->mCPUHotPlugEnabled)
6805 {
6806 if (aCpu < mHWData->mCPUCount)
6807 *aAttached = true;
6808 }
6809 else
6810 {
6811 if (aCpu < SchemaDefs::MaxCPUCount)
6812 *aAttached = mHWData->mCPUAttached[aCpu];
6813 }
6814
6815 return S_OK;
6816}
6817
6818HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6819{
6820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6821
6822 Utf8Str log = i_getLogFilename(aIdx);
6823 if (!RTFileExists(log.c_str()))
6824 log.setNull();
6825 aFilename = log;
6826
6827 return S_OK;
6828}
6829
6830HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6831{
6832 if (aSize < 0)
6833 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6834
6835 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6836
6837 HRESULT rc = S_OK;
6838 Utf8Str log = i_getLogFilename(aIdx);
6839
6840 /* do not unnecessarily hold the lock while doing something which does
6841 * not need the lock and potentially takes a long time. */
6842 alock.release();
6843
6844 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6845 * keeps the SOAP reply size under 1M for the webservice (we're using
6846 * base64 encoded strings for binary data for years now, avoiding the
6847 * expansion of each byte array element to approx. 25 bytes of XML. */
6848 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6849 aData.resize(cbData);
6850
6851 RTFILE LogFile;
6852 int vrc = RTFileOpen(&LogFile, log.c_str(),
6853 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6854 if (RT_SUCCESS(vrc))
6855 {
6856 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6857 if (RT_SUCCESS(vrc))
6858 aData.resize(cbData);
6859 else
6860 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6861 tr("Could not read log file '%s' (%Rrc)"),
6862 log.c_str(), vrc);
6863 RTFileClose(LogFile);
6864 }
6865 else
6866 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6867 tr("Could not open log file '%s' (%Rrc)"),
6868 log.c_str(), vrc);
6869
6870 if (FAILED(rc))
6871 aData.resize(0);
6872
6873 return rc;
6874}
6875
6876
6877/**
6878 * Currently this method doesn't attach device to the running VM,
6879 * just makes sure it's plugged on next VM start.
6880 */
6881HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6882{
6883 // lock scope
6884 {
6885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6886
6887 HRESULT rc = i_checkStateDependency(MutableStateDep);
6888 if (FAILED(rc)) return rc;
6889
6890 ChipsetType_T aChipset = ChipsetType_PIIX3;
6891 COMGETTER(ChipsetType)(&aChipset);
6892
6893 if (aChipset != ChipsetType_ICH9)
6894 {
6895 return setError(E_INVALIDARG,
6896 tr("Host PCI attachment only supported with ICH9 chipset"));
6897 }
6898
6899 // check if device with this host PCI address already attached
6900 for (HWData::PCIDeviceAssignmentList::const_iterator
6901 it = mHWData->mPCIDeviceAssignments.begin();
6902 it != mHWData->mPCIDeviceAssignments.end();
6903 ++it)
6904 {
6905 LONG iHostAddress = -1;
6906 ComPtr<PCIDeviceAttachment> pAttach;
6907 pAttach = *it;
6908 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6909 if (iHostAddress == aHostAddress)
6910 return setError(E_INVALIDARG,
6911 tr("Device with host PCI address already attached to this VM"));
6912 }
6913
6914 ComObjPtr<PCIDeviceAttachment> pda;
6915 char name[32];
6916
6917 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6918 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6919 pda.createObject();
6920 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6921 i_setModified(IsModified_MachineData);
6922 mHWData.backup();
6923 mHWData->mPCIDeviceAssignments.push_back(pda);
6924 }
6925
6926 return S_OK;
6927}
6928
6929/**
6930 * Currently this method doesn't detach device from the running VM,
6931 * just makes sure it's not plugged on next VM start.
6932 */
6933HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6934{
6935 ComObjPtr<PCIDeviceAttachment> pAttach;
6936 bool fRemoved = false;
6937 HRESULT rc;
6938
6939 // lock scope
6940 {
6941 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6942
6943 rc = i_checkStateDependency(MutableStateDep);
6944 if (FAILED(rc)) return rc;
6945
6946 for (HWData::PCIDeviceAssignmentList::const_iterator
6947 it = mHWData->mPCIDeviceAssignments.begin();
6948 it != mHWData->mPCIDeviceAssignments.end();
6949 ++it)
6950 {
6951 LONG iHostAddress = -1;
6952 pAttach = *it;
6953 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6954 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6955 {
6956 i_setModified(IsModified_MachineData);
6957 mHWData.backup();
6958 mHWData->mPCIDeviceAssignments.remove(pAttach);
6959 fRemoved = true;
6960 break;
6961 }
6962 }
6963 }
6964
6965
6966 /* Fire event outside of the lock */
6967 if (fRemoved)
6968 {
6969 Assert(!pAttach.isNull());
6970 ComPtr<IEventSource> es;
6971 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6972 Assert(SUCCEEDED(rc));
6973 Bstr mid;
6974 rc = this->COMGETTER(Id)(mid.asOutParam());
6975 Assert(SUCCEEDED(rc));
6976 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6977 }
6978
6979 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6980 tr("No host PCI device %08x attached"),
6981 aHostAddress
6982 );
6983}
6984
6985HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6986{
6987 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6988
6989 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6990 size_t i = 0;
6991 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6992 it = mHWData->mPCIDeviceAssignments.begin();
6993 it != mHWData->mPCIDeviceAssignments.end();
6994 ++it, ++i)
6995 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6996
6997 return S_OK;
6998}
6999
7000HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
7001{
7002 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
7003
7004 return S_OK;
7005}
7006
7007HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
7008{
7009 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7010
7011 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
7012
7013 return S_OK;
7014}
7015
7016HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
7017{
7018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7019 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7020 if (SUCCEEDED(hrc))
7021 {
7022 hrc = mHWData.backupEx();
7023 if (SUCCEEDED(hrc))
7024 {
7025 i_setModified(IsModified_MachineData);
7026 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
7027 }
7028 }
7029 return hrc;
7030}
7031
7032HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
7033{
7034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7035 aTracingConfig = mHWData->mDebugging.strTracingConfig;
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7043 if (SUCCEEDED(hrc))
7044 {
7045 hrc = mHWData.backupEx();
7046 if (SUCCEEDED(hrc))
7047 {
7048 mHWData->mDebugging.strTracingConfig = aTracingConfig;
7049 if (SUCCEEDED(hrc))
7050 i_setModified(IsModified_MachineData);
7051 }
7052 }
7053 return hrc;
7054}
7055
7056HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
7057{
7058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7059
7060 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
7061
7062 return S_OK;
7063}
7064
7065HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
7066{
7067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7068 HRESULT hrc = i_checkStateDependency(MutableStateDep);
7069 if (SUCCEEDED(hrc))
7070 {
7071 hrc = mHWData.backupEx();
7072 if (SUCCEEDED(hrc))
7073 {
7074 i_setModified(IsModified_MachineData);
7075 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
7076 }
7077 }
7078 return hrc;
7079}
7080
7081HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
7082{
7083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
7086
7087 return S_OK;
7088}
7089
7090HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
7091{
7092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7093
7094 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7095 if ( SUCCEEDED(hrc)
7096 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
7097 {
7098 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7099 int vrc;
7100
7101 if (aAutostartEnabled)
7102 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
7103 else
7104 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
7105
7106 if (RT_SUCCESS(vrc))
7107 {
7108 hrc = mHWData.backupEx();
7109 if (SUCCEEDED(hrc))
7110 {
7111 i_setModified(IsModified_MachineData);
7112 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
7113 }
7114 }
7115 else if (vrc == VERR_NOT_SUPPORTED)
7116 hrc = setError(VBOX_E_NOT_SUPPORTED,
7117 tr("The VM autostart feature is not supported on this platform"));
7118 else if (vrc == VERR_PATH_NOT_FOUND)
7119 hrc = setError(E_FAIL,
7120 tr("The path to the autostart database is not set"));
7121 else
7122 hrc = setError(E_UNEXPECTED,
7123 tr("%s machine '%s' to the autostart database failed with %Rrc"),
7124 aAutostartEnabled ? "Adding" : "Removing",
7125 mUserData->s.strName.c_str(), vrc);
7126 }
7127 return hrc;
7128}
7129
7130HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
7131{
7132 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7133
7134 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
7135
7136 return S_OK;
7137}
7138
7139HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
7140{
7141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7142 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7143 if (SUCCEEDED(hrc))
7144 {
7145 hrc = mHWData.backupEx();
7146 if (SUCCEEDED(hrc))
7147 {
7148 i_setModified(IsModified_MachineData);
7149 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
7150 }
7151 }
7152 return hrc;
7153}
7154
7155HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
7156{
7157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7158
7159 *aAutostopType = mHWData->mAutostart.enmAutostopType;
7160
7161 return S_OK;
7162}
7163
7164HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
7165{
7166 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7167 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7168 if ( SUCCEEDED(hrc)
7169 && mHWData->mAutostart.enmAutostopType != aAutostopType)
7170 {
7171 AutostartDb *autostartDb = mParent->i_getAutostartDb();
7172 int vrc;
7173
7174 if (aAutostopType != AutostopType_Disabled)
7175 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
7176 else
7177 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
7178
7179 if (RT_SUCCESS(vrc))
7180 {
7181 hrc = mHWData.backupEx();
7182 if (SUCCEEDED(hrc))
7183 {
7184 i_setModified(IsModified_MachineData);
7185 mHWData->mAutostart.enmAutostopType = aAutostopType;
7186 }
7187 }
7188 else if (vrc == VERR_NOT_SUPPORTED)
7189 hrc = setError(VBOX_E_NOT_SUPPORTED,
7190 tr("The VM autostop feature is not supported on this platform"));
7191 else if (vrc == VERR_PATH_NOT_FOUND)
7192 hrc = setError(E_FAIL,
7193 tr("The path to the autostart database is not set"));
7194 else
7195 hrc = setError(E_UNEXPECTED,
7196 tr("%s machine '%s' to the autostop database failed with %Rrc"),
7197 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
7198 mUserData->s.strName.c_str(), vrc);
7199 }
7200 return hrc;
7201}
7202
7203HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
7204{
7205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7206
7207 aDefaultFrontend = mHWData->mDefaultFrontend;
7208
7209 return S_OK;
7210}
7211
7212HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
7213{
7214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7215 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7216 if (SUCCEEDED(hrc))
7217 {
7218 hrc = mHWData.backupEx();
7219 if (SUCCEEDED(hrc))
7220 {
7221 i_setModified(IsModified_MachineData);
7222 mHWData->mDefaultFrontend = aDefaultFrontend;
7223 }
7224 }
7225 return hrc;
7226}
7227
7228HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
7229{
7230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7231 size_t cbIcon = mUserData->s.ovIcon.size();
7232 aIcon.resize(cbIcon);
7233 if (cbIcon)
7234 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7235 return S_OK;
7236}
7237
7238HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7239{
7240 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7241 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7242 if (SUCCEEDED(hrc))
7243 {
7244 i_setModified(IsModified_MachineData);
7245 mUserData.backup();
7246 size_t cbIcon = aIcon.size();
7247 mUserData->s.ovIcon.resize(cbIcon);
7248 if (cbIcon)
7249 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7250 }
7251 return hrc;
7252}
7253
7254HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7255{
7256#ifdef VBOX_WITH_USB
7257 *aUSBProxyAvailable = true;
7258#else
7259 *aUSBProxyAvailable = false;
7260#endif
7261 return S_OK;
7262}
7263
7264HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7265{
7266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7267
7268 aVMProcessPriority = mUserData->s.strVMPriority;
7269
7270 return S_OK;
7271}
7272
7273HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7274{
7275 RT_NOREF(aVMProcessPriority);
7276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7277 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7278 if (SUCCEEDED(hrc))
7279 {
7280 /** @todo r=klaus: currently this is marked as not implemented, as
7281 * the code for setting the priority of the process is not there
7282 * (neither when starting the VM nor at runtime). */
7283 ReturnComNotImplemented();
7284#if 0
7285 hrc = mUserData.backupEx();
7286 if (SUCCEEDED(hrc))
7287 {
7288 i_setModified(IsModified_MachineData);
7289 mUserData->s.strVMPriority = aVMProcessPriority;
7290 }
7291#endif
7292 }
7293 return hrc;
7294}
7295
7296HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7297 ComPtr<IProgress> &aProgress)
7298{
7299 ComObjPtr<Progress> pP;
7300 Progress *ppP = pP;
7301 IProgress *iP = static_cast<IProgress *>(ppP);
7302 IProgress **pProgress = &iP;
7303
7304 IMachine *pTarget = aTarget;
7305
7306 /* Convert the options. */
7307 RTCList<CloneOptions_T> optList;
7308 if (aOptions.size())
7309 for (size_t i = 0; i < aOptions.size(); ++i)
7310 optList.append(aOptions[i]);
7311
7312 if (optList.contains(CloneOptions_Link))
7313 {
7314 if (!i_isSnapshotMachine())
7315 return setError(E_INVALIDARG,
7316 tr("Linked clone can only be created from a snapshot"));
7317 if (aMode != CloneMode_MachineState)
7318 return setError(E_INVALIDARG,
7319 tr("Linked clone can only be created for a single machine state"));
7320 }
7321 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7322
7323 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7324
7325 HRESULT rc = pWorker->start(pProgress);
7326
7327 pP = static_cast<Progress *>(*pProgress);
7328 pP.queryInterfaceTo(aProgress.asOutParam());
7329
7330 return rc;
7331
7332}
7333
7334HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7335 const com::Utf8Str &aType,
7336 ComPtr<IProgress> &aProgress)
7337{
7338 LogFlowThisFuncEnter();
7339
7340 ComObjPtr<Progress> progress;
7341
7342 progress.createObject();
7343
7344 HRESULT rc = S_OK;
7345 Utf8Str targetPath = aTargetPath;
7346 Utf8Str type = aType;
7347
7348 /* Initialize our worker task */
7349 MachineMoveVM* task = NULL;
7350 try
7351 {
7352 task = new MachineMoveVM(this, targetPath, type, progress);
7353 }
7354 catch(...)
7355 {
7356 delete task;
7357 return rc;
7358 }
7359
7360 /*
7361 * task pointer will be owned by the ThreadTask class.
7362 * There is no need to call operator "delete" in the end.
7363 */
7364 rc = task->init();
7365 if (SUCCEEDED(rc))
7366 {
7367 rc = task->createThread();
7368 if (FAILED(rc))
7369 {
7370 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7371 }
7372
7373 /* Return progress to the caller */
7374 progress.queryInterfaceTo(aProgress.asOutParam());
7375 }
7376
7377 LogFlowThisFuncLeave();
7378 return rc;
7379
7380}
7381
7382HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7383{
7384 NOREF(aProgress);
7385 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7386
7387 // This check should always fail.
7388 HRESULT rc = i_checkStateDependency(MutableStateDep);
7389 if (FAILED(rc)) return rc;
7390
7391 AssertFailedReturn(E_NOTIMPL);
7392}
7393
7394HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7395{
7396 NOREF(aSavedStateFile);
7397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7398
7399 // This check should always fail.
7400 HRESULT rc = i_checkStateDependency(MutableStateDep);
7401 if (FAILED(rc)) return rc;
7402
7403 AssertFailedReturn(E_NOTIMPL);
7404}
7405
7406HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7407{
7408 NOREF(aFRemoveFile);
7409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7410
7411 // This check should always fail.
7412 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7413 if (FAILED(rc)) return rc;
7414
7415 AssertFailedReturn(E_NOTIMPL);
7416}
7417
7418// public methods for internal purposes
7419/////////////////////////////////////////////////////////////////////////////
7420
7421/**
7422 * Adds the given IsModified_* flag to the dirty flags of the machine.
7423 * This must be called either during i_loadSettings or under the machine write lock.
7424 * @param fl Flag
7425 * @param fAllowStateModification If state modifications are allowed.
7426 */
7427void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7428{
7429 mData->flModifications |= fl;
7430 if (fAllowStateModification && i_isStateModificationAllowed())
7431 mData->mCurrentStateModified = true;
7432}
7433
7434/**
7435 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7436 * care of the write locking.
7437 *
7438 * @param fModification The flag to add.
7439 * @param fAllowStateModification If state modifications are allowed.
7440 */
7441void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7442{
7443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7444 i_setModified(fModification, fAllowStateModification);
7445}
7446
7447/**
7448 * Saves the registry entry of this machine to the given configuration node.
7449 *
7450 * @param data Machine registry data.
7451 *
7452 * @note locks this object for reading.
7453 */
7454HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7455{
7456 AutoLimitedCaller autoCaller(this);
7457 AssertComRCReturnRC(autoCaller.rc());
7458
7459 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7460
7461 data.uuid = mData->mUuid;
7462 data.strSettingsFile = mData->m_strConfigFile;
7463
7464 return S_OK;
7465}
7466
7467/**
7468 * Calculates the absolute path of the given path taking the directory of the
7469 * machine settings file as the current directory.
7470 *
7471 * @param strPath Path to calculate the absolute path for.
7472 * @param aResult Where to put the result (used only on success, can be the
7473 * same Utf8Str instance as passed in @a aPath).
7474 * @return IPRT result.
7475 *
7476 * @note Locks this object for reading.
7477 */
7478int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7479{
7480 AutoCaller autoCaller(this);
7481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7482
7483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7484
7485 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7486
7487 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7488
7489 strSettingsDir.stripFilename();
7490 char folder[RTPATH_MAX];
7491 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7492 if (RT_SUCCESS(vrc))
7493 aResult = folder;
7494
7495 return vrc;
7496}
7497
7498/**
7499 * Copies strSource to strTarget, making it relative to the machine folder
7500 * if it is a subdirectory thereof, or simply copying it otherwise.
7501 *
7502 * @param strSource Path to evaluate and copy.
7503 * @param strTarget Buffer to receive target path.
7504 *
7505 * @note Locks this object for reading.
7506 */
7507void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7508 Utf8Str &strTarget)
7509{
7510 AutoCaller autoCaller(this);
7511 AssertComRCReturn(autoCaller.rc(), (void)0);
7512
7513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7514
7515 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7516 // use strTarget as a temporary buffer to hold the machine settings dir
7517 strTarget = mData->m_strConfigFileFull;
7518 strTarget.stripFilename();
7519 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7520 {
7521 // is relative: then append what's left
7522 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7523 // for empty paths (only possible for subdirs) use "." to avoid
7524 // triggering default settings for not present config attributes.
7525 if (strTarget.isEmpty())
7526 strTarget = ".";
7527 }
7528 else
7529 // is not relative: then overwrite
7530 strTarget = strSource;
7531}
7532
7533/**
7534 * Returns the full path to the machine's log folder in the
7535 * \a aLogFolder argument.
7536 */
7537void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7538{
7539 AutoCaller autoCaller(this);
7540 AssertComRCReturnVoid(autoCaller.rc());
7541
7542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7543
7544 char szTmp[RTPATH_MAX];
7545 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7546 if (RT_SUCCESS(vrc))
7547 {
7548 if (szTmp[0] && !mUserData.isNull())
7549 {
7550 char szTmp2[RTPATH_MAX];
7551 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7552 if (RT_SUCCESS(vrc))
7553 aLogFolder = Utf8StrFmt("%s%c%s",
7554 szTmp2,
7555 RTPATH_DELIMITER,
7556 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7557 }
7558 else
7559 vrc = VERR_PATH_IS_RELATIVE;
7560 }
7561
7562 if (RT_FAILURE(vrc))
7563 {
7564 // fallback if VBOX_USER_LOGHOME is not set or invalid
7565 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7566 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7567 aLogFolder.append(RTPATH_DELIMITER);
7568 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7569 }
7570}
7571
7572/**
7573 * Returns the full path to the machine's log file for an given index.
7574 */
7575Utf8Str Machine::i_getLogFilename(ULONG idx)
7576{
7577 Utf8Str logFolder;
7578 getLogFolder(logFolder);
7579 Assert(logFolder.length());
7580
7581 Utf8Str log;
7582 if (idx == 0)
7583 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7584#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7585 else if (idx == 1)
7586 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7587 else
7588 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7589#else
7590 else
7591 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7592#endif
7593 return log;
7594}
7595
7596/**
7597 * Returns the full path to the machine's hardened log file.
7598 */
7599Utf8Str Machine::i_getHardeningLogFilename(void)
7600{
7601 Utf8Str strFilename;
7602 getLogFolder(strFilename);
7603 Assert(strFilename.length());
7604 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7605 return strFilename;
7606}
7607
7608
7609/**
7610 * Composes a unique saved state filename based on the current system time. The filename is
7611 * granular to the second so this will work so long as no more than one snapshot is taken on
7612 * a machine per second.
7613 *
7614 * Before version 4.1, we used this formula for saved state files:
7615 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7616 * which no longer works because saved state files can now be shared between the saved state of the
7617 * "saved" machine and an online snapshot, and the following would cause problems:
7618 * 1) save machine
7619 * 2) create online snapshot from that machine state --> reusing saved state file
7620 * 3) save machine again --> filename would be reused, breaking the online snapshot
7621 *
7622 * So instead we now use a timestamp.
7623 *
7624 * @param strStateFilePath
7625 */
7626
7627void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7628{
7629 AutoCaller autoCaller(this);
7630 AssertComRCReturnVoid(autoCaller.rc());
7631
7632 {
7633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7634 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7635 }
7636
7637 RTTIMESPEC ts;
7638 RTTimeNow(&ts);
7639 RTTIME time;
7640 RTTimeExplode(&time, &ts);
7641
7642 strStateFilePath += RTPATH_DELIMITER;
7643 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7644 time.i32Year, time.u8Month, time.u8MonthDay,
7645 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7646}
7647
7648/**
7649 * Returns the full path to the default video capture file.
7650 */
7651void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7652{
7653 AutoCaller autoCaller(this);
7654 AssertComRCReturnVoid(autoCaller.rc());
7655
7656 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7657
7658 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7659 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7660 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7661}
7662
7663/**
7664 * Returns whether at least one USB controller is present for the VM.
7665 */
7666bool Machine::i_isUSBControllerPresent()
7667{
7668 AutoCaller autoCaller(this);
7669 AssertComRCReturn(autoCaller.rc(), false);
7670
7671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7672
7673 return (mUSBControllers->size() > 0);
7674}
7675
7676/**
7677 * @note Locks this object for writing, calls the client process
7678 * (inside the lock).
7679 */
7680HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7681 const Utf8Str &strFrontend,
7682 const Utf8Str &strEnvironment,
7683 ProgressProxy *aProgress)
7684{
7685 LogFlowThisFuncEnter();
7686
7687 AssertReturn(aControl, E_FAIL);
7688 AssertReturn(aProgress, E_FAIL);
7689 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7690
7691 AutoCaller autoCaller(this);
7692 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7693
7694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7695
7696 if (!mData->mRegistered)
7697 return setError(E_UNEXPECTED,
7698 tr("The machine '%s' is not registered"),
7699 mUserData->s.strName.c_str());
7700
7701 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7702
7703 /* The process started when launching a VM with separate UI/VM processes is always
7704 * the UI process, i.e. needs special handling as it won't claim the session. */
7705 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7706
7707 if (fSeparate)
7708 {
7709 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7710 return setError(VBOX_E_INVALID_OBJECT_STATE,
7711 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7712 mUserData->s.strName.c_str());
7713 }
7714 else
7715 {
7716 if ( mData->mSession.mState == SessionState_Locked
7717 || mData->mSession.mState == SessionState_Spawning
7718 || mData->mSession.mState == SessionState_Unlocking)
7719 return setError(VBOX_E_INVALID_OBJECT_STATE,
7720 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7721 mUserData->s.strName.c_str());
7722
7723 /* may not be busy */
7724 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7725 }
7726
7727 /* get the path to the executable */
7728 char szPath[RTPATH_MAX];
7729 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7730 size_t cchBufLeft = strlen(szPath);
7731 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7732 szPath[cchBufLeft] = 0;
7733 char *pszNamePart = szPath + cchBufLeft;
7734 cchBufLeft = sizeof(szPath) - cchBufLeft;
7735
7736 int vrc = VINF_SUCCESS;
7737 RTPROCESS pid = NIL_RTPROCESS;
7738
7739 RTENV env = RTENV_DEFAULT;
7740
7741 if (!strEnvironment.isEmpty())
7742 {
7743 char *newEnvStr = NULL;
7744
7745 do
7746 {
7747 /* clone the current environment */
7748 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7749 AssertRCBreakStmt(vrc2, vrc = vrc2);
7750
7751 newEnvStr = RTStrDup(strEnvironment.c_str());
7752 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7753
7754 /* put new variables to the environment
7755 * (ignore empty variable names here since RTEnv API
7756 * intentionally doesn't do that) */
7757 char *var = newEnvStr;
7758 for (char *p = newEnvStr; *p; ++p)
7759 {
7760 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7761 {
7762 *p = '\0';
7763 if (*var)
7764 {
7765 char *val = strchr(var, '=');
7766 if (val)
7767 {
7768 *val++ = '\0';
7769 vrc2 = RTEnvSetEx(env, var, val);
7770 }
7771 else
7772 vrc2 = RTEnvUnsetEx(env, var);
7773 if (RT_FAILURE(vrc2))
7774 break;
7775 }
7776 var = p + 1;
7777 }
7778 }
7779 if (RT_SUCCESS(vrc2) && *var)
7780 vrc2 = RTEnvPutEx(env, var);
7781
7782 AssertRCBreakStmt(vrc2, vrc = vrc2);
7783 }
7784 while (0);
7785
7786 if (newEnvStr != NULL)
7787 RTStrFree(newEnvStr);
7788 }
7789
7790 /* Hardening logging */
7791#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7792 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7793 {
7794 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7795 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7796 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7797 {
7798 Utf8Str strStartupLogDir = strHardeningLogFile;
7799 strStartupLogDir.stripFilename();
7800 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7801 file without stripping the file. */
7802 }
7803 strSupHardeningLogArg.append(strHardeningLogFile);
7804
7805 /* Remove legacy log filename to avoid confusion. */
7806 Utf8Str strOldStartupLogFile;
7807 getLogFolder(strOldStartupLogFile);
7808 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7809 RTFileDelete(strOldStartupLogFile.c_str());
7810 }
7811 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7812#else
7813 const char *pszSupHardeningLogArg = NULL;
7814#endif
7815
7816 Utf8Str strCanonicalName;
7817
7818#ifdef VBOX_WITH_QTGUI
7819 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7820 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7821 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7822 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7823 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7824 {
7825 strCanonicalName = "GUI/Qt";
7826# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7827 /* Modify the base path so that we don't need to use ".." below. */
7828 RTPathStripTrailingSlash(szPath);
7829 RTPathStripFilename(szPath);
7830 cchBufLeft = strlen(szPath);
7831 pszNamePart = szPath + cchBufLeft;
7832 cchBufLeft = sizeof(szPath) - cchBufLeft;
7833
7834# define OSX_APP_NAME "VirtualBoxVM"
7835# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7836
7837 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7838 if ( strAppOverride.contains(".")
7839 || strAppOverride.contains("/")
7840 || strAppOverride.contains("\\")
7841 || strAppOverride.contains(":"))
7842 strAppOverride.setNull();
7843 Utf8Str strAppPath;
7844 if (!strAppOverride.isEmpty())
7845 {
7846 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7847 Utf8Str strFullPath(szPath);
7848 strFullPath.append(strAppPath);
7849 /* there is a race, but people using this deserve the failure */
7850 if (!RTFileExists(strFullPath.c_str()))
7851 strAppOverride.setNull();
7852 }
7853 if (strAppOverride.isEmpty())
7854 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7855 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7856 strcpy(pszNamePart, strAppPath.c_str());
7857# else
7858# ifndef VBOX_GUI_WITH_SHARED_LIBRARY
7859 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7860# else
7861 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7862# endif
7863 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7864 strcpy(pszNamePart, s_szVirtualBox_exe);
7865# endif
7866
7867 Utf8Str idStr = mData->mUuid.toString();
7868 const char *apszArgs[] =
7869 {
7870 szPath,
7871 "--comment", mUserData->s.strName.c_str(),
7872 "--startvm", idStr.c_str(),
7873 "--no-startvm-errormsgbox",
7874 NULL, /* For "--separate". */
7875 NULL, /* For "--sup-startup-log". */
7876 NULL
7877 };
7878 unsigned iArg = 6;
7879 if (fSeparate)
7880 apszArgs[iArg++] = "--separate";
7881 apszArgs[iArg++] = pszSupHardeningLogArg;
7882
7883 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7884 }
7885#else /* !VBOX_WITH_QTGUI */
7886 if (0)
7887 ;
7888#endif /* VBOX_WITH_QTGUI */
7889
7890 else
7891
7892#ifdef VBOX_WITH_VBOXSDL
7893 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7894 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7895 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7896 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7897 {
7898 strCanonicalName = "GUI/SDL";
7899 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7900 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7901 strcpy(pszNamePart, s_szVBoxSDL_exe);
7902
7903 Utf8Str idStr = mData->mUuid.toString();
7904 const char *apszArgs[] =
7905 {
7906 szPath,
7907 "--comment", mUserData->s.strName.c_str(),
7908 "--startvm", idStr.c_str(),
7909 NULL, /* For "--separate". */
7910 NULL, /* For "--sup-startup-log". */
7911 NULL
7912 };
7913 unsigned iArg = 5;
7914 if (fSeparate)
7915 apszArgs[iArg++] = "--separate";
7916 apszArgs[iArg++] = pszSupHardeningLogArg;
7917
7918 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7919 }
7920#else /* !VBOX_WITH_VBOXSDL */
7921 if (0)
7922 ;
7923#endif /* !VBOX_WITH_VBOXSDL */
7924
7925 else
7926
7927#ifdef VBOX_WITH_HEADLESS
7928 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7929 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7930 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7931 )
7932 {
7933 strCanonicalName = "headless";
7934 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7935 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7936 * and a VM works even if the server has not been installed.
7937 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7938 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7939 * differently in 4.0 and 3.x.
7940 */
7941 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7942 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7943 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7944
7945 Utf8Str idStr = mData->mUuid.toString();
7946 const char *apszArgs[] =
7947 {
7948 szPath,
7949 "--comment", mUserData->s.strName.c_str(),
7950 "--startvm", idStr.c_str(),
7951 "--vrde", "config",
7952 NULL, /* For "--capture". */
7953 NULL, /* For "--sup-startup-log". */
7954 NULL
7955 };
7956 unsigned iArg = 7;
7957 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7958 apszArgs[iArg++] = "--capture";
7959 apszArgs[iArg++] = pszSupHardeningLogArg;
7960
7961# ifdef RT_OS_WINDOWS
7962 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7963# else
7964 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7965# endif
7966 }
7967#else /* !VBOX_WITH_HEADLESS */
7968 if (0)
7969 ;
7970#endif /* !VBOX_WITH_HEADLESS */
7971 else
7972 {
7973 RTEnvDestroy(env);
7974 return setError(E_INVALIDARG,
7975 tr("Invalid frontend name: '%s'"),
7976 strFrontend.c_str());
7977 }
7978
7979 RTEnvDestroy(env);
7980
7981 if (RT_FAILURE(vrc))
7982 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7983 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7984 mUserData->s.strName.c_str(), vrc);
7985
7986 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7987
7988 if (!fSeparate)
7989 {
7990 /*
7991 * Note that we don't release the lock here before calling the client,
7992 * because it doesn't need to call us back if called with a NULL argument.
7993 * Releasing the lock here is dangerous because we didn't prepare the
7994 * launch data yet, but the client we've just started may happen to be
7995 * too fast and call LockMachine() that will fail (because of PID, etc.),
7996 * so that the Machine will never get out of the Spawning session state.
7997 */
7998
7999 /* inform the session that it will be a remote one */
8000 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
8001#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
8002 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
8003#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8004 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
8005#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
8006 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
8007
8008 if (FAILED(rc))
8009 {
8010 /* restore the session state */
8011 mData->mSession.mState = SessionState_Unlocked;
8012 alock.release();
8013 mParent->i_addProcessToReap(pid);
8014 /* The failure may occur w/o any error info (from RPC), so provide one */
8015 return setError(VBOX_E_VM_ERROR,
8016 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
8017 }
8018
8019 /* attach launch data to the machine */
8020 Assert(mData->mSession.mPID == NIL_RTPROCESS);
8021 mData->mSession.mRemoteControls.push_back(aControl);
8022 mData->mSession.mProgress = aProgress;
8023 mData->mSession.mPID = pid;
8024 mData->mSession.mState = SessionState_Spawning;
8025 Assert(strCanonicalName.isNotEmpty());
8026 mData->mSession.mName = strCanonicalName;
8027 }
8028 else
8029 {
8030 /* For separate UI process we declare the launch as completed instantly, as the
8031 * actual headless VM start may or may not come. No point in remembering anything
8032 * yet, as what matters for us is when the headless VM gets started. */
8033 aProgress->i_notifyComplete(S_OK);
8034 }
8035
8036 alock.release();
8037 mParent->i_addProcessToReap(pid);
8038
8039 LogFlowThisFuncLeave();
8040 return S_OK;
8041}
8042
8043/**
8044 * Returns @c true if the given session machine instance has an open direct
8045 * session (and optionally also for direct sessions which are closing) and
8046 * returns the session control machine instance if so.
8047 *
8048 * Note that when the method returns @c false, the arguments remain unchanged.
8049 *
8050 * @param aMachine Session machine object.
8051 * @param aControl Direct session control object (optional).
8052 * @param aRequireVM If true then only allow VM sessions.
8053 * @param aAllowClosing If true then additionally a session which is currently
8054 * being closed will also be allowed.
8055 *
8056 * @note locks this object for reading.
8057 */
8058bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
8059 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
8060 bool aRequireVM /*= false*/,
8061 bool aAllowClosing /*= false*/)
8062{
8063 AutoLimitedCaller autoCaller(this);
8064 AssertComRCReturn(autoCaller.rc(), false);
8065
8066 /* just return false for inaccessible machines */
8067 if (getObjectState().getState() != ObjectState::Ready)
8068 return false;
8069
8070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8071
8072 if ( ( mData->mSession.mState == SessionState_Locked
8073 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
8074 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
8075 )
8076 {
8077 AssertReturn(!mData->mSession.mMachine.isNull(), false);
8078
8079 aMachine = mData->mSession.mMachine;
8080
8081 if (aControl != NULL)
8082 *aControl = mData->mSession.mDirectControl;
8083
8084 return true;
8085 }
8086
8087 return false;
8088}
8089
8090/**
8091 * Returns @c true if the given machine has an spawning direct session.
8092 *
8093 * @note locks this object for reading.
8094 */
8095bool Machine::i_isSessionSpawning()
8096{
8097 AutoLimitedCaller autoCaller(this);
8098 AssertComRCReturn(autoCaller.rc(), false);
8099
8100 /* just return false for inaccessible machines */
8101 if (getObjectState().getState() != ObjectState::Ready)
8102 return false;
8103
8104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8105
8106 if (mData->mSession.mState == SessionState_Spawning)
8107 return true;
8108
8109 return false;
8110}
8111
8112/**
8113 * Called from the client watcher thread to check for unexpected client process
8114 * death during Session_Spawning state (e.g. before it successfully opened a
8115 * direct session).
8116 *
8117 * On Win32 and on OS/2, this method is called only when we've got the
8118 * direct client's process termination notification, so it always returns @c
8119 * true.
8120 *
8121 * On other platforms, this method returns @c true if the client process is
8122 * terminated and @c false if it's still alive.
8123 *
8124 * @note Locks this object for writing.
8125 */
8126bool Machine::i_checkForSpawnFailure()
8127{
8128 AutoCaller autoCaller(this);
8129 if (!autoCaller.isOk())
8130 {
8131 /* nothing to do */
8132 LogFlowThisFunc(("Already uninitialized!\n"));
8133 return true;
8134 }
8135
8136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8137
8138 if (mData->mSession.mState != SessionState_Spawning)
8139 {
8140 /* nothing to do */
8141 LogFlowThisFunc(("Not spawning any more!\n"));
8142 return true;
8143 }
8144
8145 HRESULT rc = S_OK;
8146
8147 /* PID not yet initialized, skip check. */
8148 if (mData->mSession.mPID == NIL_RTPROCESS)
8149 return false;
8150
8151 RTPROCSTATUS status;
8152 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
8153
8154 if (vrc != VERR_PROCESS_RUNNING)
8155 {
8156 Utf8Str strExtraInfo;
8157
8158#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
8159 /* If the startup logfile exists and is of non-zero length, tell the
8160 user to look there for more details to encourage them to attach it
8161 when reporting startup issues. */
8162 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
8163 uint64_t cbStartupLogFile = 0;
8164 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
8165 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
8166 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
8167#endif
8168
8169 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
8170 rc = setError(E_FAIL,
8171 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
8172 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
8173 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
8174 rc = setError(E_FAIL,
8175 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
8176 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8177 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
8178 rc = setError(E_FAIL,
8179 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
8180 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
8181 else
8182 rc = setErrorBoth(E_FAIL, vrc,
8183 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
8184 i_getName().c_str(), vrc, strExtraInfo.c_str());
8185 }
8186
8187 if (FAILED(rc))
8188 {
8189 /* Close the remote session, remove the remote control from the list
8190 * and reset session state to Closed (@note keep the code in sync with
8191 * the relevant part in LockMachine()). */
8192
8193 Assert(mData->mSession.mRemoteControls.size() == 1);
8194 if (mData->mSession.mRemoteControls.size() == 1)
8195 {
8196 ErrorInfoKeeper eik;
8197 mData->mSession.mRemoteControls.front()->Uninitialize();
8198 }
8199
8200 mData->mSession.mRemoteControls.clear();
8201 mData->mSession.mState = SessionState_Unlocked;
8202
8203 /* finalize the progress after setting the state */
8204 if (!mData->mSession.mProgress.isNull())
8205 {
8206 mData->mSession.mProgress->notifyComplete(rc);
8207 mData->mSession.mProgress.setNull();
8208 }
8209
8210 mData->mSession.mPID = NIL_RTPROCESS;
8211
8212 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
8213 return true;
8214 }
8215
8216 return false;
8217}
8218
8219/**
8220 * Checks whether the machine can be registered. If so, commits and saves
8221 * all settings.
8222 *
8223 * @note Must be called from mParent's write lock. Locks this object and
8224 * children for writing.
8225 */
8226HRESULT Machine::i_prepareRegister()
8227{
8228 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
8229
8230 AutoLimitedCaller autoCaller(this);
8231 AssertComRCReturnRC(autoCaller.rc());
8232
8233 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8234
8235 /* wait for state dependents to drop to zero */
8236 i_ensureNoStateDependencies();
8237
8238 if (!mData->mAccessible)
8239 return setError(VBOX_E_INVALID_OBJECT_STATE,
8240 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
8241 mUserData->s.strName.c_str(),
8242 mData->mUuid.toString().c_str());
8243
8244 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
8245
8246 if (mData->mRegistered)
8247 return setError(VBOX_E_INVALID_OBJECT_STATE,
8248 tr("The machine '%s' with UUID {%s} is already registered"),
8249 mUserData->s.strName.c_str(),
8250 mData->mUuid.toString().c_str());
8251
8252 HRESULT rc = S_OK;
8253
8254 // Ensure the settings are saved. If we are going to be registered and
8255 // no config file exists yet, create it by calling i_saveSettings() too.
8256 if ( (mData->flModifications)
8257 || (!mData->pMachineConfigFile->fileExists())
8258 )
8259 {
8260 rc = i_saveSettings(NULL);
8261 // no need to check whether VirtualBox.xml needs saving too since
8262 // we can't have a machine XML file rename pending
8263 if (FAILED(rc)) return rc;
8264 }
8265
8266 /* more config checking goes here */
8267
8268 if (SUCCEEDED(rc))
8269 {
8270 /* we may have had implicit modifications we want to fix on success */
8271 i_commit();
8272
8273 mData->mRegistered = true;
8274 }
8275 else
8276 {
8277 /* we may have had implicit modifications we want to cancel on failure*/
8278 i_rollback(false /* aNotify */);
8279 }
8280
8281 return rc;
8282}
8283
8284/**
8285 * Increases the number of objects dependent on the machine state or on the
8286 * registered state. Guarantees that these two states will not change at least
8287 * until #i_releaseStateDependency() is called.
8288 *
8289 * Depending on the @a aDepType value, additional state checks may be made.
8290 * These checks will set extended error info on failure. See
8291 * #i_checkStateDependency() for more info.
8292 *
8293 * If this method returns a failure, the dependency is not added and the caller
8294 * is not allowed to rely on any particular machine state or registration state
8295 * value and may return the failed result code to the upper level.
8296 *
8297 * @param aDepType Dependency type to add.
8298 * @param aState Current machine state (NULL if not interested).
8299 * @param aRegistered Current registered state (NULL if not interested).
8300 *
8301 * @note Locks this object for writing.
8302 */
8303HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8304 MachineState_T *aState /* = NULL */,
8305 BOOL *aRegistered /* = NULL */)
8306{
8307 AutoCaller autoCaller(this);
8308 AssertComRCReturnRC(autoCaller.rc());
8309
8310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8311
8312 HRESULT rc = i_checkStateDependency(aDepType);
8313 if (FAILED(rc)) return rc;
8314
8315 {
8316 if (mData->mMachineStateChangePending != 0)
8317 {
8318 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8319 * drop to zero so don't add more. It may make sense to wait a bit
8320 * and retry before reporting an error (since the pending state
8321 * transition should be really quick) but let's just assert for
8322 * now to see if it ever happens on practice. */
8323
8324 AssertFailed();
8325
8326 return setError(E_ACCESSDENIED,
8327 tr("Machine state change is in progress. Please retry the operation later."));
8328 }
8329
8330 ++mData->mMachineStateDeps;
8331 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8332 }
8333
8334 if (aState)
8335 *aState = mData->mMachineState;
8336 if (aRegistered)
8337 *aRegistered = mData->mRegistered;
8338
8339 return S_OK;
8340}
8341
8342/**
8343 * Decreases the number of objects dependent on the machine state.
8344 * Must always complete the #i_addStateDependency() call after the state
8345 * dependency is no more necessary.
8346 */
8347void Machine::i_releaseStateDependency()
8348{
8349 AutoCaller autoCaller(this);
8350 AssertComRCReturnVoid(autoCaller.rc());
8351
8352 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8353
8354 /* releaseStateDependency() w/o addStateDependency()? */
8355 AssertReturnVoid(mData->mMachineStateDeps != 0);
8356 -- mData->mMachineStateDeps;
8357
8358 if (mData->mMachineStateDeps == 0)
8359 {
8360 /* inform i_ensureNoStateDependencies() that there are no more deps */
8361 if (mData->mMachineStateChangePending != 0)
8362 {
8363 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8364 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8365 }
8366 }
8367}
8368
8369Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8370{
8371 /* start with nothing found */
8372 Utf8Str strResult("");
8373
8374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8375
8376 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8377 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8378 // found:
8379 strResult = it->second; // source is a Utf8Str
8380
8381 return strResult;
8382}
8383
8384// protected methods
8385/////////////////////////////////////////////////////////////////////////////
8386
8387/**
8388 * Performs machine state checks based on the @a aDepType value. If a check
8389 * fails, this method will set extended error info, otherwise it will return
8390 * S_OK. It is supposed, that on failure, the caller will immediately return
8391 * the return value of this method to the upper level.
8392 *
8393 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8394 *
8395 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8396 * current state of this machine object allows to change settings of the
8397 * machine (i.e. the machine is not registered, or registered but not running
8398 * and not saved). It is useful to call this method from Machine setters
8399 * before performing any change.
8400 *
8401 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8402 * as for MutableStateDep except that if the machine is saved, S_OK is also
8403 * returned. This is useful in setters which allow changing machine
8404 * properties when it is in the saved state.
8405 *
8406 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8407 * if the current state of this machine object allows to change runtime
8408 * changeable settings of the machine (i.e. the machine is not registered, or
8409 * registered but either running or not running and not saved). It is useful
8410 * to call this method from Machine setters before performing any changes to
8411 * runtime changeable settings.
8412 *
8413 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8414 * the same as for MutableOrRunningStateDep except that if the machine is
8415 * saved, S_OK is also returned. This is useful in setters which allow
8416 * changing runtime and saved state changeable machine properties.
8417 *
8418 * @param aDepType Dependency type to check.
8419 *
8420 * @note Non Machine based classes should use #i_addStateDependency() and
8421 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8422 * template.
8423 *
8424 * @note This method must be called from under this object's read or write
8425 * lock.
8426 */
8427HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8428{
8429 switch (aDepType)
8430 {
8431 case AnyStateDep:
8432 {
8433 break;
8434 }
8435 case MutableStateDep:
8436 {
8437 if ( mData->mRegistered
8438 && ( !i_isSessionMachine()
8439 || ( mData->mMachineState != MachineState_Aborted
8440 && mData->mMachineState != MachineState_Teleported
8441 && mData->mMachineState != MachineState_PoweredOff
8442 )
8443 )
8444 )
8445 return setError(VBOX_E_INVALID_VM_STATE,
8446 tr("The machine is not mutable (state is %s)"),
8447 Global::stringifyMachineState(mData->mMachineState));
8448 break;
8449 }
8450 case MutableOrSavedStateDep:
8451 {
8452 if ( mData->mRegistered
8453 && ( !i_isSessionMachine()
8454 || ( mData->mMachineState != MachineState_Aborted
8455 && mData->mMachineState != MachineState_Teleported
8456 && mData->mMachineState != MachineState_Saved
8457 && mData->mMachineState != MachineState_PoweredOff
8458 )
8459 )
8460 )
8461 return setError(VBOX_E_INVALID_VM_STATE,
8462 tr("The machine is not mutable or saved (state is %s)"),
8463 Global::stringifyMachineState(mData->mMachineState));
8464 break;
8465 }
8466 case MutableOrRunningStateDep:
8467 {
8468 if ( mData->mRegistered
8469 && ( !i_isSessionMachine()
8470 || ( mData->mMachineState != MachineState_Aborted
8471 && mData->mMachineState != MachineState_Teleported
8472 && mData->mMachineState != MachineState_PoweredOff
8473 && !Global::IsOnline(mData->mMachineState)
8474 )
8475 )
8476 )
8477 return setError(VBOX_E_INVALID_VM_STATE,
8478 tr("The machine is not mutable or running (state is %s)"),
8479 Global::stringifyMachineState(mData->mMachineState));
8480 break;
8481 }
8482 case MutableOrSavedOrRunningStateDep:
8483 {
8484 if ( mData->mRegistered
8485 && ( !i_isSessionMachine()
8486 || ( mData->mMachineState != MachineState_Aborted
8487 && mData->mMachineState != MachineState_Teleported
8488 && mData->mMachineState != MachineState_Saved
8489 && mData->mMachineState != MachineState_PoweredOff
8490 && !Global::IsOnline(mData->mMachineState)
8491 )
8492 )
8493 )
8494 return setError(VBOX_E_INVALID_VM_STATE,
8495 tr("The machine is not mutable, saved or running (state is %s)"),
8496 Global::stringifyMachineState(mData->mMachineState));
8497 break;
8498 }
8499 }
8500
8501 return S_OK;
8502}
8503
8504/**
8505 * Helper to initialize all associated child objects and allocate data
8506 * structures.
8507 *
8508 * This method must be called as a part of the object's initialization procedure
8509 * (usually done in the #init() method).
8510 *
8511 * @note Must be called only from #init() or from #i_registeredInit().
8512 */
8513HRESULT Machine::initDataAndChildObjects()
8514{
8515 AutoCaller autoCaller(this);
8516 AssertComRCReturnRC(autoCaller.rc());
8517 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8518 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8519
8520 AssertReturn(!mData->mAccessible, E_FAIL);
8521
8522 /* allocate data structures */
8523 mSSData.allocate();
8524 mUserData.allocate();
8525 mHWData.allocate();
8526 mMediumAttachments.allocate();
8527 mStorageControllers.allocate();
8528 mUSBControllers.allocate();
8529
8530 /* initialize mOSTypeId */
8531 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8532
8533/** @todo r=bird: init() methods never fails, right? Why don't we make them
8534 * return void then! */
8535
8536 /* create associated BIOS settings object */
8537 unconst(mBIOSSettings).createObject();
8538 mBIOSSettings->init(this);
8539
8540 /* create an associated VRDE object (default is disabled) */
8541 unconst(mVRDEServer).createObject();
8542 mVRDEServer->init(this);
8543
8544 /* create associated serial port objects */
8545 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8546 {
8547 unconst(mSerialPorts[slot]).createObject();
8548 mSerialPorts[slot]->init(this, slot);
8549 }
8550
8551 /* create associated parallel port objects */
8552 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8553 {
8554 unconst(mParallelPorts[slot]).createObject();
8555 mParallelPorts[slot]->init(this, slot);
8556 }
8557
8558 /* create the audio adapter object (always present, default is disabled) */
8559 unconst(mAudioAdapter).createObject();
8560 mAudioAdapter->init(this);
8561
8562 /* create the USB device filters object (always present) */
8563 unconst(mUSBDeviceFilters).createObject();
8564 mUSBDeviceFilters->init(this);
8565
8566 /* create associated network adapter objects */
8567 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8568 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8569 {
8570 unconst(mNetworkAdapters[slot]).createObject();
8571 mNetworkAdapters[slot]->init(this, slot);
8572 }
8573
8574 /* create the bandwidth control */
8575 unconst(mBandwidthControl).createObject();
8576 mBandwidthControl->init(this);
8577
8578 return S_OK;
8579}
8580
8581/**
8582 * Helper to uninitialize all associated child objects and to free all data
8583 * structures.
8584 *
8585 * This method must be called as a part of the object's uninitialization
8586 * procedure (usually done in the #uninit() method).
8587 *
8588 * @note Must be called only from #uninit() or from #i_registeredInit().
8589 */
8590void Machine::uninitDataAndChildObjects()
8591{
8592 AutoCaller autoCaller(this);
8593 AssertComRCReturnVoid(autoCaller.rc());
8594 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8595 || getObjectState().getState() == ObjectState::Limited);
8596
8597 /* tell all our other child objects we've been uninitialized */
8598 if (mBandwidthControl)
8599 {
8600 mBandwidthControl->uninit();
8601 unconst(mBandwidthControl).setNull();
8602 }
8603
8604 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8605 {
8606 if (mNetworkAdapters[slot])
8607 {
8608 mNetworkAdapters[slot]->uninit();
8609 unconst(mNetworkAdapters[slot]).setNull();
8610 }
8611 }
8612
8613 if (mUSBDeviceFilters)
8614 {
8615 mUSBDeviceFilters->uninit();
8616 unconst(mUSBDeviceFilters).setNull();
8617 }
8618
8619 if (mAudioAdapter)
8620 {
8621 mAudioAdapter->uninit();
8622 unconst(mAudioAdapter).setNull();
8623 }
8624
8625 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8626 {
8627 if (mParallelPorts[slot])
8628 {
8629 mParallelPorts[slot]->uninit();
8630 unconst(mParallelPorts[slot]).setNull();
8631 }
8632 }
8633
8634 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8635 {
8636 if (mSerialPorts[slot])
8637 {
8638 mSerialPorts[slot]->uninit();
8639 unconst(mSerialPorts[slot]).setNull();
8640 }
8641 }
8642
8643 if (mVRDEServer)
8644 {
8645 mVRDEServer->uninit();
8646 unconst(mVRDEServer).setNull();
8647 }
8648
8649 if (mBIOSSettings)
8650 {
8651 mBIOSSettings->uninit();
8652 unconst(mBIOSSettings).setNull();
8653 }
8654
8655 /* Deassociate media (only when a real Machine or a SnapshotMachine
8656 * instance is uninitialized; SessionMachine instances refer to real
8657 * Machine media). This is necessary for a clean re-initialization of
8658 * the VM after successfully re-checking the accessibility state. Note
8659 * that in case of normal Machine or SnapshotMachine uninitialization (as
8660 * a result of unregistering or deleting the snapshot), outdated media
8661 * attachments will already be uninitialized and deleted, so this
8662 * code will not affect them. */
8663 if ( !mMediumAttachments.isNull()
8664 && !i_isSessionMachine()
8665 )
8666 {
8667 for (MediumAttachmentList::const_iterator
8668 it = mMediumAttachments->begin();
8669 it != mMediumAttachments->end();
8670 ++it)
8671 {
8672 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8673 if (pMedium.isNull())
8674 continue;
8675 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8676 AssertComRC(rc);
8677 }
8678 }
8679
8680 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8681 {
8682 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8683 if (mData->mFirstSnapshot)
8684 {
8685 // snapshots tree is protected by machine write lock; strictly
8686 // this isn't necessary here since we're deleting the entire
8687 // machine, but otherwise we assert in Snapshot::uninit()
8688 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8689 mData->mFirstSnapshot->uninit();
8690 mData->mFirstSnapshot.setNull();
8691 }
8692
8693 mData->mCurrentSnapshot.setNull();
8694 }
8695
8696 /* free data structures (the essential mData structure is not freed here
8697 * since it may be still in use) */
8698 mMediumAttachments.free();
8699 mStorageControllers.free();
8700 mUSBControllers.free();
8701 mHWData.free();
8702 mUserData.free();
8703 mSSData.free();
8704}
8705
8706/**
8707 * Returns a pointer to the Machine object for this machine that acts like a
8708 * parent for complex machine data objects such as shared folders, etc.
8709 *
8710 * For primary Machine objects and for SnapshotMachine objects, returns this
8711 * object's pointer itself. For SessionMachine objects, returns the peer
8712 * (primary) machine pointer.
8713 */
8714Machine *Machine::i_getMachine()
8715{
8716 if (i_isSessionMachine())
8717 return (Machine*)mPeer;
8718 return this;
8719}
8720
8721/**
8722 * Makes sure that there are no machine state dependents. If necessary, waits
8723 * for the number of dependents to drop to zero.
8724 *
8725 * Make sure this method is called from under this object's write lock to
8726 * guarantee that no new dependents may be added when this method returns
8727 * control to the caller.
8728 *
8729 * @note Locks this object for writing. The lock will be released while waiting
8730 * (if necessary).
8731 *
8732 * @warning To be used only in methods that change the machine state!
8733 */
8734void Machine::i_ensureNoStateDependencies()
8735{
8736 AssertReturnVoid(isWriteLockOnCurrentThread());
8737
8738 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8739
8740 /* Wait for all state dependents if necessary */
8741 if (mData->mMachineStateDeps != 0)
8742 {
8743 /* lazy semaphore creation */
8744 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8745 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8746
8747 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8748 mData->mMachineStateDeps));
8749
8750 ++mData->mMachineStateChangePending;
8751
8752 /* reset the semaphore before waiting, the last dependent will signal
8753 * it */
8754 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8755
8756 alock.release();
8757
8758 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8759
8760 alock.acquire();
8761
8762 -- mData->mMachineStateChangePending;
8763 }
8764}
8765
8766/**
8767 * Changes the machine state and informs callbacks.
8768 *
8769 * This method is not intended to fail so it either returns S_OK or asserts (and
8770 * returns a failure).
8771 *
8772 * @note Locks this object for writing.
8773 */
8774HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8775{
8776 LogFlowThisFuncEnter();
8777 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8778 Assert(aMachineState != MachineState_Null);
8779
8780 AutoCaller autoCaller(this);
8781 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8782
8783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8784
8785 /* wait for state dependents to drop to zero */
8786 i_ensureNoStateDependencies();
8787
8788 MachineState_T const enmOldState = mData->mMachineState;
8789 if (enmOldState != aMachineState)
8790 {
8791 mData->mMachineState = aMachineState;
8792 RTTimeNow(&mData->mLastStateChange);
8793
8794#ifdef VBOX_WITH_DTRACE_R3_MAIN
8795 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8796#endif
8797 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8798 }
8799
8800 LogFlowThisFuncLeave();
8801 return S_OK;
8802}
8803
8804/**
8805 * Searches for a shared folder with the given logical name
8806 * in the collection of shared folders.
8807 *
8808 * @param aName logical name of the shared folder
8809 * @param aSharedFolder where to return the found object
8810 * @param aSetError whether to set the error info if the folder is
8811 * not found
8812 * @return
8813 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8814 *
8815 * @note
8816 * must be called from under the object's lock!
8817 */
8818HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8819 ComObjPtr<SharedFolder> &aSharedFolder,
8820 bool aSetError /* = false */)
8821{
8822 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8823 for (HWData::SharedFolderList::const_iterator
8824 it = mHWData->mSharedFolders.begin();
8825 it != mHWData->mSharedFolders.end();
8826 ++it)
8827 {
8828 SharedFolder *pSF = *it;
8829 AutoCaller autoCaller(pSF);
8830 if (pSF->i_getName() == aName)
8831 {
8832 aSharedFolder = pSF;
8833 rc = S_OK;
8834 break;
8835 }
8836 }
8837
8838 if (aSetError && FAILED(rc))
8839 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8840
8841 return rc;
8842}
8843
8844/**
8845 * Initializes all machine instance data from the given settings structures
8846 * from XML. The exception is the machine UUID which needs special handling
8847 * depending on the caller's use case, so the caller needs to set that herself.
8848 *
8849 * This gets called in several contexts during machine initialization:
8850 *
8851 * -- When machine XML exists on disk already and needs to be loaded into memory,
8852 * for example, from #i_registeredInit() to load all registered machines on
8853 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8854 * attached to the machine should be part of some media registry already.
8855 *
8856 * -- During OVF import, when a machine config has been constructed from an
8857 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8858 * ensure that the media listed as attachments in the config (which have
8859 * been imported from the OVF) receive the correct registry ID.
8860 *
8861 * -- During VM cloning.
8862 *
8863 * @param config Machine settings from XML.
8864 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8865 * for each attached medium in the config.
8866 * @return
8867 */
8868HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8869 const Guid *puuidRegistry)
8870{
8871 // copy name, description, OS type, teleporter, UTC etc.
8872 mUserData->s = config.machineUserData;
8873
8874 // look up the object by Id to check it is valid
8875 ComObjPtr<GuestOSType> pGuestOSType;
8876 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8877 if (!pGuestOSType.isNull())
8878 mUserData->s.strOsType = pGuestOSType->i_id();
8879
8880 // stateFile (optional)
8881 if (config.strStateFile.isEmpty())
8882 mSSData->strStateFilePath.setNull();
8883 else
8884 {
8885 Utf8Str stateFilePathFull(config.strStateFile);
8886 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8887 if (RT_FAILURE(vrc))
8888 return setErrorBoth(E_FAIL, vrc,
8889 tr("Invalid saved state file path '%s' (%Rrc)"),
8890 config.strStateFile.c_str(),
8891 vrc);
8892 mSSData->strStateFilePath = stateFilePathFull;
8893 }
8894
8895 // snapshot folder needs special processing so set it again
8896 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8897 if (FAILED(rc)) return rc;
8898
8899 /* Copy the extra data items (config may or may not be the same as
8900 * mData->pMachineConfigFile) if necessary. When loading the XML files
8901 * from disk they are the same, but not for OVF import. */
8902 if (mData->pMachineConfigFile != &config)
8903 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8904
8905 /* currentStateModified (optional, default is true) */
8906 mData->mCurrentStateModified = config.fCurrentStateModified;
8907
8908 mData->mLastStateChange = config.timeLastStateChange;
8909
8910 /*
8911 * note: all mUserData members must be assigned prior this point because
8912 * we need to commit changes in order to let mUserData be shared by all
8913 * snapshot machine instances.
8914 */
8915 mUserData.commitCopy();
8916
8917 // machine registry, if present (must be loaded before snapshots)
8918 if (config.canHaveOwnMediaRegistry())
8919 {
8920 // determine machine folder
8921 Utf8Str strMachineFolder = i_getSettingsFileFull();
8922 strMachineFolder.stripFilename();
8923 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8924 config.mediaRegistry,
8925 strMachineFolder);
8926 if (FAILED(rc)) return rc;
8927 }
8928
8929 /* Snapshot node (optional) */
8930 size_t cRootSnapshots;
8931 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8932 {
8933 // there must be only one root snapshot
8934 Assert(cRootSnapshots == 1);
8935
8936 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8937
8938 rc = i_loadSnapshot(snap,
8939 config.uuidCurrentSnapshot,
8940 NULL); // no parent == first snapshot
8941 if (FAILED(rc)) return rc;
8942 }
8943
8944 // hardware data
8945 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8946 if (FAILED(rc)) return rc;
8947
8948 /*
8949 * NOTE: the assignment below must be the last thing to do,
8950 * otherwise it will be not possible to change the settings
8951 * somewhere in the code above because all setters will be
8952 * blocked by i_checkStateDependency(MutableStateDep).
8953 */
8954
8955 /* set the machine state to Aborted or Saved when appropriate */
8956 if (config.fAborted)
8957 {
8958 mSSData->strStateFilePath.setNull();
8959
8960 /* no need to use i_setMachineState() during init() */
8961 mData->mMachineState = MachineState_Aborted;
8962 }
8963 else if (!mSSData->strStateFilePath.isEmpty())
8964 {
8965 /* no need to use i_setMachineState() during init() */
8966 mData->mMachineState = MachineState_Saved;
8967 }
8968
8969 // after loading settings, we are no longer different from the XML on disk
8970 mData->flModifications = 0;
8971
8972 return S_OK;
8973}
8974
8975/**
8976 * Recursively loads all snapshots starting from the given.
8977 *
8978 * @param data snapshot settings.
8979 * @param aCurSnapshotId Current snapshot ID from the settings file.
8980 * @param aParentSnapshot Parent snapshot.
8981 */
8982HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8983 const Guid &aCurSnapshotId,
8984 Snapshot *aParentSnapshot)
8985{
8986 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8987 AssertReturn(!i_isSessionMachine(), E_FAIL);
8988
8989 HRESULT rc = S_OK;
8990
8991 Utf8Str strStateFile;
8992 if (!data.strStateFile.isEmpty())
8993 {
8994 /* optional */
8995 strStateFile = data.strStateFile;
8996 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8997 if (RT_FAILURE(vrc))
8998 return setErrorBoth(E_FAIL, vrc,
8999 tr("Invalid saved state file path '%s' (%Rrc)"),
9000 strStateFile.c_str(),
9001 vrc);
9002 }
9003
9004 /* create a snapshot machine object */
9005 ComObjPtr<SnapshotMachine> pSnapshotMachine;
9006 pSnapshotMachine.createObject();
9007 rc = pSnapshotMachine->initFromSettings(this,
9008 data.hardware,
9009 &data.debugging,
9010 &data.autostart,
9011 data.uuid.ref(),
9012 strStateFile);
9013 if (FAILED(rc)) return rc;
9014
9015 /* create a snapshot object */
9016 ComObjPtr<Snapshot> pSnapshot;
9017 pSnapshot.createObject();
9018 /* initialize the snapshot */
9019 rc = pSnapshot->init(mParent, // VirtualBox object
9020 data.uuid,
9021 data.strName,
9022 data.strDescription,
9023 data.timestamp,
9024 pSnapshotMachine,
9025 aParentSnapshot);
9026 if (FAILED(rc)) return rc;
9027
9028 /* memorize the first snapshot if necessary */
9029 if (!mData->mFirstSnapshot)
9030 mData->mFirstSnapshot = pSnapshot;
9031
9032 /* memorize the current snapshot when appropriate */
9033 if ( !mData->mCurrentSnapshot
9034 && pSnapshot->i_getId() == aCurSnapshotId
9035 )
9036 mData->mCurrentSnapshot = pSnapshot;
9037
9038 // now create the children
9039 for (settings::SnapshotsList::const_iterator
9040 it = data.llChildSnapshots.begin();
9041 it != data.llChildSnapshots.end();
9042 ++it)
9043 {
9044 const settings::Snapshot &childData = *it;
9045 // recurse
9046 rc = i_loadSnapshot(childData,
9047 aCurSnapshotId,
9048 pSnapshot); // parent = the one we created above
9049 if (FAILED(rc)) return rc;
9050 }
9051
9052 return rc;
9053}
9054
9055/**
9056 * Loads settings into mHWData.
9057 *
9058 * @param puuidRegistry Registry ID.
9059 * @param puuidSnapshot Snapshot ID
9060 * @param data Reference to the hardware settings.
9061 * @param pDbg Pointer to the debugging settings.
9062 * @param pAutostart Pointer to the autostart settings.
9063 */
9064HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
9065 const Guid *puuidSnapshot,
9066 const settings::Hardware &data,
9067 const settings::Debugging *pDbg,
9068 const settings::Autostart *pAutostart)
9069{
9070 AssertReturn(!i_isSessionMachine(), E_FAIL);
9071
9072 HRESULT rc = S_OK;
9073
9074 try
9075 {
9076 ComObjPtr<GuestOSType> pGuestOSType;
9077 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
9078
9079 /* The hardware version attribute (optional). */
9080 mHWData->mHWVersion = data.strVersion;
9081 mHWData->mHardwareUUID = data.uuid;
9082
9083 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
9084 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
9085 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
9086 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
9087 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
9088 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
9089 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
9090 mHWData->mPAEEnabled = data.fPAE;
9091 mHWData->mLongMode = data.enmLongMode;
9092 mHWData->mTripleFaultReset = data.fTripleFaultReset;
9093 mHWData->mAPIC = data.fAPIC;
9094 mHWData->mX2APIC = data.fX2APIC;
9095 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
9096 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
9097 mHWData->mSpecCtrl = data.fSpecCtrl;
9098 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
9099 mHWData->mNestedHWVirt = data.fNestedHWVirt;
9100 mHWData->mCPUCount = data.cCPUs;
9101 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
9102 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
9103 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
9104 mHWData->mCpuProfile = data.strCpuProfile;
9105
9106 // cpu
9107 if (mHWData->mCPUHotPlugEnabled)
9108 {
9109 for (settings::CpuList::const_iterator
9110 it = data.llCpus.begin();
9111 it != data.llCpus.end();
9112 ++it)
9113 {
9114 const settings::Cpu &cpu = *it;
9115
9116 mHWData->mCPUAttached[cpu.ulId] = true;
9117 }
9118 }
9119
9120 // cpuid leafs
9121 for (settings::CpuIdLeafsList::const_iterator
9122 it = data.llCpuIdLeafs.begin();
9123 it != data.llCpuIdLeafs.end();
9124 ++it)
9125 {
9126 const settings::CpuIdLeaf &rLeaf= *it;
9127 if ( rLeaf.idx < UINT32_C(0x20)
9128 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
9129 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
9130 mHWData->mCpuIdLeafList.push_back(rLeaf);
9131 /* else: just ignore */
9132 }
9133
9134 mHWData->mMemorySize = data.ulMemorySizeMB;
9135 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
9136
9137 // boot order
9138 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
9139 {
9140 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
9141 if (it == data.mapBootOrder.end())
9142 mHWData->mBootOrder[i] = DeviceType_Null;
9143 else
9144 mHWData->mBootOrder[i] = it->second;
9145 }
9146
9147 mHWData->mGraphicsControllerType = data.graphicsControllerType;
9148 mHWData->mVRAMSize = data.ulVRAMSizeMB;
9149 mHWData->mMonitorCount = data.cMonitors;
9150 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
9151 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
9152 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
9153 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
9154 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
9155 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
9156 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
9157 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
9158 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
9159 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
9160 if (!data.strVideoCaptureFile.isEmpty())
9161 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
9162 else
9163 mHWData->mVideoCaptureFile.setNull();
9164 mHWData->mVideoCaptureOptions = data.strVideoCaptureOptions;
9165 mHWData->mFirmwareType = data.firmwareType;
9166 mHWData->mPointingHIDType = data.pointingHIDType;
9167 mHWData->mKeyboardHIDType = data.keyboardHIDType;
9168 mHWData->mChipsetType = data.chipsetType;
9169 mHWData->mParavirtProvider = data.paravirtProvider;
9170 mHWData->mParavirtDebug = data.strParavirtDebug;
9171 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
9172 mHWData->mHPETEnabled = data.fHPETEnabled;
9173
9174 /* VRDEServer */
9175 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
9176 if (FAILED(rc)) return rc;
9177
9178 /* BIOS */
9179 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
9180 if (FAILED(rc)) return rc;
9181
9182 // Bandwidth control (must come before network adapters)
9183 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
9184 if (FAILED(rc)) return rc;
9185
9186 /* Shared folders */
9187 for (settings::USBControllerList::const_iterator
9188 it = data.usbSettings.llUSBControllers.begin();
9189 it != data.usbSettings.llUSBControllers.end();
9190 ++it)
9191 {
9192 const settings::USBController &settingsCtrl = *it;
9193 ComObjPtr<USBController> newCtrl;
9194
9195 newCtrl.createObject();
9196 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
9197 mUSBControllers->push_back(newCtrl);
9198 }
9199
9200 /* USB device filters */
9201 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
9202 if (FAILED(rc)) return rc;
9203
9204 // network adapters (establish array size first and apply defaults, to
9205 // ensure reading the same settings as we saved, since the list skips
9206 // adapters having defaults)
9207 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
9208 size_t oldCount = mNetworkAdapters.size();
9209 if (newCount > oldCount)
9210 {
9211 mNetworkAdapters.resize(newCount);
9212 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
9213 {
9214 unconst(mNetworkAdapters[slot]).createObject();
9215 mNetworkAdapters[slot]->init(this, (ULONG)slot);
9216 }
9217 }
9218 else if (newCount < oldCount)
9219 mNetworkAdapters.resize(newCount);
9220 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
9221 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
9222 for (settings::NetworkAdaptersList::const_iterator
9223 it = data.llNetworkAdapters.begin();
9224 it != data.llNetworkAdapters.end();
9225 ++it)
9226 {
9227 const settings::NetworkAdapter &nic = *it;
9228
9229 /* slot uniqueness is guaranteed by XML Schema */
9230 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
9231 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
9232 if (FAILED(rc)) return rc;
9233 }
9234
9235 // serial ports (establish defaults first, to ensure reading the same
9236 // settings as we saved, since the list skips ports having defaults)
9237 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
9238 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
9239 for (settings::SerialPortsList::const_iterator
9240 it = data.llSerialPorts.begin();
9241 it != data.llSerialPorts.end();
9242 ++it)
9243 {
9244 const settings::SerialPort &s = *it;
9245
9246 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
9247 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
9248 if (FAILED(rc)) return rc;
9249 }
9250
9251 // parallel ports (establish defaults first, to ensure reading the same
9252 // settings as we saved, since the list skips ports having defaults)
9253 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9254 mParallelPorts[i]->i_applyDefaults();
9255 for (settings::ParallelPortsList::const_iterator
9256 it = data.llParallelPorts.begin();
9257 it != data.llParallelPorts.end();
9258 ++it)
9259 {
9260 const settings::ParallelPort &p = *it;
9261
9262 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9263 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9264 if (FAILED(rc)) return rc;
9265 }
9266
9267 /* AudioAdapter */
9268 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9269 if (FAILED(rc)) return rc;
9270
9271 /* storage controllers */
9272 rc = i_loadStorageControllers(data.storage,
9273 puuidRegistry,
9274 puuidSnapshot);
9275 if (FAILED(rc)) return rc;
9276
9277 /* Shared folders */
9278 for (settings::SharedFoldersList::const_iterator
9279 it = data.llSharedFolders.begin();
9280 it != data.llSharedFolders.end();
9281 ++it)
9282 {
9283 const settings::SharedFolder &sf = *it;
9284
9285 ComObjPtr<SharedFolder> sharedFolder;
9286 /* Check for double entries. Not allowed! */
9287 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9288 if (SUCCEEDED(rc))
9289 return setError(VBOX_E_OBJECT_IN_USE,
9290 tr("Shared folder named '%s' already exists"),
9291 sf.strName.c_str());
9292
9293 /* Create the new shared folder. Don't break on error. This will be
9294 * reported when the machine starts. */
9295 sharedFolder.createObject();
9296 rc = sharedFolder->init(i_getMachine(),
9297 sf.strName,
9298 sf.strHostPath,
9299 RT_BOOL(sf.fWritable),
9300 RT_BOOL(sf.fAutoMount),
9301 false /* fFailOnError */);
9302 if (FAILED(rc)) return rc;
9303 mHWData->mSharedFolders.push_back(sharedFolder);
9304 }
9305
9306 // Clipboard
9307 mHWData->mClipboardMode = data.clipboardMode;
9308
9309 // drag'n'drop
9310 mHWData->mDnDMode = data.dndMode;
9311
9312 // guest settings
9313 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9314
9315 // IO settings
9316 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9317 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9318
9319 // Host PCI devices
9320 for (settings::HostPCIDeviceAttachmentList::const_iterator
9321 it = data.pciAttachments.begin();
9322 it != data.pciAttachments.end();
9323 ++it)
9324 {
9325 const settings::HostPCIDeviceAttachment &hpda = *it;
9326 ComObjPtr<PCIDeviceAttachment> pda;
9327
9328 pda.createObject();
9329 pda->i_loadSettings(this, hpda);
9330 mHWData->mPCIDeviceAssignments.push_back(pda);
9331 }
9332
9333 /*
9334 * (The following isn't really real hardware, but it lives in HWData
9335 * for reasons of convenience.)
9336 */
9337
9338#ifdef VBOX_WITH_GUEST_PROPS
9339 /* Guest properties (optional) */
9340
9341 /* Only load transient guest properties for configs which have saved
9342 * state, because there shouldn't be any for powered off VMs. The same
9343 * logic applies for snapshots, as offline snapshots shouldn't have
9344 * any such properties. They confuse the code in various places.
9345 * Note: can't rely on the machine state, as it isn't set yet. */
9346 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9347 /* apologies for the hacky unconst() usage, but this needs hacking
9348 * actually inconsistent settings into consistency, otherwise there
9349 * will be some corner cases where the inconsistency survives
9350 * surprisingly long without getting fixed, especially for snapshots
9351 * as there are no config changes. */
9352 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9353 for (settings::GuestPropertiesList::iterator
9354 it = llGuestProperties.begin();
9355 it != llGuestProperties.end();
9356 /*nothing*/)
9357 {
9358 const settings::GuestProperty &prop = *it;
9359 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9360 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9361 if ( fSkipTransientGuestProperties
9362 && ( fFlags & GUEST_PROP_F_TRANSIENT
9363 || fFlags & GUEST_PROP_F_TRANSRESET))
9364 {
9365 it = llGuestProperties.erase(it);
9366 continue;
9367 }
9368 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9369 mHWData->mGuestProperties[prop.strName] = property;
9370 ++it;
9371 }
9372#endif /* VBOX_WITH_GUEST_PROPS defined */
9373
9374 rc = i_loadDebugging(pDbg);
9375 if (FAILED(rc))
9376 return rc;
9377
9378 mHWData->mAutostart = *pAutostart;
9379
9380 /* default frontend */
9381 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9382 }
9383 catch (std::bad_alloc &)
9384 {
9385 return E_OUTOFMEMORY;
9386 }
9387
9388 AssertComRC(rc);
9389 return rc;
9390}
9391
9392/**
9393 * Called from i_loadHardware() to load the debugging settings of the
9394 * machine.
9395 *
9396 * @param pDbg Pointer to the settings.
9397 */
9398HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9399{
9400 mHWData->mDebugging = *pDbg;
9401 /* no more processing currently required, this will probably change. */
9402 return S_OK;
9403}
9404
9405/**
9406 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9407 *
9408 * @param data storage settings.
9409 * @param puuidRegistry media registry ID to set media to or NULL;
9410 * see Machine::i_loadMachineDataFromSettings()
9411 * @param puuidSnapshot snapshot ID
9412 * @return
9413 */
9414HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9415 const Guid *puuidRegistry,
9416 const Guid *puuidSnapshot)
9417{
9418 AssertReturn(!i_isSessionMachine(), E_FAIL);
9419
9420 HRESULT rc = S_OK;
9421
9422 for (settings::StorageControllersList::const_iterator
9423 it = data.llStorageControllers.begin();
9424 it != data.llStorageControllers.end();
9425 ++it)
9426 {
9427 const settings::StorageController &ctlData = *it;
9428
9429 ComObjPtr<StorageController> pCtl;
9430 /* Try to find one with the name first. */
9431 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9432 if (SUCCEEDED(rc))
9433 return setError(VBOX_E_OBJECT_IN_USE,
9434 tr("Storage controller named '%s' already exists"),
9435 ctlData.strName.c_str());
9436
9437 pCtl.createObject();
9438 rc = pCtl->init(this,
9439 ctlData.strName,
9440 ctlData.storageBus,
9441 ctlData.ulInstance,
9442 ctlData.fBootable);
9443 if (FAILED(rc)) return rc;
9444
9445 mStorageControllers->push_back(pCtl);
9446
9447 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9448 if (FAILED(rc)) return rc;
9449
9450 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9451 if (FAILED(rc)) return rc;
9452
9453 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9454 if (FAILED(rc)) return rc;
9455
9456 /* Load the attached devices now. */
9457 rc = i_loadStorageDevices(pCtl,
9458 ctlData,
9459 puuidRegistry,
9460 puuidSnapshot);
9461 if (FAILED(rc)) return rc;
9462 }
9463
9464 return S_OK;
9465}
9466
9467/**
9468 * Called from i_loadStorageControllers for a controller's devices.
9469 *
9470 * @param aStorageController
9471 * @param data
9472 * @param puuidRegistry media registry ID to set media to or NULL; see
9473 * Machine::i_loadMachineDataFromSettings()
9474 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9475 * @return
9476 */
9477HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9478 const settings::StorageController &data,
9479 const Guid *puuidRegistry,
9480 const Guid *puuidSnapshot)
9481{
9482 HRESULT rc = S_OK;
9483
9484 /* paranoia: detect duplicate attachments */
9485 for (settings::AttachedDevicesList::const_iterator
9486 it = data.llAttachedDevices.begin();
9487 it != data.llAttachedDevices.end();
9488 ++it)
9489 {
9490 const settings::AttachedDevice &ad = *it;
9491
9492 for (settings::AttachedDevicesList::const_iterator it2 = it;
9493 it2 != data.llAttachedDevices.end();
9494 ++it2)
9495 {
9496 if (it == it2)
9497 continue;
9498
9499 const settings::AttachedDevice &ad2 = *it2;
9500
9501 if ( ad.lPort == ad2.lPort
9502 && ad.lDevice == ad2.lDevice)
9503 {
9504 return setError(E_FAIL,
9505 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9506 aStorageController->i_getName().c_str(),
9507 ad.lPort,
9508 ad.lDevice,
9509 mUserData->s.strName.c_str());
9510 }
9511 }
9512 }
9513
9514 for (settings::AttachedDevicesList::const_iterator
9515 it = data.llAttachedDevices.begin();
9516 it != data.llAttachedDevices.end();
9517 ++it)
9518 {
9519 const settings::AttachedDevice &dev = *it;
9520 ComObjPtr<Medium> medium;
9521
9522 switch (dev.deviceType)
9523 {
9524 case DeviceType_Floppy:
9525 case DeviceType_DVD:
9526 if (dev.strHostDriveSrc.isNotEmpty())
9527 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9528 false /* fRefresh */, medium);
9529 else
9530 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9531 dev.uuid,
9532 false /* fRefresh */,
9533 false /* aSetError */,
9534 medium);
9535 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9536 // This is not an error. The host drive or UUID might have vanished, so just go
9537 // ahead without this removeable medium attachment
9538 rc = S_OK;
9539 break;
9540
9541 case DeviceType_HardDisk:
9542 {
9543 /* find a hard disk by UUID */
9544 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9545 if (FAILED(rc))
9546 {
9547 if (i_isSnapshotMachine())
9548 {
9549 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9550 // so the user knows that the bad disk is in a snapshot somewhere
9551 com::ErrorInfo info;
9552 return setError(E_FAIL,
9553 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9554 puuidSnapshot->raw(),
9555 info.getText().raw());
9556 }
9557 else
9558 return rc;
9559 }
9560
9561 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9562
9563 if (medium->i_getType() == MediumType_Immutable)
9564 {
9565 if (i_isSnapshotMachine())
9566 return setError(E_FAIL,
9567 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9568 "of the virtual machine '%s' ('%s')"),
9569 medium->i_getLocationFull().c_str(),
9570 dev.uuid.raw(),
9571 puuidSnapshot->raw(),
9572 mUserData->s.strName.c_str(),
9573 mData->m_strConfigFileFull.c_str());
9574
9575 return setError(E_FAIL,
9576 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9577 medium->i_getLocationFull().c_str(),
9578 dev.uuid.raw(),
9579 mUserData->s.strName.c_str(),
9580 mData->m_strConfigFileFull.c_str());
9581 }
9582
9583 if (medium->i_getType() == MediumType_MultiAttach)
9584 {
9585 if (i_isSnapshotMachine())
9586 return setError(E_FAIL,
9587 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9588 "of the virtual machine '%s' ('%s')"),
9589 medium->i_getLocationFull().c_str(),
9590 dev.uuid.raw(),
9591 puuidSnapshot->raw(),
9592 mUserData->s.strName.c_str(),
9593 mData->m_strConfigFileFull.c_str());
9594
9595 return setError(E_FAIL,
9596 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9597 medium->i_getLocationFull().c_str(),
9598 dev.uuid.raw(),
9599 mUserData->s.strName.c_str(),
9600 mData->m_strConfigFileFull.c_str());
9601 }
9602
9603 if ( !i_isSnapshotMachine()
9604 && medium->i_getChildren().size() != 0
9605 )
9606 return setError(E_FAIL,
9607 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9608 "because it has %d differencing child hard disks"),
9609 medium->i_getLocationFull().c_str(),
9610 dev.uuid.raw(),
9611 mUserData->s.strName.c_str(),
9612 mData->m_strConfigFileFull.c_str(),
9613 medium->i_getChildren().size());
9614
9615 if (i_findAttachment(*mMediumAttachments.data(),
9616 medium))
9617 return setError(E_FAIL,
9618 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9619 medium->i_getLocationFull().c_str(),
9620 dev.uuid.raw(),
9621 mUserData->s.strName.c_str(),
9622 mData->m_strConfigFileFull.c_str());
9623
9624 break;
9625 }
9626
9627 default:
9628 return setError(E_FAIL,
9629 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9630 medium->i_getLocationFull().c_str(),
9631 mUserData->s.strName.c_str(),
9632 mData->m_strConfigFileFull.c_str());
9633 }
9634
9635 if (FAILED(rc))
9636 break;
9637
9638 /* Bandwidth groups are loaded at this point. */
9639 ComObjPtr<BandwidthGroup> pBwGroup;
9640
9641 if (!dev.strBwGroup.isEmpty())
9642 {
9643 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9644 if (FAILED(rc))
9645 return setError(E_FAIL,
9646 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9647 medium->i_getLocationFull().c_str(),
9648 dev.strBwGroup.c_str(),
9649 mUserData->s.strName.c_str(),
9650 mData->m_strConfigFileFull.c_str());
9651 pBwGroup->i_reference();
9652 }
9653
9654 const Utf8Str controllerName = aStorageController->i_getName();
9655 ComObjPtr<MediumAttachment> pAttachment;
9656 pAttachment.createObject();
9657 rc = pAttachment->init(this,
9658 medium,
9659 controllerName,
9660 dev.lPort,
9661 dev.lDevice,
9662 dev.deviceType,
9663 false,
9664 dev.fPassThrough,
9665 dev.fTempEject,
9666 dev.fNonRotational,
9667 dev.fDiscard,
9668 dev.fHotPluggable,
9669 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9670 if (FAILED(rc)) break;
9671
9672 /* associate the medium with this machine and snapshot */
9673 if (!medium.isNull())
9674 {
9675 AutoCaller medCaller(medium);
9676 if (FAILED(medCaller.rc())) return medCaller.rc();
9677 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9678
9679 if (i_isSnapshotMachine())
9680 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9681 else
9682 rc = medium->i_addBackReference(mData->mUuid);
9683 /* If the medium->addBackReference fails it sets an appropriate
9684 * error message, so no need to do any guesswork here. */
9685
9686 if (puuidRegistry)
9687 // caller wants registry ID to be set on all attached media (OVF import case)
9688 medium->i_addRegistry(*puuidRegistry);
9689 }
9690
9691 if (FAILED(rc))
9692 break;
9693
9694 /* back up mMediumAttachments to let registeredInit() properly rollback
9695 * on failure (= limited accessibility) */
9696 i_setModified(IsModified_Storage);
9697 mMediumAttachments.backup();
9698 mMediumAttachments->push_back(pAttachment);
9699 }
9700
9701 return rc;
9702}
9703
9704/**
9705 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9706 *
9707 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9708 * @param aSnapshot where to return the found snapshot
9709 * @param aSetError true to set extended error info on failure
9710 */
9711HRESULT Machine::i_findSnapshotById(const Guid &aId,
9712 ComObjPtr<Snapshot> &aSnapshot,
9713 bool aSetError /* = false */)
9714{
9715 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9716
9717 if (!mData->mFirstSnapshot)
9718 {
9719 if (aSetError)
9720 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9721 return E_FAIL;
9722 }
9723
9724 if (aId.isZero())
9725 aSnapshot = mData->mFirstSnapshot;
9726 else
9727 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9728
9729 if (!aSnapshot)
9730 {
9731 if (aSetError)
9732 return setError(E_FAIL,
9733 tr("Could not find a snapshot with UUID {%s}"),
9734 aId.toString().c_str());
9735 return E_FAIL;
9736 }
9737
9738 return S_OK;
9739}
9740
9741/**
9742 * Returns the snapshot with the given name or fails of no such snapshot.
9743 *
9744 * @param strName snapshot name to find
9745 * @param aSnapshot where to return the found snapshot
9746 * @param aSetError true to set extended error info on failure
9747 */
9748HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9749 ComObjPtr<Snapshot> &aSnapshot,
9750 bool aSetError /* = false */)
9751{
9752 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9753
9754 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9755
9756 if (!mData->mFirstSnapshot)
9757 {
9758 if (aSetError)
9759 return setError(VBOX_E_OBJECT_NOT_FOUND,
9760 tr("This machine does not have any snapshots"));
9761 return VBOX_E_OBJECT_NOT_FOUND;
9762 }
9763
9764 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9765
9766 if (!aSnapshot)
9767 {
9768 if (aSetError)
9769 return setError(VBOX_E_OBJECT_NOT_FOUND,
9770 tr("Could not find a snapshot named '%s'"), strName.c_str());
9771 return VBOX_E_OBJECT_NOT_FOUND;
9772 }
9773
9774 return S_OK;
9775}
9776
9777/**
9778 * Returns a storage controller object with the given name.
9779 *
9780 * @param aName storage controller name to find
9781 * @param aStorageController where to return the found storage controller
9782 * @param aSetError true to set extended error info on failure
9783 */
9784HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9785 ComObjPtr<StorageController> &aStorageController,
9786 bool aSetError /* = false */)
9787{
9788 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9789
9790 for (StorageControllerList::const_iterator
9791 it = mStorageControllers->begin();
9792 it != mStorageControllers->end();
9793 ++it)
9794 {
9795 if ((*it)->i_getName() == aName)
9796 {
9797 aStorageController = (*it);
9798 return S_OK;
9799 }
9800 }
9801
9802 if (aSetError)
9803 return setError(VBOX_E_OBJECT_NOT_FOUND,
9804 tr("Could not find a storage controller named '%s'"),
9805 aName.c_str());
9806 return VBOX_E_OBJECT_NOT_FOUND;
9807}
9808
9809/**
9810 * Returns a USB controller object with the given name.
9811 *
9812 * @param aName USB controller name to find
9813 * @param aUSBController where to return the found USB controller
9814 * @param aSetError true to set extended error info on failure
9815 */
9816HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9817 ComObjPtr<USBController> &aUSBController,
9818 bool aSetError /* = false */)
9819{
9820 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9821
9822 for (USBControllerList::const_iterator
9823 it = mUSBControllers->begin();
9824 it != mUSBControllers->end();
9825 ++it)
9826 {
9827 if ((*it)->i_getName() == aName)
9828 {
9829 aUSBController = (*it);
9830 return S_OK;
9831 }
9832 }
9833
9834 if (aSetError)
9835 return setError(VBOX_E_OBJECT_NOT_FOUND,
9836 tr("Could not find a storage controller named '%s'"),
9837 aName.c_str());
9838 return VBOX_E_OBJECT_NOT_FOUND;
9839}
9840
9841/**
9842 * Returns the number of USB controller instance of the given type.
9843 *
9844 * @param enmType USB controller type.
9845 */
9846ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9847{
9848 ULONG cCtrls = 0;
9849
9850 for (USBControllerList::const_iterator
9851 it = mUSBControllers->begin();
9852 it != mUSBControllers->end();
9853 ++it)
9854 {
9855 if ((*it)->i_getControllerType() == enmType)
9856 cCtrls++;
9857 }
9858
9859 return cCtrls;
9860}
9861
9862HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9863 MediumAttachmentList &atts)
9864{
9865 AutoCaller autoCaller(this);
9866 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9867
9868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9869
9870 for (MediumAttachmentList::const_iterator
9871 it = mMediumAttachments->begin();
9872 it != mMediumAttachments->end();
9873 ++it)
9874 {
9875 const ComObjPtr<MediumAttachment> &pAtt = *it;
9876 // should never happen, but deal with NULL pointers in the list.
9877 AssertContinue(!pAtt.isNull());
9878
9879 // getControllerName() needs caller+read lock
9880 AutoCaller autoAttCaller(pAtt);
9881 if (FAILED(autoAttCaller.rc()))
9882 {
9883 atts.clear();
9884 return autoAttCaller.rc();
9885 }
9886 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9887
9888 if (pAtt->i_getControllerName() == aName)
9889 atts.push_back(pAtt);
9890 }
9891
9892 return S_OK;
9893}
9894
9895
9896/**
9897 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9898 * file if the machine name was changed and about creating a new settings file
9899 * if this is a new machine.
9900 *
9901 * @note Must be never called directly but only from #saveSettings().
9902 */
9903HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9904{
9905 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9906
9907 HRESULT rc = S_OK;
9908
9909 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9910
9911 /// @todo need to handle primary group change, too
9912
9913 /* attempt to rename the settings file if machine name is changed */
9914 if ( mUserData->s.fNameSync
9915 && mUserData.isBackedUp()
9916 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9917 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9918 )
9919 {
9920 bool dirRenamed = false;
9921 bool fileRenamed = false;
9922
9923 Utf8Str configFile, newConfigFile;
9924 Utf8Str configFilePrev, newConfigFilePrev;
9925 Utf8Str configDir, newConfigDir;
9926
9927 do
9928 {
9929 int vrc = VINF_SUCCESS;
9930
9931 Utf8Str name = mUserData.backedUpData()->s.strName;
9932 Utf8Str newName = mUserData->s.strName;
9933 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9934 if (group == "/")
9935 group.setNull();
9936 Utf8Str newGroup = mUserData->s.llGroups.front();
9937 if (newGroup == "/")
9938 newGroup.setNull();
9939
9940 configFile = mData->m_strConfigFileFull;
9941
9942 /* first, rename the directory if it matches the group and machine name */
9943 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9944 group.c_str(), RTPATH_DELIMITER, name.c_str());
9945 /** @todo hack, make somehow use of ComposeMachineFilename */
9946 if (mUserData->s.fDirectoryIncludesUUID)
9947 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9948 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9949 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9950 /** @todo hack, make somehow use of ComposeMachineFilename */
9951 if (mUserData->s.fDirectoryIncludesUUID)
9952 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9953 configDir = configFile;
9954 configDir.stripFilename();
9955 newConfigDir = configDir;
9956 if ( configDir.length() >= groupPlusName.length()
9957 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9958 groupPlusName.c_str()))
9959 {
9960 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9961 Utf8Str newConfigBaseDir(newConfigDir);
9962 newConfigDir.append(newGroupPlusName);
9963 /* consistency: use \ if appropriate on the platform */
9964 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9965 /* new dir and old dir cannot be equal here because of 'if'
9966 * above and because name != newName */
9967 Assert(configDir != newConfigDir);
9968 if (!fSettingsFileIsNew)
9969 {
9970 /* perform real rename only if the machine is not new */
9971 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9972 if ( vrc == VERR_FILE_NOT_FOUND
9973 || vrc == VERR_PATH_NOT_FOUND)
9974 {
9975 /* create the parent directory, then retry renaming */
9976 Utf8Str parent(newConfigDir);
9977 parent.stripFilename();
9978 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9979 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9980 }
9981 if (RT_FAILURE(vrc))
9982 {
9983 rc = setErrorBoth(E_FAIL, vrc,
9984 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9985 configDir.c_str(),
9986 newConfigDir.c_str(),
9987 vrc);
9988 break;
9989 }
9990 /* delete subdirectories which are no longer needed */
9991 Utf8Str dir(configDir);
9992 dir.stripFilename();
9993 while (dir != newConfigBaseDir && dir != ".")
9994 {
9995 vrc = RTDirRemove(dir.c_str());
9996 if (RT_FAILURE(vrc))
9997 break;
9998 dir.stripFilename();
9999 }
10000 dirRenamed = true;
10001 }
10002 }
10003
10004 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
10005 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
10006
10007 /* then try to rename the settings file itself */
10008 if (newConfigFile != configFile)
10009 {
10010 /* get the path to old settings file in renamed directory */
10011 configFile = Utf8StrFmt("%s%c%s",
10012 newConfigDir.c_str(),
10013 RTPATH_DELIMITER,
10014 RTPathFilename(configFile.c_str()));
10015 if (!fSettingsFileIsNew)
10016 {
10017 /* perform real rename only if the machine is not new */
10018 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
10019 if (RT_FAILURE(vrc))
10020 {
10021 rc = setErrorBoth(E_FAIL, vrc,
10022 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
10023 configFile.c_str(),
10024 newConfigFile.c_str(),
10025 vrc);
10026 break;
10027 }
10028 fileRenamed = true;
10029 configFilePrev = configFile;
10030 configFilePrev += "-prev";
10031 newConfigFilePrev = newConfigFile;
10032 newConfigFilePrev += "-prev";
10033 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
10034 }
10035 }
10036
10037 // update m_strConfigFileFull amd mConfigFile
10038 mData->m_strConfigFileFull = newConfigFile;
10039 // compute the relative path too
10040 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
10041
10042 // store the old and new so that VirtualBox::i_saveSettings() can update
10043 // the media registry
10044 if ( mData->mRegistered
10045 && (configDir != newConfigDir || configFile != newConfigFile))
10046 {
10047 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
10048
10049 if (pfNeedsGlobalSaveSettings)
10050 *pfNeedsGlobalSaveSettings = true;
10051 }
10052
10053 // in the saved state file path, replace the old directory with the new directory
10054 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
10055 {
10056 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
10057 mSSData->strStateFilePath = newConfigDir + strStateFileName;
10058 }
10059
10060 // and do the same thing for the saved state file paths of all the online snapshots
10061 if (mData->mFirstSnapshot)
10062 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
10063 newConfigDir.c_str());
10064 }
10065 while (0);
10066
10067 if (FAILED(rc))
10068 {
10069 /* silently try to rename everything back */
10070 if (fileRenamed)
10071 {
10072 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
10073 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
10074 }
10075 if (dirRenamed)
10076 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
10077 }
10078
10079 if (FAILED(rc)) return rc;
10080 }
10081
10082 if (fSettingsFileIsNew)
10083 {
10084 /* create a virgin config file */
10085 int vrc = VINF_SUCCESS;
10086
10087 /* ensure the settings directory exists */
10088 Utf8Str path(mData->m_strConfigFileFull);
10089 path.stripFilename();
10090 if (!RTDirExists(path.c_str()))
10091 {
10092 vrc = RTDirCreateFullPath(path.c_str(), 0700);
10093 if (RT_FAILURE(vrc))
10094 {
10095 return setErrorBoth(E_FAIL, vrc,
10096 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
10097 path.c_str(),
10098 vrc);
10099 }
10100 }
10101
10102 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
10103 path = Utf8Str(mData->m_strConfigFileFull);
10104 RTFILE f = NIL_RTFILE;
10105 vrc = RTFileOpen(&f, path.c_str(),
10106 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
10107 if (RT_FAILURE(vrc))
10108 return setErrorBoth(E_FAIL, vrc,
10109 tr("Could not create the settings file '%s' (%Rrc)"),
10110 path.c_str(),
10111 vrc);
10112 RTFileClose(f);
10113 }
10114
10115 return rc;
10116}
10117
10118/**
10119 * Saves and commits machine data, user data and hardware data.
10120 *
10121 * Note that on failure, the data remains uncommitted.
10122 *
10123 * @a aFlags may combine the following flags:
10124 *
10125 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
10126 * Used when saving settings after an operation that makes them 100%
10127 * correspond to the settings from the current snapshot.
10128 * - SaveS_Force: settings will be saved without doing a deep compare of the
10129 * settings structures. This is used when this is called because snapshots
10130 * have changed to avoid the overhead of the deep compare.
10131 *
10132 * @note Must be called from under this object's write lock. Locks children for
10133 * writing.
10134 *
10135 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
10136 * initialized to false and that will be set to true by this function if
10137 * the caller must invoke VirtualBox::i_saveSettings() because the global
10138 * settings have changed. This will happen if a machine rename has been
10139 * saved and the global machine and media registries will therefore need
10140 * updating.
10141 * @param aFlags Flags.
10142 */
10143HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
10144 int aFlags /*= 0*/)
10145{
10146 LogFlowThisFuncEnter();
10147
10148 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10149
10150 /* make sure child objects are unable to modify the settings while we are
10151 * saving them */
10152 i_ensureNoStateDependencies();
10153
10154 AssertReturn(!i_isSnapshotMachine(),
10155 E_FAIL);
10156
10157 HRESULT rc = S_OK;
10158 bool fNeedsWrite = false;
10159
10160 /* First, prepare to save settings. It will care about renaming the
10161 * settings directory and file if the machine name was changed and about
10162 * creating a new settings file if this is a new machine. */
10163 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
10164 if (FAILED(rc)) return rc;
10165
10166 // keep a pointer to the current settings structures
10167 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
10168 settings::MachineConfigFile *pNewConfig = NULL;
10169
10170 try
10171 {
10172 // make a fresh one to have everyone write stuff into
10173 pNewConfig = new settings::MachineConfigFile(NULL);
10174 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
10175
10176 // now go and copy all the settings data from COM to the settings structures
10177 // (this calls i_saveSettings() on all the COM objects in the machine)
10178 i_copyMachineDataToSettings(*pNewConfig);
10179
10180 if (aFlags & SaveS_ResetCurStateModified)
10181 {
10182 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
10183 mData->mCurrentStateModified = FALSE;
10184 fNeedsWrite = true; // always, no need to compare
10185 }
10186 else if (aFlags & SaveS_Force)
10187 {
10188 fNeedsWrite = true; // always, no need to compare
10189 }
10190 else
10191 {
10192 if (!mData->mCurrentStateModified)
10193 {
10194 // do a deep compare of the settings that we just saved with the settings
10195 // previously stored in the config file; this invokes MachineConfigFile::operator==
10196 // which does a deep compare of all the settings, which is expensive but less expensive
10197 // than writing out XML in vain
10198 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
10199
10200 // could still be modified if any settings changed
10201 mData->mCurrentStateModified = fAnySettingsChanged;
10202
10203 fNeedsWrite = fAnySettingsChanged;
10204 }
10205 else
10206 fNeedsWrite = true;
10207 }
10208
10209 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
10210
10211 if (fNeedsWrite)
10212 // now spit it all out!
10213 pNewConfig->write(mData->m_strConfigFileFull);
10214
10215 mData->pMachineConfigFile = pNewConfig;
10216 delete pOldConfig;
10217 i_commit();
10218
10219 // after saving settings, we are no longer different from the XML on disk
10220 mData->flModifications = 0;
10221 }
10222 catch (HRESULT err)
10223 {
10224 // we assume that error info is set by the thrower
10225 rc = err;
10226
10227 // restore old config
10228 delete pNewConfig;
10229 mData->pMachineConfigFile = pOldConfig;
10230 }
10231 catch (...)
10232 {
10233 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10234 }
10235
10236 if (fNeedsWrite)
10237 {
10238 /* Fire the data change event, even on failure (since we've already
10239 * committed all data). This is done only for SessionMachines because
10240 * mutable Machine instances are always not registered (i.e. private
10241 * to the client process that creates them) and thus don't need to
10242 * inform callbacks. */
10243 if (i_isSessionMachine())
10244 mParent->i_onMachineDataChange(mData->mUuid);
10245 }
10246
10247 LogFlowThisFunc(("rc=%08X\n", rc));
10248 LogFlowThisFuncLeave();
10249 return rc;
10250}
10251
10252/**
10253 * Implementation for saving the machine settings into the given
10254 * settings::MachineConfigFile instance. This copies machine extradata
10255 * from the previous machine config file in the instance data, if any.
10256 *
10257 * This gets called from two locations:
10258 *
10259 * -- Machine::i_saveSettings(), during the regular XML writing;
10260 *
10261 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10262 * exported to OVF and we write the VirtualBox proprietary XML
10263 * into a <vbox:Machine> tag.
10264 *
10265 * This routine fills all the fields in there, including snapshots, *except*
10266 * for the following:
10267 *
10268 * -- fCurrentStateModified. There is some special logic associated with that.
10269 *
10270 * The caller can then call MachineConfigFile::write() or do something else
10271 * with it.
10272 *
10273 * Caller must hold the machine lock!
10274 *
10275 * This throws XML errors and HRESULT, so the caller must have a catch block!
10276 */
10277void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10278{
10279 // deep copy extradata, being extra careful with self assignment (the STL
10280 // map assignment on Mac OS X clang based Xcode isn't checking)
10281 if (&config != mData->pMachineConfigFile)
10282 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10283
10284 config.uuid = mData->mUuid;
10285
10286 // copy name, description, OS type, teleport, UTC etc.
10287 config.machineUserData = mUserData->s;
10288
10289 if ( mData->mMachineState == MachineState_Saved
10290 || mData->mMachineState == MachineState_Restoring
10291 // when doing certain snapshot operations we may or may not have
10292 // a saved state in the current state, so keep everything as is
10293 || ( ( mData->mMachineState == MachineState_Snapshotting
10294 || mData->mMachineState == MachineState_DeletingSnapshot
10295 || mData->mMachineState == MachineState_RestoringSnapshot)
10296 && (!mSSData->strStateFilePath.isEmpty())
10297 )
10298 )
10299 {
10300 Assert(!mSSData->strStateFilePath.isEmpty());
10301 /* try to make the file name relative to the settings file dir */
10302 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10303 }
10304 else
10305 {
10306 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10307 config.strStateFile.setNull();
10308 }
10309
10310 if (mData->mCurrentSnapshot)
10311 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10312 else
10313 config.uuidCurrentSnapshot.clear();
10314
10315 config.timeLastStateChange = mData->mLastStateChange;
10316 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10317 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10318
10319 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10320 if (FAILED(rc)) throw rc;
10321
10322 // save machine's media registry if this is VirtualBox 4.0 or later
10323 if (config.canHaveOwnMediaRegistry())
10324 {
10325 // determine machine folder
10326 Utf8Str strMachineFolder = i_getSettingsFileFull();
10327 strMachineFolder.stripFilename();
10328 mParent->i_saveMediaRegistry(config.mediaRegistry,
10329 i_getId(), // only media with registry ID == machine UUID
10330 strMachineFolder);
10331 // this throws HRESULT
10332 }
10333
10334 // save snapshots
10335 rc = i_saveAllSnapshots(config);
10336 if (FAILED(rc)) throw rc;
10337}
10338
10339/**
10340 * Saves all snapshots of the machine into the given machine config file. Called
10341 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10342 * @param config
10343 * @return
10344 */
10345HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10346{
10347 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10348
10349 HRESULT rc = S_OK;
10350
10351 try
10352 {
10353 config.llFirstSnapshot.clear();
10354
10355 if (mData->mFirstSnapshot)
10356 {
10357 // the settings use a list for "the first snapshot"
10358 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10359
10360 // get reference to the snapshot on the list and work on that
10361 // element straight in the list to avoid excessive copying later
10362 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10363 if (FAILED(rc)) throw rc;
10364 }
10365
10366// if (mType == IsSessionMachine)
10367// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10368
10369 }
10370 catch (HRESULT err)
10371 {
10372 /* we assume that error info is set by the thrower */
10373 rc = err;
10374 }
10375 catch (...)
10376 {
10377 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10378 }
10379
10380 return rc;
10381}
10382
10383/**
10384 * Saves the VM hardware configuration. It is assumed that the
10385 * given node is empty.
10386 *
10387 * @param data Reference to the settings object for the hardware config.
10388 * @param pDbg Pointer to the settings object for the debugging config
10389 * which happens to live in mHWData.
10390 * @param pAutostart Pointer to the settings object for the autostart config
10391 * which happens to live in mHWData.
10392 */
10393HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10394 settings::Autostart *pAutostart)
10395{
10396 HRESULT rc = S_OK;
10397
10398 try
10399 {
10400 /* The hardware version attribute (optional).
10401 Automatically upgrade from 1 to current default hardware version
10402 when there is no saved state. (ugly!) */
10403 if ( mHWData->mHWVersion == "1"
10404 && mSSData->strStateFilePath.isEmpty()
10405 )
10406 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10407
10408 data.strVersion = mHWData->mHWVersion;
10409 data.uuid = mHWData->mHardwareUUID;
10410
10411 // CPU
10412 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10413 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10414 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10415 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10416 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10417 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10418 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10419 data.fPAE = !!mHWData->mPAEEnabled;
10420 data.enmLongMode = mHWData->mLongMode;
10421 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10422 data.fAPIC = !!mHWData->mAPIC;
10423 data.fX2APIC = !!mHWData->mX2APIC;
10424 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10425 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10426 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10427 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10428 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10429 data.cCPUs = mHWData->mCPUCount;
10430 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10431 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10432 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10433 data.strCpuProfile = mHWData->mCpuProfile;
10434
10435 data.llCpus.clear();
10436 if (data.fCpuHotPlug)
10437 {
10438 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10439 {
10440 if (mHWData->mCPUAttached[idx])
10441 {
10442 settings::Cpu cpu;
10443 cpu.ulId = idx;
10444 data.llCpus.push_back(cpu);
10445 }
10446 }
10447 }
10448
10449 /* Standard and Extended CPUID leafs. */
10450 data.llCpuIdLeafs.clear();
10451 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10452
10453 // memory
10454 data.ulMemorySizeMB = mHWData->mMemorySize;
10455 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10456
10457 // firmware
10458 data.firmwareType = mHWData->mFirmwareType;
10459
10460 // HID
10461 data.pointingHIDType = mHWData->mPointingHIDType;
10462 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10463
10464 // chipset
10465 data.chipsetType = mHWData->mChipsetType;
10466
10467 // paravirt
10468 data.paravirtProvider = mHWData->mParavirtProvider;
10469 data.strParavirtDebug = mHWData->mParavirtDebug;
10470
10471 // emulated USB card reader
10472 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10473
10474 // HPET
10475 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10476
10477 // boot order
10478 data.mapBootOrder.clear();
10479 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10480 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10481
10482 // display
10483 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10484 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10485 data.cMonitors = mHWData->mMonitorCount;
10486 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10487 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10488 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10489 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10490 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10491 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10492 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10493 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10494 {
10495 if (mHWData->maVideoCaptureScreens[i])
10496 ASMBitSet(&data.u64VideoCaptureScreens, i);
10497 else
10498 ASMBitClear(&data.u64VideoCaptureScreens, i);
10499 }
10500 /* store relative video capture file if possible */
10501 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10502 data.strVideoCaptureOptions = mHWData->mVideoCaptureOptions;
10503
10504 /* VRDEServer settings (optional) */
10505 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10506 if (FAILED(rc)) throw rc;
10507
10508 /* BIOS (required) */
10509 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10510 if (FAILED(rc)) throw rc;
10511
10512 /* USB Controller (required) */
10513 data.usbSettings.llUSBControllers.clear();
10514 for (USBControllerList::const_iterator
10515 it = mUSBControllers->begin();
10516 it != mUSBControllers->end();
10517 ++it)
10518 {
10519 ComObjPtr<USBController> ctrl = *it;
10520 settings::USBController settingsCtrl;
10521
10522 settingsCtrl.strName = ctrl->i_getName();
10523 settingsCtrl.enmType = ctrl->i_getControllerType();
10524
10525 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10526 }
10527
10528 /* USB device filters (required) */
10529 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10530 if (FAILED(rc)) throw rc;
10531
10532 /* Network adapters (required) */
10533 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10534 data.llNetworkAdapters.clear();
10535 /* Write out only the nominal number of network adapters for this
10536 * chipset type. Since Machine::commit() hasn't been called there
10537 * may be extra NIC settings in the vector. */
10538 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10539 {
10540 settings::NetworkAdapter nic;
10541 nic.ulSlot = (uint32_t)slot;
10542 /* paranoia check... must not be NULL, but must not crash either. */
10543 if (mNetworkAdapters[slot])
10544 {
10545 if (mNetworkAdapters[slot]->i_hasDefaults())
10546 continue;
10547
10548 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10549 if (FAILED(rc)) throw rc;
10550
10551 data.llNetworkAdapters.push_back(nic);
10552 }
10553 }
10554
10555 /* Serial ports */
10556 data.llSerialPorts.clear();
10557 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10558 {
10559 if (mSerialPorts[slot]->i_hasDefaults())
10560 continue;
10561
10562 settings::SerialPort s;
10563 s.ulSlot = slot;
10564 rc = mSerialPorts[slot]->i_saveSettings(s);
10565 if (FAILED(rc)) return rc;
10566
10567 data.llSerialPorts.push_back(s);
10568 }
10569
10570 /* Parallel ports */
10571 data.llParallelPorts.clear();
10572 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10573 {
10574 if (mParallelPorts[slot]->i_hasDefaults())
10575 continue;
10576
10577 settings::ParallelPort p;
10578 p.ulSlot = slot;
10579 rc = mParallelPorts[slot]->i_saveSettings(p);
10580 if (FAILED(rc)) return rc;
10581
10582 data.llParallelPorts.push_back(p);
10583 }
10584
10585 /* Audio adapter */
10586 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10587 if (FAILED(rc)) return rc;
10588
10589 rc = i_saveStorageControllers(data.storage);
10590 if (FAILED(rc)) return rc;
10591
10592 /* Shared folders */
10593 data.llSharedFolders.clear();
10594 for (HWData::SharedFolderList::const_iterator
10595 it = mHWData->mSharedFolders.begin();
10596 it != mHWData->mSharedFolders.end();
10597 ++it)
10598 {
10599 SharedFolder *pSF = *it;
10600 AutoCaller sfCaller(pSF);
10601 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10602 settings::SharedFolder sf;
10603 sf.strName = pSF->i_getName();
10604 sf.strHostPath = pSF->i_getHostPath();
10605 sf.fWritable = !!pSF->i_isWritable();
10606 sf.fAutoMount = !!pSF->i_isAutoMounted();
10607
10608 data.llSharedFolders.push_back(sf);
10609 }
10610
10611 // clipboard
10612 data.clipboardMode = mHWData->mClipboardMode;
10613
10614 // drag'n'drop
10615 data.dndMode = mHWData->mDnDMode;
10616
10617 /* Guest */
10618 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10619
10620 // IO settings
10621 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10622 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10623
10624 /* BandwidthControl (required) */
10625 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10626 if (FAILED(rc)) throw rc;
10627
10628 /* Host PCI devices */
10629 data.pciAttachments.clear();
10630 for (HWData::PCIDeviceAssignmentList::const_iterator
10631 it = mHWData->mPCIDeviceAssignments.begin();
10632 it != mHWData->mPCIDeviceAssignments.end();
10633 ++it)
10634 {
10635 ComObjPtr<PCIDeviceAttachment> pda = *it;
10636 settings::HostPCIDeviceAttachment hpda;
10637
10638 rc = pda->i_saveSettings(hpda);
10639 if (FAILED(rc)) throw rc;
10640
10641 data.pciAttachments.push_back(hpda);
10642 }
10643
10644 // guest properties
10645 data.llGuestProperties.clear();
10646#ifdef VBOX_WITH_GUEST_PROPS
10647 for (HWData::GuestPropertyMap::const_iterator
10648 it = mHWData->mGuestProperties.begin();
10649 it != mHWData->mGuestProperties.end();
10650 ++it)
10651 {
10652 HWData::GuestProperty property = it->second;
10653
10654 /* Remove transient guest properties at shutdown unless we
10655 * are saving state. Note that restoring snapshot intentionally
10656 * keeps them, they will be removed if appropriate once the final
10657 * machine state is set (as crashes etc. need to work). */
10658 if ( ( mData->mMachineState == MachineState_PoweredOff
10659 || mData->mMachineState == MachineState_Aborted
10660 || mData->mMachineState == MachineState_Teleported)
10661 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10662 continue;
10663 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10664 prop.strName = it->first;
10665 prop.strValue = property.strValue;
10666 prop.timestamp = property.mTimestamp;
10667 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10668 GuestPropWriteFlags(property.mFlags, szFlags);
10669 prop.strFlags = szFlags;
10670
10671 data.llGuestProperties.push_back(prop);
10672 }
10673
10674 /* I presume this doesn't require a backup(). */
10675 mData->mGuestPropertiesModified = FALSE;
10676#endif /* VBOX_WITH_GUEST_PROPS defined */
10677
10678 *pDbg = mHWData->mDebugging;
10679 *pAutostart = mHWData->mAutostart;
10680
10681 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10682 }
10683 catch (std::bad_alloc &)
10684 {
10685 return E_OUTOFMEMORY;
10686 }
10687
10688 AssertComRC(rc);
10689 return rc;
10690}
10691
10692/**
10693 * Saves the storage controller configuration.
10694 *
10695 * @param data storage settings.
10696 */
10697HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10698{
10699 data.llStorageControllers.clear();
10700
10701 for (StorageControllerList::const_iterator
10702 it = mStorageControllers->begin();
10703 it != mStorageControllers->end();
10704 ++it)
10705 {
10706 HRESULT rc;
10707 ComObjPtr<StorageController> pCtl = *it;
10708
10709 settings::StorageController ctl;
10710 ctl.strName = pCtl->i_getName();
10711 ctl.controllerType = pCtl->i_getControllerType();
10712 ctl.storageBus = pCtl->i_getStorageBus();
10713 ctl.ulInstance = pCtl->i_getInstance();
10714 ctl.fBootable = pCtl->i_getBootable();
10715
10716 /* Save the port count. */
10717 ULONG portCount;
10718 rc = pCtl->COMGETTER(PortCount)(&portCount);
10719 ComAssertComRCRet(rc, rc);
10720 ctl.ulPortCount = portCount;
10721
10722 /* Save fUseHostIOCache */
10723 BOOL fUseHostIOCache;
10724 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10725 ComAssertComRCRet(rc, rc);
10726 ctl.fUseHostIOCache = !!fUseHostIOCache;
10727
10728 /* save the devices now. */
10729 rc = i_saveStorageDevices(pCtl, ctl);
10730 ComAssertComRCRet(rc, rc);
10731
10732 data.llStorageControllers.push_back(ctl);
10733 }
10734
10735 return S_OK;
10736}
10737
10738/**
10739 * Saves the hard disk configuration.
10740 */
10741HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10742 settings::StorageController &data)
10743{
10744 MediumAttachmentList atts;
10745
10746 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10747 if (FAILED(rc)) return rc;
10748
10749 data.llAttachedDevices.clear();
10750 for (MediumAttachmentList::const_iterator
10751 it = atts.begin();
10752 it != atts.end();
10753 ++it)
10754 {
10755 settings::AttachedDevice dev;
10756 IMediumAttachment *iA = *it;
10757 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10758 Medium *pMedium = pAttach->i_getMedium();
10759
10760 dev.deviceType = pAttach->i_getType();
10761 dev.lPort = pAttach->i_getPort();
10762 dev.lDevice = pAttach->i_getDevice();
10763 dev.fPassThrough = pAttach->i_getPassthrough();
10764 dev.fHotPluggable = pAttach->i_getHotPluggable();
10765 if (pMedium)
10766 {
10767 if (pMedium->i_isHostDrive())
10768 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10769 else
10770 dev.uuid = pMedium->i_getId();
10771 dev.fTempEject = pAttach->i_getTempEject();
10772 dev.fNonRotational = pAttach->i_getNonRotational();
10773 dev.fDiscard = pAttach->i_getDiscard();
10774 }
10775
10776 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10777
10778 data.llAttachedDevices.push_back(dev);
10779 }
10780
10781 return S_OK;
10782}
10783
10784/**
10785 * Saves machine state settings as defined by aFlags
10786 * (SaveSTS_* values).
10787 *
10788 * @param aFlags Combination of SaveSTS_* flags.
10789 *
10790 * @note Locks objects for writing.
10791 */
10792HRESULT Machine::i_saveStateSettings(int aFlags)
10793{
10794 if (aFlags == 0)
10795 return S_OK;
10796
10797 AutoCaller autoCaller(this);
10798 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10799
10800 /* This object's write lock is also necessary to serialize file access
10801 * (prevent concurrent reads and writes) */
10802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10803
10804 HRESULT rc = S_OK;
10805
10806 Assert(mData->pMachineConfigFile);
10807
10808 try
10809 {
10810 if (aFlags & SaveSTS_CurStateModified)
10811 mData->pMachineConfigFile->fCurrentStateModified = true;
10812
10813 if (aFlags & SaveSTS_StateFilePath)
10814 {
10815 if (!mSSData->strStateFilePath.isEmpty())
10816 /* try to make the file name relative to the settings file dir */
10817 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10818 else
10819 mData->pMachineConfigFile->strStateFile.setNull();
10820 }
10821
10822 if (aFlags & SaveSTS_StateTimeStamp)
10823 {
10824 Assert( mData->mMachineState != MachineState_Aborted
10825 || mSSData->strStateFilePath.isEmpty());
10826
10827 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10828
10829 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10830/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10831 }
10832
10833 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10834 }
10835 catch (...)
10836 {
10837 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10838 }
10839
10840 return rc;
10841}
10842
10843/**
10844 * Ensures that the given medium is added to a media registry. If this machine
10845 * was created with 4.0 or later, then the machine registry is used. Otherwise
10846 * the global VirtualBox media registry is used.
10847 *
10848 * Caller must NOT hold machine lock, media tree or any medium locks!
10849 *
10850 * @param pMedium
10851 */
10852void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10853{
10854 /* Paranoia checks: do not hold machine or media tree locks. */
10855 AssertReturnVoid(!isWriteLockOnCurrentThread());
10856 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10857
10858 ComObjPtr<Medium> pBase;
10859 {
10860 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10861 pBase = pMedium->i_getBase();
10862 }
10863
10864 /* Paranoia checks: do not hold medium locks. */
10865 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10866 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10867
10868 // decide which medium registry to use now that the medium is attached:
10869 Guid uuid;
10870 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10871 if (fCanHaveOwnMediaRegistry)
10872 // machine XML is VirtualBox 4.0 or higher:
10873 uuid = i_getId(); // machine UUID
10874 else
10875 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10876
10877 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10878 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10879 if (pMedium->i_addRegistry(uuid))
10880 mParent->i_markRegistryModified(uuid);
10881
10882 /* For more complex hard disk structures it can happen that the base
10883 * medium isn't yet associated with any medium registry. Do that now. */
10884 if (pMedium != pBase)
10885 {
10886 /* Tree lock needed by Medium::addRegistry when recursing. */
10887 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10888 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10889 {
10890 treeLock.release();
10891 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10892 treeLock.acquire();
10893 }
10894 if (pBase->i_addRegistryRecursive(uuid))
10895 {
10896 treeLock.release();
10897 mParent->i_markRegistryModified(uuid);
10898 }
10899 }
10900}
10901
10902/**
10903 * Creates differencing hard disks for all normal hard disks attached to this
10904 * machine and a new set of attachments to refer to created disks.
10905 *
10906 * Used when taking a snapshot or when deleting the current state. Gets called
10907 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10908 *
10909 * This method assumes that mMediumAttachments contains the original hard disk
10910 * attachments it needs to create diffs for. On success, these attachments will
10911 * be replaced with the created diffs.
10912 *
10913 * Attachments with non-normal hard disks are left as is.
10914 *
10915 * If @a aOnline is @c false then the original hard disks that require implicit
10916 * diffs will be locked for reading. Otherwise it is assumed that they are
10917 * already locked for writing (when the VM was started). Note that in the latter
10918 * case it is responsibility of the caller to lock the newly created diffs for
10919 * writing if this method succeeds.
10920 *
10921 * @param aProgress Progress object to run (must contain at least as
10922 * many operations left as the number of hard disks
10923 * attached).
10924 * @param aWeight Weight of this operation.
10925 * @param aOnline Whether the VM was online prior to this operation.
10926 *
10927 * @note The progress object is not marked as completed, neither on success nor
10928 * on failure. This is a responsibility of the caller.
10929 *
10930 * @note Locks this object and the media tree for writing.
10931 */
10932HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10933 ULONG aWeight,
10934 bool aOnline)
10935{
10936 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10937
10938 AutoCaller autoCaller(this);
10939 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10940
10941 AutoMultiWriteLock2 alock(this->lockHandle(),
10942 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10943
10944 /* must be in a protective state because we release the lock below */
10945 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10946 || mData->mMachineState == MachineState_OnlineSnapshotting
10947 || mData->mMachineState == MachineState_LiveSnapshotting
10948 || mData->mMachineState == MachineState_RestoringSnapshot
10949 || mData->mMachineState == MachineState_DeletingSnapshot
10950 , E_FAIL);
10951
10952 HRESULT rc = S_OK;
10953
10954 // use appropriate locked media map (online or offline)
10955 MediumLockListMap lockedMediaOffline;
10956 MediumLockListMap *lockedMediaMap;
10957 if (aOnline)
10958 lockedMediaMap = &mData->mSession.mLockedMedia;
10959 else
10960 lockedMediaMap = &lockedMediaOffline;
10961
10962 try
10963 {
10964 if (!aOnline)
10965 {
10966 /* lock all attached hard disks early to detect "in use"
10967 * situations before creating actual diffs */
10968 for (MediumAttachmentList::const_iterator
10969 it = mMediumAttachments->begin();
10970 it != mMediumAttachments->end();
10971 ++it)
10972 {
10973 MediumAttachment *pAtt = *it;
10974 if (pAtt->i_getType() == DeviceType_HardDisk)
10975 {
10976 Medium *pMedium = pAtt->i_getMedium();
10977 Assert(pMedium);
10978
10979 MediumLockList *pMediumLockList(new MediumLockList());
10980 alock.release();
10981 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10982 NULL /* pToLockWrite */,
10983 false /* fMediumLockWriteAll */,
10984 NULL,
10985 *pMediumLockList);
10986 alock.acquire();
10987 if (FAILED(rc))
10988 {
10989 delete pMediumLockList;
10990 throw rc;
10991 }
10992 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10993 if (FAILED(rc))
10994 {
10995 throw setError(rc,
10996 tr("Collecting locking information for all attached media failed"));
10997 }
10998 }
10999 }
11000
11001 /* Now lock all media. If this fails, nothing is locked. */
11002 alock.release();
11003 rc = lockedMediaMap->Lock();
11004 alock.acquire();
11005 if (FAILED(rc))
11006 {
11007 throw setError(rc,
11008 tr("Locking of attached media failed"));
11009 }
11010 }
11011
11012 /* remember the current list (note that we don't use backup() since
11013 * mMediumAttachments may be already backed up) */
11014 MediumAttachmentList atts = *mMediumAttachments.data();
11015
11016 /* start from scratch */
11017 mMediumAttachments->clear();
11018
11019 /* go through remembered attachments and create diffs for normal hard
11020 * disks and attach them */
11021 for (MediumAttachmentList::const_iterator
11022 it = atts.begin();
11023 it != atts.end();
11024 ++it)
11025 {
11026 MediumAttachment *pAtt = *it;
11027
11028 DeviceType_T devType = pAtt->i_getType();
11029 Medium *pMedium = pAtt->i_getMedium();
11030
11031 if ( devType != DeviceType_HardDisk
11032 || pMedium == NULL
11033 || pMedium->i_getType() != MediumType_Normal)
11034 {
11035 /* copy the attachment as is */
11036
11037 /** @todo the progress object created in SessionMachine::TakeSnaphot
11038 * only expects operations for hard disks. Later other
11039 * device types need to show up in the progress as well. */
11040 if (devType == DeviceType_HardDisk)
11041 {
11042 if (pMedium == NULL)
11043 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
11044 aWeight); // weight
11045 else
11046 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
11047 pMedium->i_getBase()->i_getName().c_str()).raw(),
11048 aWeight); // weight
11049 }
11050
11051 mMediumAttachments->push_back(pAtt);
11052 continue;
11053 }
11054
11055 /* need a diff */
11056 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
11057 pMedium->i_getBase()->i_getName().c_str()).raw(),
11058 aWeight); // weight
11059
11060 Utf8Str strFullSnapshotFolder;
11061 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
11062
11063 ComObjPtr<Medium> diff;
11064 diff.createObject();
11065 // store the diff in the same registry as the parent
11066 // (this cannot fail here because we can't create implicit diffs for
11067 // unregistered images)
11068 Guid uuidRegistryParent;
11069 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
11070 Assert(fInRegistry); NOREF(fInRegistry);
11071 rc = diff->init(mParent,
11072 pMedium->i_getPreferredDiffFormat(),
11073 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
11074 uuidRegistryParent,
11075 DeviceType_HardDisk);
11076 if (FAILED(rc)) throw rc;
11077
11078 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
11079 * the push_back? Looks like we're going to release medium with the
11080 * wrong kind of lock (general issue with if we fail anywhere at all)
11081 * and an orphaned VDI in the snapshots folder. */
11082
11083 /* update the appropriate lock list */
11084 MediumLockList *pMediumLockList;
11085 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
11086 AssertComRCThrowRC(rc);
11087 if (aOnline)
11088 {
11089 alock.release();
11090 /* The currently attached medium will be read-only, change
11091 * the lock type to read. */
11092 rc = pMediumLockList->Update(pMedium, false);
11093 alock.acquire();
11094 AssertComRCThrowRC(rc);
11095 }
11096
11097 /* release the locks before the potentially lengthy operation */
11098 alock.release();
11099 rc = pMedium->i_createDiffStorage(diff,
11100 pMedium->i_getPreferredDiffVariant(),
11101 pMediumLockList,
11102 NULL /* aProgress */,
11103 true /* aWait */);
11104 alock.acquire();
11105 if (FAILED(rc)) throw rc;
11106
11107 /* actual lock list update is done in Machine::i_commitMedia */
11108
11109 rc = diff->i_addBackReference(mData->mUuid);
11110 AssertComRCThrowRC(rc);
11111
11112 /* add a new attachment */
11113 ComObjPtr<MediumAttachment> attachment;
11114 attachment.createObject();
11115 rc = attachment->init(this,
11116 diff,
11117 pAtt->i_getControllerName(),
11118 pAtt->i_getPort(),
11119 pAtt->i_getDevice(),
11120 DeviceType_HardDisk,
11121 true /* aImplicit */,
11122 false /* aPassthrough */,
11123 false /* aTempEject */,
11124 pAtt->i_getNonRotational(),
11125 pAtt->i_getDiscard(),
11126 pAtt->i_getHotPluggable(),
11127 pAtt->i_getBandwidthGroup());
11128 if (FAILED(rc)) throw rc;
11129
11130 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
11131 AssertComRCThrowRC(rc);
11132 mMediumAttachments->push_back(attachment);
11133 }
11134 }
11135 catch (HRESULT aRC) { rc = aRC; }
11136
11137 /* unlock all hard disks we locked when there is no VM */
11138 if (!aOnline)
11139 {
11140 ErrorInfoKeeper eik;
11141
11142 HRESULT rc1 = lockedMediaMap->Clear();
11143 AssertComRC(rc1);
11144 }
11145
11146 return rc;
11147}
11148
11149/**
11150 * Deletes implicit differencing hard disks created either by
11151 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
11152 * mMediumAttachments.
11153 *
11154 * Note that to delete hard disks created by #attachDevice() this method is
11155 * called from #i_rollbackMedia() when the changes are rolled back.
11156 *
11157 * @note Locks this object and the media tree for writing.
11158 */
11159HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
11160{
11161 LogFlowThisFunc(("aOnline=%d\n", aOnline));
11162
11163 AutoCaller autoCaller(this);
11164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
11165
11166 AutoMultiWriteLock2 alock(this->lockHandle(),
11167 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
11168
11169 /* We absolutely must have backed up state. */
11170 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
11171
11172 /* Check if there are any implicitly created diff images. */
11173 bool fImplicitDiffs = false;
11174 for (MediumAttachmentList::const_iterator
11175 it = mMediumAttachments->begin();
11176 it != mMediumAttachments->end();
11177 ++it)
11178 {
11179 const ComObjPtr<MediumAttachment> &pAtt = *it;
11180 if (pAtt->i_isImplicit())
11181 {
11182 fImplicitDiffs = true;
11183 break;
11184 }
11185 }
11186 /* If there is nothing to do, leave early. This saves lots of image locking
11187 * effort. It also avoids a MachineStateChanged event without real reason.
11188 * This is important e.g. when loading a VM config, because there should be
11189 * no events. Otherwise API clients can become thoroughly confused for
11190 * inaccessible VMs (the code for loading VM configs uses this method for
11191 * cleanup if the config makes no sense), as they take such events as an
11192 * indication that the VM is alive, and they would force the VM config to
11193 * be reread, leading to an endless loop. */
11194 if (!fImplicitDiffs)
11195 return S_OK;
11196
11197 HRESULT rc = S_OK;
11198 MachineState_T oldState = mData->mMachineState;
11199
11200 /* will release the lock before the potentially lengthy operation,
11201 * so protect with the special state (unless already protected) */
11202 if ( oldState != MachineState_Snapshotting
11203 && oldState != MachineState_OnlineSnapshotting
11204 && oldState != MachineState_LiveSnapshotting
11205 && oldState != MachineState_RestoringSnapshot
11206 && oldState != MachineState_DeletingSnapshot
11207 && oldState != MachineState_DeletingSnapshotOnline
11208 && oldState != MachineState_DeletingSnapshotPaused
11209 )
11210 i_setMachineState(MachineState_SettingUp);
11211
11212 // use appropriate locked media map (online or offline)
11213 MediumLockListMap lockedMediaOffline;
11214 MediumLockListMap *lockedMediaMap;
11215 if (aOnline)
11216 lockedMediaMap = &mData->mSession.mLockedMedia;
11217 else
11218 lockedMediaMap = &lockedMediaOffline;
11219
11220 try
11221 {
11222 if (!aOnline)
11223 {
11224 /* lock all attached hard disks early to detect "in use"
11225 * situations before deleting actual diffs */
11226 for (MediumAttachmentList::const_iterator
11227 it = mMediumAttachments->begin();
11228 it != mMediumAttachments->end();
11229 ++it)
11230 {
11231 MediumAttachment *pAtt = *it;
11232 if (pAtt->i_getType() == DeviceType_HardDisk)
11233 {
11234 Medium *pMedium = pAtt->i_getMedium();
11235 Assert(pMedium);
11236
11237 MediumLockList *pMediumLockList(new MediumLockList());
11238 alock.release();
11239 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
11240 NULL /* pToLockWrite */,
11241 false /* fMediumLockWriteAll */,
11242 NULL,
11243 *pMediumLockList);
11244 alock.acquire();
11245
11246 if (FAILED(rc))
11247 {
11248 delete pMediumLockList;
11249 throw rc;
11250 }
11251
11252 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11253 if (FAILED(rc))
11254 throw rc;
11255 }
11256 }
11257
11258 if (FAILED(rc))
11259 throw rc;
11260 } // end of offline
11261
11262 /* Lock lists are now up to date and include implicitly created media */
11263
11264 /* Go through remembered attachments and delete all implicitly created
11265 * diffs and fix up the attachment information */
11266 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11267 MediumAttachmentList implicitAtts;
11268 for (MediumAttachmentList::const_iterator
11269 it = mMediumAttachments->begin();
11270 it != mMediumAttachments->end();
11271 ++it)
11272 {
11273 ComObjPtr<MediumAttachment> pAtt = *it;
11274 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11275 if (pMedium.isNull())
11276 continue;
11277
11278 // Implicit attachments go on the list for deletion and back references are removed.
11279 if (pAtt->i_isImplicit())
11280 {
11281 /* Deassociate and mark for deletion */
11282 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11283 rc = pMedium->i_removeBackReference(mData->mUuid);
11284 if (FAILED(rc))
11285 throw rc;
11286 implicitAtts.push_back(pAtt);
11287 continue;
11288 }
11289
11290 /* Was this medium attached before? */
11291 if (!i_findAttachment(oldAtts, pMedium))
11292 {
11293 /* no: de-associate */
11294 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11295 rc = pMedium->i_removeBackReference(mData->mUuid);
11296 if (FAILED(rc))
11297 throw rc;
11298 continue;
11299 }
11300 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11301 }
11302
11303 /* If there are implicit attachments to delete, throw away the lock
11304 * map contents (which will unlock all media) since the medium
11305 * attachments will be rolled back. Below we need to completely
11306 * recreate the lock map anyway since it is infinitely complex to
11307 * do this incrementally (would need reconstructing each attachment
11308 * change, which would be extremely hairy). */
11309 if (implicitAtts.size() != 0)
11310 {
11311 ErrorInfoKeeper eik;
11312
11313 HRESULT rc1 = lockedMediaMap->Clear();
11314 AssertComRC(rc1);
11315 }
11316
11317 /* rollback hard disk changes */
11318 mMediumAttachments.rollback();
11319
11320 MultiResult mrc(S_OK);
11321
11322 // Delete unused implicit diffs.
11323 if (implicitAtts.size() != 0)
11324 {
11325 alock.release();
11326
11327 for (MediumAttachmentList::const_iterator
11328 it = implicitAtts.begin();
11329 it != implicitAtts.end();
11330 ++it)
11331 {
11332 // Remove medium associated with this attachment.
11333 ComObjPtr<MediumAttachment> pAtt = *it;
11334 Assert(pAtt);
11335 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11336 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11337 Assert(pMedium);
11338
11339 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11340 // continue on delete failure, just collect error messages
11341 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11342 pMedium->i_getLocationFull().c_str() ));
11343 mrc = rc;
11344 }
11345 // Clear the list of deleted implicit attachments now, while not
11346 // holding the lock, as it will ultimately trigger Medium::uninit()
11347 // calls which assume that the media tree lock isn't held.
11348 implicitAtts.clear();
11349
11350 alock.acquire();
11351
11352 /* if there is a VM recreate media lock map as mentioned above,
11353 * otherwise it is a waste of time and we leave things unlocked */
11354 if (aOnline)
11355 {
11356 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11357 /* must never be NULL, but better safe than sorry */
11358 if (!pMachine.isNull())
11359 {
11360 alock.release();
11361 rc = mData->mSession.mMachine->i_lockMedia();
11362 alock.acquire();
11363 if (FAILED(rc))
11364 throw rc;
11365 }
11366 }
11367 }
11368 }
11369 catch (HRESULT aRC) {rc = aRC;}
11370
11371 if (mData->mMachineState == MachineState_SettingUp)
11372 i_setMachineState(oldState);
11373
11374 /* unlock all hard disks we locked when there is no VM */
11375 if (!aOnline)
11376 {
11377 ErrorInfoKeeper eik;
11378
11379 HRESULT rc1 = lockedMediaMap->Clear();
11380 AssertComRC(rc1);
11381 }
11382
11383 return rc;
11384}
11385
11386
11387/**
11388 * Looks through the given list of media attachments for one with the given parameters
11389 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11390 * can be searched as well if needed.
11391 *
11392 * @param ll
11393 * @param aControllerName
11394 * @param aControllerPort
11395 * @param aDevice
11396 * @return
11397 */
11398MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11399 const Utf8Str &aControllerName,
11400 LONG aControllerPort,
11401 LONG aDevice)
11402{
11403 for (MediumAttachmentList::const_iterator
11404 it = ll.begin();
11405 it != ll.end();
11406 ++it)
11407 {
11408 MediumAttachment *pAttach = *it;
11409 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11410 return pAttach;
11411 }
11412
11413 return NULL;
11414}
11415
11416/**
11417 * Looks through the given list of media attachments for one with the given parameters
11418 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11419 * can be searched as well if needed.
11420 *
11421 * @param ll
11422 * @param pMedium
11423 * @return
11424 */
11425MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11426 ComObjPtr<Medium> pMedium)
11427{
11428 for (MediumAttachmentList::const_iterator
11429 it = ll.begin();
11430 it != ll.end();
11431 ++it)
11432 {
11433 MediumAttachment *pAttach = *it;
11434 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11435 if (pMediumThis == pMedium)
11436 return pAttach;
11437 }
11438
11439 return NULL;
11440}
11441
11442/**
11443 * Looks through the given list of media attachments for one with the given parameters
11444 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11445 * can be searched as well if needed.
11446 *
11447 * @param ll
11448 * @param id
11449 * @return
11450 */
11451MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11452 Guid &id)
11453{
11454 for (MediumAttachmentList::const_iterator
11455 it = ll.begin();
11456 it != ll.end();
11457 ++it)
11458 {
11459 MediumAttachment *pAttach = *it;
11460 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11461 if (pMediumThis->i_getId() == id)
11462 return pAttach;
11463 }
11464
11465 return NULL;
11466}
11467
11468/**
11469 * Main implementation for Machine::DetachDevice. This also gets called
11470 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11471 *
11472 * @param pAttach Medium attachment to detach.
11473 * @param writeLock Machine write lock which the caller must have locked once.
11474 * This may be released temporarily in here.
11475 * @param pSnapshot If NULL, then the detachment is for the current machine.
11476 * Otherwise this is for a SnapshotMachine, and this must be
11477 * its snapshot.
11478 * @return
11479 */
11480HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11481 AutoWriteLock &writeLock,
11482 Snapshot *pSnapshot)
11483{
11484 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11485 DeviceType_T mediumType = pAttach->i_getType();
11486
11487 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11488
11489 if (pAttach->i_isImplicit())
11490 {
11491 /* attempt to implicitly delete the implicitly created diff */
11492
11493 /// @todo move the implicit flag from MediumAttachment to Medium
11494 /// and forbid any hard disk operation when it is implicit. Or maybe
11495 /// a special media state for it to make it even more simple.
11496
11497 Assert(mMediumAttachments.isBackedUp());
11498
11499 /* will release the lock before the potentially lengthy operation, so
11500 * protect with the special state */
11501 MachineState_T oldState = mData->mMachineState;
11502 i_setMachineState(MachineState_SettingUp);
11503
11504 writeLock.release();
11505
11506 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11507 true /*aWait*/);
11508
11509 writeLock.acquire();
11510
11511 i_setMachineState(oldState);
11512
11513 if (FAILED(rc)) return rc;
11514 }
11515
11516 i_setModified(IsModified_Storage);
11517 mMediumAttachments.backup();
11518 mMediumAttachments->remove(pAttach);
11519
11520 if (!oldmedium.isNull())
11521 {
11522 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11523 if (pSnapshot)
11524 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11525 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11526 else if (mediumType != DeviceType_HardDisk)
11527 oldmedium->i_removeBackReference(mData->mUuid);
11528 }
11529
11530 return S_OK;
11531}
11532
11533/**
11534 * Goes thru all media of the given list and
11535 *
11536 * 1) calls i_detachDevice() on each of them for this machine and
11537 * 2) adds all Medium objects found in the process to the given list,
11538 * depending on cleanupMode.
11539 *
11540 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11541 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11542 * media to the list.
11543 *
11544 * This gets called from Machine::Unregister, both for the actual Machine and
11545 * the SnapshotMachine objects that might be found in the snapshots.
11546 *
11547 * Requires caller and locking. The machine lock must be passed in because it
11548 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11549 *
11550 * @param writeLock Machine lock from top-level caller; this gets passed to
11551 * i_detachDevice.
11552 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11553 * object if called for a SnapshotMachine.
11554 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11555 * added to llMedia; if Full, then all media get added;
11556 * otherwise no media get added.
11557 * @param llMedia Caller's list to receive Medium objects which got detached so
11558 * caller can close() them, depending on cleanupMode.
11559 * @return
11560 */
11561HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11562 Snapshot *pSnapshot,
11563 CleanupMode_T cleanupMode,
11564 MediaList &llMedia)
11565{
11566 Assert(isWriteLockOnCurrentThread());
11567
11568 HRESULT rc;
11569
11570 // make a temporary list because i_detachDevice invalidates iterators into
11571 // mMediumAttachments
11572 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11573
11574 for (MediumAttachmentList::iterator
11575 it = llAttachments2.begin();
11576 it != llAttachments2.end();
11577 ++it)
11578 {
11579 ComObjPtr<MediumAttachment> &pAttach = *it;
11580 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11581
11582 if (!pMedium.isNull())
11583 {
11584 AutoCaller mac(pMedium);
11585 if (FAILED(mac.rc())) return mac.rc();
11586 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11587 DeviceType_T devType = pMedium->i_getDeviceType();
11588 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11589 && devType == DeviceType_HardDisk)
11590 || (cleanupMode == CleanupMode_Full)
11591 )
11592 {
11593 llMedia.push_back(pMedium);
11594 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11595 /* Not allowed to keep this lock as below we need the parent
11596 * medium lock, and the lock order is parent to child. */
11597 lock.release();
11598 /*
11599 * Search for medias which are not attached to any machine, but
11600 * in the chain to an attached disk. Mediums are only consided
11601 * if they are:
11602 * - have only one child
11603 * - no references to any machines
11604 * - are of normal medium type
11605 */
11606 while (!pParent.isNull())
11607 {
11608 AutoCaller mac1(pParent);
11609 if (FAILED(mac1.rc())) return mac1.rc();
11610 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11611 if (pParent->i_getChildren().size() == 1)
11612 {
11613 if ( pParent->i_getMachineBackRefCount() == 0
11614 && pParent->i_getType() == MediumType_Normal
11615 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11616 llMedia.push_back(pParent);
11617 }
11618 else
11619 break;
11620 pParent = pParent->i_getParent();
11621 }
11622 }
11623 }
11624
11625 // real machine: then we need to use the proper method
11626 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11627
11628 if (FAILED(rc))
11629 return rc;
11630 }
11631
11632 return S_OK;
11633}
11634
11635/**
11636 * Perform deferred hard disk detachments.
11637 *
11638 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11639 * changed (not backed up).
11640 *
11641 * If @a aOnline is @c true then this method will also unlock the old hard
11642 * disks for which the new implicit diffs were created and will lock these new
11643 * diffs for writing.
11644 *
11645 * @param aOnline Whether the VM was online prior to this operation.
11646 *
11647 * @note Locks this object for writing!
11648 */
11649void Machine::i_commitMedia(bool aOnline /*= false*/)
11650{
11651 AutoCaller autoCaller(this);
11652 AssertComRCReturnVoid(autoCaller.rc());
11653
11654 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11655
11656 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11657
11658 HRESULT rc = S_OK;
11659
11660 /* no attach/detach operations -- nothing to do */
11661 if (!mMediumAttachments.isBackedUp())
11662 return;
11663
11664 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11665 bool fMediaNeedsLocking = false;
11666
11667 /* enumerate new attachments */
11668 for (MediumAttachmentList::const_iterator
11669 it = mMediumAttachments->begin();
11670 it != mMediumAttachments->end();
11671 ++it)
11672 {
11673 MediumAttachment *pAttach = *it;
11674
11675 pAttach->i_commit();
11676
11677 Medium *pMedium = pAttach->i_getMedium();
11678 bool fImplicit = pAttach->i_isImplicit();
11679
11680 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11681 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11682 fImplicit));
11683
11684 /** @todo convert all this Machine-based voodoo to MediumAttachment
11685 * based commit logic. */
11686 if (fImplicit)
11687 {
11688 /* convert implicit attachment to normal */
11689 pAttach->i_setImplicit(false);
11690
11691 if ( aOnline
11692 && pMedium
11693 && pAttach->i_getType() == DeviceType_HardDisk
11694 )
11695 {
11696 /* update the appropriate lock list */
11697 MediumLockList *pMediumLockList;
11698 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11699 AssertComRC(rc);
11700 if (pMediumLockList)
11701 {
11702 /* unlock if there's a need to change the locking */
11703 if (!fMediaNeedsLocking)
11704 {
11705 rc = mData->mSession.mLockedMedia.Unlock();
11706 AssertComRC(rc);
11707 fMediaNeedsLocking = true;
11708 }
11709 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11710 AssertComRC(rc);
11711 rc = pMediumLockList->Append(pMedium, true);
11712 AssertComRC(rc);
11713 }
11714 }
11715
11716 continue;
11717 }
11718
11719 if (pMedium)
11720 {
11721 /* was this medium attached before? */
11722 for (MediumAttachmentList::iterator
11723 oldIt = oldAtts.begin();
11724 oldIt != oldAtts.end();
11725 ++oldIt)
11726 {
11727 MediumAttachment *pOldAttach = *oldIt;
11728 if (pOldAttach->i_getMedium() == pMedium)
11729 {
11730 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11731
11732 /* yes: remove from old to avoid de-association */
11733 oldAtts.erase(oldIt);
11734 break;
11735 }
11736 }
11737 }
11738 }
11739
11740 /* enumerate remaining old attachments and de-associate from the
11741 * current machine state */
11742 for (MediumAttachmentList::const_iterator
11743 it = oldAtts.begin();
11744 it != oldAtts.end();
11745 ++it)
11746 {
11747 MediumAttachment *pAttach = *it;
11748 Medium *pMedium = pAttach->i_getMedium();
11749
11750 /* Detach only hard disks, since DVD/floppy media is detached
11751 * instantly in MountMedium. */
11752 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11753 {
11754 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11755
11756 /* now de-associate from the current machine state */
11757 rc = pMedium->i_removeBackReference(mData->mUuid);
11758 AssertComRC(rc);
11759
11760 if (aOnline)
11761 {
11762 /* unlock since medium is not used anymore */
11763 MediumLockList *pMediumLockList;
11764 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11765 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11766 {
11767 /* this happens for online snapshots, there the attachment
11768 * is changing, but only to a diff image created under
11769 * the old one, so there is no separate lock list */
11770 Assert(!pMediumLockList);
11771 }
11772 else
11773 {
11774 AssertComRC(rc);
11775 if (pMediumLockList)
11776 {
11777 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11778 AssertComRC(rc);
11779 }
11780 }
11781 }
11782 }
11783 }
11784
11785 /* take media locks again so that the locking state is consistent */
11786 if (fMediaNeedsLocking)
11787 {
11788 Assert(aOnline);
11789 rc = mData->mSession.mLockedMedia.Lock();
11790 AssertComRC(rc);
11791 }
11792
11793 /* commit the hard disk changes */
11794 mMediumAttachments.commit();
11795
11796 if (i_isSessionMachine())
11797 {
11798 /*
11799 * Update the parent machine to point to the new owner.
11800 * This is necessary because the stored parent will point to the
11801 * session machine otherwise and cause crashes or errors later
11802 * when the session machine gets invalid.
11803 */
11804 /** @todo Change the MediumAttachment class to behave like any other
11805 * class in this regard by creating peer MediumAttachment
11806 * objects for session machines and share the data with the peer
11807 * machine.
11808 */
11809 for (MediumAttachmentList::const_iterator
11810 it = mMediumAttachments->begin();
11811 it != mMediumAttachments->end();
11812 ++it)
11813 (*it)->i_updateParentMachine(mPeer);
11814
11815 /* attach new data to the primary machine and reshare it */
11816 mPeer->mMediumAttachments.attach(mMediumAttachments);
11817 }
11818
11819 return;
11820}
11821
11822/**
11823 * Perform deferred deletion of implicitly created diffs.
11824 *
11825 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11826 * changed (not backed up).
11827 *
11828 * @note Locks this object for writing!
11829 */
11830void Machine::i_rollbackMedia()
11831{
11832 AutoCaller autoCaller(this);
11833 AssertComRCReturnVoid(autoCaller.rc());
11834
11835 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11836 LogFlowThisFunc(("Entering rollbackMedia\n"));
11837
11838 HRESULT rc = S_OK;
11839
11840 /* no attach/detach operations -- nothing to do */
11841 if (!mMediumAttachments.isBackedUp())
11842 return;
11843
11844 /* enumerate new attachments */
11845 for (MediumAttachmentList::const_iterator
11846 it = mMediumAttachments->begin();
11847 it != mMediumAttachments->end();
11848 ++it)
11849 {
11850 MediumAttachment *pAttach = *it;
11851 /* Fix up the backrefs for DVD/floppy media. */
11852 if (pAttach->i_getType() != DeviceType_HardDisk)
11853 {
11854 Medium *pMedium = pAttach->i_getMedium();
11855 if (pMedium)
11856 {
11857 rc = pMedium->i_removeBackReference(mData->mUuid);
11858 AssertComRC(rc);
11859 }
11860 }
11861
11862 (*it)->i_rollback();
11863
11864 pAttach = *it;
11865 /* Fix up the backrefs for DVD/floppy media. */
11866 if (pAttach->i_getType() != DeviceType_HardDisk)
11867 {
11868 Medium *pMedium = pAttach->i_getMedium();
11869 if (pMedium)
11870 {
11871 rc = pMedium->i_addBackReference(mData->mUuid);
11872 AssertComRC(rc);
11873 }
11874 }
11875 }
11876
11877 /** @todo convert all this Machine-based voodoo to MediumAttachment
11878 * based rollback logic. */
11879 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11880
11881 return;
11882}
11883
11884/**
11885 * Returns true if the settings file is located in the directory named exactly
11886 * as the machine; this means, among other things, that the machine directory
11887 * should be auto-renamed.
11888 *
11889 * @param aSettingsDir if not NULL, the full machine settings file directory
11890 * name will be assigned there.
11891 *
11892 * @note Doesn't lock anything.
11893 * @note Not thread safe (must be called from this object's lock).
11894 */
11895bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11896{
11897 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11898 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11899 if (aSettingsDir)
11900 *aSettingsDir = strMachineDirName;
11901 strMachineDirName.stripPath(); // vmname
11902 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11903 strConfigFileOnly.stripPath() // vmname.vbox
11904 .stripSuffix(); // vmname
11905 /** @todo hack, make somehow use of ComposeMachineFilename */
11906 if (mUserData->s.fDirectoryIncludesUUID)
11907 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11908
11909 AssertReturn(!strMachineDirName.isEmpty(), false);
11910 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11911
11912 return strMachineDirName == strConfigFileOnly;
11913}
11914
11915/**
11916 * Discards all changes to machine settings.
11917 *
11918 * @param aNotify Whether to notify the direct session about changes or not.
11919 *
11920 * @note Locks objects for writing!
11921 */
11922void Machine::i_rollback(bool aNotify)
11923{
11924 AutoCaller autoCaller(this);
11925 AssertComRCReturn(autoCaller.rc(), (void)0);
11926
11927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11928
11929 if (!mStorageControllers.isNull())
11930 {
11931 if (mStorageControllers.isBackedUp())
11932 {
11933 /* unitialize all new devices (absent in the backed up list). */
11934 StorageControllerList *backedList = mStorageControllers.backedUpData();
11935 for (StorageControllerList::const_iterator
11936 it = mStorageControllers->begin();
11937 it != mStorageControllers->end();
11938 ++it)
11939 {
11940 if ( std::find(backedList->begin(), backedList->end(), *it)
11941 == backedList->end()
11942 )
11943 {
11944 (*it)->uninit();
11945 }
11946 }
11947
11948 /* restore the list */
11949 mStorageControllers.rollback();
11950 }
11951
11952 /* rollback any changes to devices after restoring the list */
11953 if (mData->flModifications & IsModified_Storage)
11954 {
11955 for (StorageControllerList::const_iterator
11956 it = mStorageControllers->begin();
11957 it != mStorageControllers->end();
11958 ++it)
11959 {
11960 (*it)->i_rollback();
11961 }
11962 }
11963 }
11964
11965 if (!mUSBControllers.isNull())
11966 {
11967 if (mUSBControllers.isBackedUp())
11968 {
11969 /* unitialize all new devices (absent in the backed up list). */
11970 USBControllerList *backedList = mUSBControllers.backedUpData();
11971 for (USBControllerList::const_iterator
11972 it = mUSBControllers->begin();
11973 it != mUSBControllers->end();
11974 ++it)
11975 {
11976 if ( std::find(backedList->begin(), backedList->end(), *it)
11977 == backedList->end()
11978 )
11979 {
11980 (*it)->uninit();
11981 }
11982 }
11983
11984 /* restore the list */
11985 mUSBControllers.rollback();
11986 }
11987
11988 /* rollback any changes to devices after restoring the list */
11989 if (mData->flModifications & IsModified_USB)
11990 {
11991 for (USBControllerList::const_iterator
11992 it = mUSBControllers->begin();
11993 it != mUSBControllers->end();
11994 ++it)
11995 {
11996 (*it)->i_rollback();
11997 }
11998 }
11999 }
12000
12001 mUserData.rollback();
12002
12003 mHWData.rollback();
12004
12005 if (mData->flModifications & IsModified_Storage)
12006 i_rollbackMedia();
12007
12008 if (mBIOSSettings)
12009 mBIOSSettings->i_rollback();
12010
12011 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
12012 mVRDEServer->i_rollback();
12013
12014 if (mAudioAdapter)
12015 mAudioAdapter->i_rollback();
12016
12017 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
12018 mUSBDeviceFilters->i_rollback();
12019
12020 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
12021 mBandwidthControl->i_rollback();
12022
12023 if (!mHWData.isNull())
12024 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
12025 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
12026 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
12027 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
12028
12029 if (mData->flModifications & IsModified_NetworkAdapters)
12030 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12031 if ( mNetworkAdapters[slot]
12032 && mNetworkAdapters[slot]->i_isModified())
12033 {
12034 mNetworkAdapters[slot]->i_rollback();
12035 networkAdapters[slot] = mNetworkAdapters[slot];
12036 }
12037
12038 if (mData->flModifications & IsModified_SerialPorts)
12039 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12040 if ( mSerialPorts[slot]
12041 && mSerialPorts[slot]->i_isModified())
12042 {
12043 mSerialPorts[slot]->i_rollback();
12044 serialPorts[slot] = mSerialPorts[slot];
12045 }
12046
12047 if (mData->flModifications & IsModified_ParallelPorts)
12048 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12049 if ( mParallelPorts[slot]
12050 && mParallelPorts[slot]->i_isModified())
12051 {
12052 mParallelPorts[slot]->i_rollback();
12053 parallelPorts[slot] = mParallelPorts[slot];
12054 }
12055
12056 if (aNotify)
12057 {
12058 /* inform the direct session about changes */
12059
12060 ComObjPtr<Machine> that = this;
12061 uint32_t flModifications = mData->flModifications;
12062 alock.release();
12063
12064 if (flModifications & IsModified_SharedFolders)
12065 that->i_onSharedFolderChange();
12066
12067 if (flModifications & IsModified_VRDEServer)
12068 that->i_onVRDEServerChange(/* aRestart */ TRUE);
12069 if (flModifications & IsModified_USB)
12070 that->i_onUSBControllerChange();
12071
12072 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
12073 if (networkAdapters[slot])
12074 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
12075 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
12076 if (serialPorts[slot])
12077 that->i_onSerialPortChange(serialPorts[slot]);
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
12079 if (parallelPorts[slot])
12080 that->i_onParallelPortChange(parallelPorts[slot]);
12081
12082 if (flModifications & IsModified_Storage)
12083 that->i_onStorageControllerChange();
12084
12085#if 0
12086 if (flModifications & IsModified_BandwidthControl)
12087 that->onBandwidthControlChange();
12088#endif
12089 }
12090}
12091
12092/**
12093 * Commits all the changes to machine settings.
12094 *
12095 * Note that this operation is supposed to never fail.
12096 *
12097 * @note Locks this object and children for writing.
12098 */
12099void Machine::i_commit()
12100{
12101 AutoCaller autoCaller(this);
12102 AssertComRCReturnVoid(autoCaller.rc());
12103
12104 AutoCaller peerCaller(mPeer);
12105 AssertComRCReturnVoid(peerCaller.rc());
12106
12107 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
12108
12109 /*
12110 * use safe commit to ensure Snapshot machines (that share mUserData)
12111 * will still refer to a valid memory location
12112 */
12113 mUserData.commitCopy();
12114
12115 mHWData.commit();
12116
12117 if (mMediumAttachments.isBackedUp())
12118 i_commitMedia(Global::IsOnline(mData->mMachineState));
12119
12120 mBIOSSettings->i_commit();
12121 mVRDEServer->i_commit();
12122 mAudioAdapter->i_commit();
12123 mUSBDeviceFilters->i_commit();
12124 mBandwidthControl->i_commit();
12125
12126 /* Since mNetworkAdapters is a list which might have been changed (resized)
12127 * without using the Backupable<> template we need to handle the copying
12128 * of the list entries manually, including the creation of peers for the
12129 * new objects. */
12130 bool commitNetworkAdapters = false;
12131 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
12132 if (mPeer)
12133 {
12134 /* commit everything, even the ones which will go away */
12135 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
12136 mNetworkAdapters[slot]->i_commit();
12137 /* copy over the new entries, creating a peer and uninit the original */
12138 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
12139 for (size_t slot = 0; slot < newSize; slot++)
12140 {
12141 /* look if this adapter has a peer device */
12142 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
12143 if (!peer)
12144 {
12145 /* no peer means the adapter is a newly created one;
12146 * create a peer owning data this data share it with */
12147 peer.createObject();
12148 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
12149 }
12150 mPeer->mNetworkAdapters[slot] = peer;
12151 }
12152 /* uninit any no longer needed network adapters */
12153 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
12154 mNetworkAdapters[slot]->uninit();
12155 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
12156 {
12157 if (mPeer->mNetworkAdapters[slot])
12158 mPeer->mNetworkAdapters[slot]->uninit();
12159 }
12160 /* Keep the original network adapter count until this point, so that
12161 * discarding a chipset type change will not lose settings. */
12162 mNetworkAdapters.resize(newSize);
12163 mPeer->mNetworkAdapters.resize(newSize);
12164 }
12165 else
12166 {
12167 /* we have no peer (our parent is the newly created machine);
12168 * just commit changes to the network adapters */
12169 commitNetworkAdapters = true;
12170 }
12171 if (commitNetworkAdapters)
12172 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
12173 mNetworkAdapters[slot]->i_commit();
12174
12175 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12176 mSerialPorts[slot]->i_commit();
12177 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12178 mParallelPorts[slot]->i_commit();
12179
12180 bool commitStorageControllers = false;
12181
12182 if (mStorageControllers.isBackedUp())
12183 {
12184 mStorageControllers.commit();
12185
12186 if (mPeer)
12187 {
12188 /* Commit all changes to new controllers (this will reshare data with
12189 * peers for those who have peers) */
12190 StorageControllerList *newList = new StorageControllerList();
12191 for (StorageControllerList::const_iterator
12192 it = mStorageControllers->begin();
12193 it != mStorageControllers->end();
12194 ++it)
12195 {
12196 (*it)->i_commit();
12197
12198 /* look if this controller has a peer device */
12199 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
12200 if (!peer)
12201 {
12202 /* no peer means the device is a newly created one;
12203 * create a peer owning data this device share it with */
12204 peer.createObject();
12205 peer->init(mPeer, *it, true /* aReshare */);
12206 }
12207 else
12208 {
12209 /* remove peer from the old list */
12210 mPeer->mStorageControllers->remove(peer);
12211 }
12212 /* and add it to the new list */
12213 newList->push_back(peer);
12214 }
12215
12216 /* uninit old peer's controllers that are left */
12217 for (StorageControllerList::const_iterator
12218 it = mPeer->mStorageControllers->begin();
12219 it != mPeer->mStorageControllers->end();
12220 ++it)
12221 {
12222 (*it)->uninit();
12223 }
12224
12225 /* attach new list of controllers to our peer */
12226 mPeer->mStorageControllers.attach(newList);
12227 }
12228 else
12229 {
12230 /* we have no peer (our parent is the newly created machine);
12231 * just commit changes to devices */
12232 commitStorageControllers = true;
12233 }
12234 }
12235 else
12236 {
12237 /* the list of controllers itself is not changed,
12238 * just commit changes to controllers themselves */
12239 commitStorageControllers = true;
12240 }
12241
12242 if (commitStorageControllers)
12243 {
12244 for (StorageControllerList::const_iterator
12245 it = mStorageControllers->begin();
12246 it != mStorageControllers->end();
12247 ++it)
12248 {
12249 (*it)->i_commit();
12250 }
12251 }
12252
12253 bool commitUSBControllers = false;
12254
12255 if (mUSBControllers.isBackedUp())
12256 {
12257 mUSBControllers.commit();
12258
12259 if (mPeer)
12260 {
12261 /* Commit all changes to new controllers (this will reshare data with
12262 * peers for those who have peers) */
12263 USBControllerList *newList = new USBControllerList();
12264 for (USBControllerList::const_iterator
12265 it = mUSBControllers->begin();
12266 it != mUSBControllers->end();
12267 ++it)
12268 {
12269 (*it)->i_commit();
12270
12271 /* look if this controller has a peer device */
12272 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12273 if (!peer)
12274 {
12275 /* no peer means the device is a newly created one;
12276 * create a peer owning data this device share it with */
12277 peer.createObject();
12278 peer->init(mPeer, *it, true /* aReshare */);
12279 }
12280 else
12281 {
12282 /* remove peer from the old list */
12283 mPeer->mUSBControllers->remove(peer);
12284 }
12285 /* and add it to the new list */
12286 newList->push_back(peer);
12287 }
12288
12289 /* uninit old peer's controllers that are left */
12290 for (USBControllerList::const_iterator
12291 it = mPeer->mUSBControllers->begin();
12292 it != mPeer->mUSBControllers->end();
12293 ++it)
12294 {
12295 (*it)->uninit();
12296 }
12297
12298 /* attach new list of controllers to our peer */
12299 mPeer->mUSBControllers.attach(newList);
12300 }
12301 else
12302 {
12303 /* we have no peer (our parent is the newly created machine);
12304 * just commit changes to devices */
12305 commitUSBControllers = true;
12306 }
12307 }
12308 else
12309 {
12310 /* the list of controllers itself is not changed,
12311 * just commit changes to controllers themselves */
12312 commitUSBControllers = true;
12313 }
12314
12315 if (commitUSBControllers)
12316 {
12317 for (USBControllerList::const_iterator
12318 it = mUSBControllers->begin();
12319 it != mUSBControllers->end();
12320 ++it)
12321 {
12322 (*it)->i_commit();
12323 }
12324 }
12325
12326 if (i_isSessionMachine())
12327 {
12328 /* attach new data to the primary machine and reshare it */
12329 mPeer->mUserData.attach(mUserData);
12330 mPeer->mHWData.attach(mHWData);
12331 /* mmMediumAttachments is reshared by fixupMedia */
12332 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12333 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12334 }
12335}
12336
12337/**
12338 * Copies all the hardware data from the given machine.
12339 *
12340 * Currently, only called when the VM is being restored from a snapshot. In
12341 * particular, this implies that the VM is not running during this method's
12342 * call.
12343 *
12344 * @note This method must be called from under this object's lock.
12345 *
12346 * @note This method doesn't call #i_commit(), so all data remains backed up and
12347 * unsaved.
12348 */
12349void Machine::i_copyFrom(Machine *aThat)
12350{
12351 AssertReturnVoid(!i_isSnapshotMachine());
12352 AssertReturnVoid(aThat->i_isSnapshotMachine());
12353
12354 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12355
12356 mHWData.assignCopy(aThat->mHWData);
12357
12358 // create copies of all shared folders (mHWData after attaching a copy
12359 // contains just references to original objects)
12360 for (HWData::SharedFolderList::iterator
12361 it = mHWData->mSharedFolders.begin();
12362 it != mHWData->mSharedFolders.end();
12363 ++it)
12364 {
12365 ComObjPtr<SharedFolder> folder;
12366 folder.createObject();
12367 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12368 AssertComRC(rc);
12369 *it = folder;
12370 }
12371
12372 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12373 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12374 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12375 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12376 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12377
12378 /* create private copies of all controllers */
12379 mStorageControllers.backup();
12380 mStorageControllers->clear();
12381 for (StorageControllerList::const_iterator
12382 it = aThat->mStorageControllers->begin();
12383 it != aThat->mStorageControllers->end();
12384 ++it)
12385 {
12386 ComObjPtr<StorageController> ctrl;
12387 ctrl.createObject();
12388 ctrl->initCopy(this, *it);
12389 mStorageControllers->push_back(ctrl);
12390 }
12391
12392 /* create private copies of all USB controllers */
12393 mUSBControllers.backup();
12394 mUSBControllers->clear();
12395 for (USBControllerList::const_iterator
12396 it = aThat->mUSBControllers->begin();
12397 it != aThat->mUSBControllers->end();
12398 ++it)
12399 {
12400 ComObjPtr<USBController> ctrl;
12401 ctrl.createObject();
12402 ctrl->initCopy(this, *it);
12403 mUSBControllers->push_back(ctrl);
12404 }
12405
12406 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12407 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12408 {
12409 if (mNetworkAdapters[slot].isNotNull())
12410 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12411 else
12412 {
12413 unconst(mNetworkAdapters[slot]).createObject();
12414 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12415 }
12416 }
12417 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12418 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12419 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12420 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12421}
12422
12423/**
12424 * Returns whether the given storage controller is hotplug capable.
12425 *
12426 * @returns true if the controller supports hotplugging
12427 * false otherwise.
12428 * @param enmCtrlType The controller type to check for.
12429 */
12430bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12431{
12432 ComPtr<ISystemProperties> systemProperties;
12433 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12434 if (FAILED(rc))
12435 return false;
12436
12437 BOOL aHotplugCapable = FALSE;
12438 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12439
12440 return RT_BOOL(aHotplugCapable);
12441}
12442
12443#ifdef VBOX_WITH_RESOURCE_USAGE_API
12444
12445void Machine::i_getDiskList(MediaList &list)
12446{
12447 for (MediumAttachmentList::const_iterator
12448 it = mMediumAttachments->begin();
12449 it != mMediumAttachments->end();
12450 ++it)
12451 {
12452 MediumAttachment *pAttach = *it;
12453 /* just in case */
12454 AssertContinue(pAttach);
12455
12456 AutoCaller localAutoCallerA(pAttach);
12457 if (FAILED(localAutoCallerA.rc())) continue;
12458
12459 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12460
12461 if (pAttach->i_getType() == DeviceType_HardDisk)
12462 list.push_back(pAttach->i_getMedium());
12463 }
12464}
12465
12466void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12467{
12468 AssertReturnVoid(isWriteLockOnCurrentThread());
12469 AssertPtrReturnVoid(aCollector);
12470
12471 pm::CollectorHAL *hal = aCollector->getHAL();
12472 /* Create sub metrics */
12473 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12474 "Percentage of processor time spent in user mode by the VM process.");
12475 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12476 "Percentage of processor time spent in kernel mode by the VM process.");
12477 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12478 "Size of resident portion of VM process in memory.");
12479 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12480 "Actual size of all VM disks combined.");
12481 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12482 "Network receive rate.");
12483 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12484 "Network transmit rate.");
12485 /* Create and register base metrics */
12486 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12487 cpuLoadUser, cpuLoadKernel);
12488 aCollector->registerBaseMetric(cpuLoad);
12489 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12490 ramUsageUsed);
12491 aCollector->registerBaseMetric(ramUsage);
12492 MediaList disks;
12493 i_getDiskList(disks);
12494 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12495 diskUsageUsed);
12496 aCollector->registerBaseMetric(diskUsage);
12497
12498 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12499 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12500 new pm::AggregateAvg()));
12501 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12502 new pm::AggregateMin()));
12503 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12504 new pm::AggregateMax()));
12505 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12506 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12507 new pm::AggregateAvg()));
12508 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12509 new pm::AggregateMin()));
12510 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12511 new pm::AggregateMax()));
12512
12513 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12514 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12515 new pm::AggregateAvg()));
12516 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12517 new pm::AggregateMin()));
12518 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12519 new pm::AggregateMax()));
12520
12521 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12522 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12523 new pm::AggregateAvg()));
12524 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12525 new pm::AggregateMin()));
12526 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12527 new pm::AggregateMax()));
12528
12529
12530 /* Guest metrics collector */
12531 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12532 aCollector->registerGuest(mCollectorGuest);
12533 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12534
12535 /* Create sub metrics */
12536 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12537 "Percentage of processor time spent in user mode as seen by the guest.");
12538 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12539 "Percentage of processor time spent in kernel mode as seen by the guest.");
12540 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12541 "Percentage of processor time spent idling as seen by the guest.");
12542
12543 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12544 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12545 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12546 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12547 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12548 pm::SubMetric *guestMemCache = new pm::SubMetric(
12549 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12550
12551 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12552 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12553
12554 /* Create and register base metrics */
12555 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12556 machineNetRx, machineNetTx);
12557 aCollector->registerBaseMetric(machineNetRate);
12558
12559 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12560 guestLoadUser, guestLoadKernel, guestLoadIdle);
12561 aCollector->registerBaseMetric(guestCpuLoad);
12562
12563 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12564 guestMemTotal, guestMemFree,
12565 guestMemBalloon, guestMemShared,
12566 guestMemCache, guestPagedTotal);
12567 aCollector->registerBaseMetric(guestCpuMem);
12568
12569 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12570 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12571 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12572 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12573
12574 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12575 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12576 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12577 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12578
12579 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12580 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12581 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12582 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12583
12584 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12585 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12586 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12587 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12588
12589 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12590 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12591 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12592 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12593
12594 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12595 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12596 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12597 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12598
12599 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12600 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12601 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12602 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12603
12604 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12605 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12606 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12607 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12608
12609 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12610 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12611 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12612 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12613
12614 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12615 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12616 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12617 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12618
12619 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12620 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12621 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12622 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12623}
12624
12625void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12626{
12627 AssertReturnVoid(isWriteLockOnCurrentThread());
12628
12629 if (aCollector)
12630 {
12631 aCollector->unregisterMetricsFor(aMachine);
12632 aCollector->unregisterBaseMetricsFor(aMachine);
12633 }
12634}
12635
12636#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12637
12638
12639////////////////////////////////////////////////////////////////////////////////
12640
12641DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12642
12643HRESULT SessionMachine::FinalConstruct()
12644{
12645 LogFlowThisFunc(("\n"));
12646
12647 mClientToken = NULL;
12648
12649 return BaseFinalConstruct();
12650}
12651
12652void SessionMachine::FinalRelease()
12653{
12654 LogFlowThisFunc(("\n"));
12655
12656 Assert(!mClientToken);
12657 /* paranoia, should not hang around any more */
12658 if (mClientToken)
12659 {
12660 delete mClientToken;
12661 mClientToken = NULL;
12662 }
12663
12664 uninit(Uninit::Unexpected);
12665
12666 BaseFinalRelease();
12667}
12668
12669/**
12670 * @note Must be called only by Machine::LockMachine() from its own write lock.
12671 */
12672HRESULT SessionMachine::init(Machine *aMachine)
12673{
12674 LogFlowThisFuncEnter();
12675 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12676
12677 AssertReturn(aMachine, E_INVALIDARG);
12678
12679 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12680
12681 /* Enclose the state transition NotReady->InInit->Ready */
12682 AutoInitSpan autoInitSpan(this);
12683 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12684
12685 HRESULT rc = S_OK;
12686
12687 RT_ZERO(mAuthLibCtx);
12688
12689 /* create the machine client token */
12690 try
12691 {
12692 mClientToken = new ClientToken(aMachine, this);
12693 if (!mClientToken->isReady())
12694 {
12695 delete mClientToken;
12696 mClientToken = NULL;
12697 rc = E_FAIL;
12698 }
12699 }
12700 catch (std::bad_alloc &)
12701 {
12702 rc = E_OUTOFMEMORY;
12703 }
12704 if (FAILED(rc))
12705 return rc;
12706
12707 /* memorize the peer Machine */
12708 unconst(mPeer) = aMachine;
12709 /* share the parent pointer */
12710 unconst(mParent) = aMachine->mParent;
12711
12712 /* take the pointers to data to share */
12713 mData.share(aMachine->mData);
12714 mSSData.share(aMachine->mSSData);
12715
12716 mUserData.share(aMachine->mUserData);
12717 mHWData.share(aMachine->mHWData);
12718 mMediumAttachments.share(aMachine->mMediumAttachments);
12719
12720 mStorageControllers.allocate();
12721 for (StorageControllerList::const_iterator
12722 it = aMachine->mStorageControllers->begin();
12723 it != aMachine->mStorageControllers->end();
12724 ++it)
12725 {
12726 ComObjPtr<StorageController> ctl;
12727 ctl.createObject();
12728 ctl->init(this, *it);
12729 mStorageControllers->push_back(ctl);
12730 }
12731
12732 mUSBControllers.allocate();
12733 for (USBControllerList::const_iterator
12734 it = aMachine->mUSBControllers->begin();
12735 it != aMachine->mUSBControllers->end();
12736 ++it)
12737 {
12738 ComObjPtr<USBController> ctl;
12739 ctl.createObject();
12740 ctl->init(this, *it);
12741 mUSBControllers->push_back(ctl);
12742 }
12743
12744 unconst(mBIOSSettings).createObject();
12745 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12746 /* create another VRDEServer object that will be mutable */
12747 unconst(mVRDEServer).createObject();
12748 mVRDEServer->init(this, aMachine->mVRDEServer);
12749 /* create another audio adapter object that will be mutable */
12750 unconst(mAudioAdapter).createObject();
12751 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12752 /* create a list of serial ports that will be mutable */
12753 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12754 {
12755 unconst(mSerialPorts[slot]).createObject();
12756 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12757 }
12758 /* create a list of parallel ports that will be mutable */
12759 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12760 {
12761 unconst(mParallelPorts[slot]).createObject();
12762 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12763 }
12764
12765 /* create another USB device filters object that will be mutable */
12766 unconst(mUSBDeviceFilters).createObject();
12767 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12768
12769 /* create a list of network adapters that will be mutable */
12770 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12771 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12772 {
12773 unconst(mNetworkAdapters[slot]).createObject();
12774 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12775 }
12776
12777 /* create another bandwidth control object that will be mutable */
12778 unconst(mBandwidthControl).createObject();
12779 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12780
12781 /* default is to delete saved state on Saved -> PoweredOff transition */
12782 mRemoveSavedState = true;
12783
12784 /* Confirm a successful initialization when it's the case */
12785 autoInitSpan.setSucceeded();
12786
12787 miNATNetworksStarted = 0;
12788
12789 LogFlowThisFuncLeave();
12790 return rc;
12791}
12792
12793/**
12794 * Uninitializes this session object. If the reason is other than
12795 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12796 * or the client watcher code.
12797 *
12798 * @param aReason uninitialization reason
12799 *
12800 * @note Locks mParent + this object for writing.
12801 */
12802void SessionMachine::uninit(Uninit::Reason aReason)
12803{
12804 LogFlowThisFuncEnter();
12805 LogFlowThisFunc(("reason=%d\n", aReason));
12806
12807 /*
12808 * Strongly reference ourselves to prevent this object deletion after
12809 * mData->mSession.mMachine.setNull() below (which can release the last
12810 * reference and call the destructor). Important: this must be done before
12811 * accessing any members (and before AutoUninitSpan that does it as well).
12812 * This self reference will be released as the very last step on return.
12813 */
12814 ComObjPtr<SessionMachine> selfRef;
12815 if (aReason != Uninit::Unexpected)
12816 selfRef = this;
12817
12818 /* Enclose the state transition Ready->InUninit->NotReady */
12819 AutoUninitSpan autoUninitSpan(this);
12820 if (autoUninitSpan.uninitDone())
12821 {
12822 LogFlowThisFunc(("Already uninitialized\n"));
12823 LogFlowThisFuncLeave();
12824 return;
12825 }
12826
12827 if (autoUninitSpan.initFailed())
12828 {
12829 /* We've been called by init() because it's failed. It's not really
12830 * necessary (nor it's safe) to perform the regular uninit sequence
12831 * below, the following is enough.
12832 */
12833 LogFlowThisFunc(("Initialization failed.\n"));
12834 /* destroy the machine client token */
12835 if (mClientToken)
12836 {
12837 delete mClientToken;
12838 mClientToken = NULL;
12839 }
12840 uninitDataAndChildObjects();
12841 mData.free();
12842 unconst(mParent) = NULL;
12843 unconst(mPeer) = NULL;
12844 LogFlowThisFuncLeave();
12845 return;
12846 }
12847
12848 MachineState_T lastState;
12849 {
12850 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12851 lastState = mData->mMachineState;
12852 }
12853 NOREF(lastState);
12854
12855#ifdef VBOX_WITH_USB
12856 // release all captured USB devices, but do this before requesting the locks below
12857 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12858 {
12859 /* Console::captureUSBDevices() is called in the VM process only after
12860 * setting the machine state to Starting or Restoring.
12861 * Console::detachAllUSBDevices() will be called upon successful
12862 * termination. So, we need to release USB devices only if there was
12863 * an abnormal termination of a running VM.
12864 *
12865 * This is identical to SessionMachine::DetachAllUSBDevices except
12866 * for the aAbnormal argument. */
12867 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12868 AssertComRC(rc);
12869 NOREF(rc);
12870
12871 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12872 if (service)
12873 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12874 }
12875#endif /* VBOX_WITH_USB */
12876
12877 // we need to lock this object in uninit() because the lock is shared
12878 // with mPeer (as well as data we modify below). mParent lock is needed
12879 // by several calls to it.
12880 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12881
12882#ifdef VBOX_WITH_RESOURCE_USAGE_API
12883 /*
12884 * It is safe to call Machine::i_unregisterMetrics() here because
12885 * PerformanceCollector::samplerCallback no longer accesses guest methods
12886 * holding the lock.
12887 */
12888 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12889 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12890 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12891 if (mCollectorGuest)
12892 {
12893 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12894 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12895 mCollectorGuest = NULL;
12896 }
12897#endif
12898
12899 if (aReason == Uninit::Abnormal)
12900 {
12901 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12902
12903 /* reset the state to Aborted */
12904 if (mData->mMachineState != MachineState_Aborted)
12905 i_setMachineState(MachineState_Aborted);
12906 }
12907
12908 // any machine settings modified?
12909 if (mData->flModifications)
12910 {
12911 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12912 i_rollback(false /* aNotify */);
12913 }
12914
12915 mData->mSession.mPID = NIL_RTPROCESS;
12916
12917 if (aReason == Uninit::Unexpected)
12918 {
12919 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12920 * client watcher thread to update the set of machines that have open
12921 * sessions. */
12922 mParent->i_updateClientWatcher();
12923 }
12924
12925 /* uninitialize all remote controls */
12926 if (mData->mSession.mRemoteControls.size())
12927 {
12928 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12929 mData->mSession.mRemoteControls.size()));
12930
12931 /* Always restart a the beginning, since the iterator is invalidated
12932 * by using erase(). */
12933 for (Data::Session::RemoteControlList::iterator
12934 it = mData->mSession.mRemoteControls.begin();
12935 it != mData->mSession.mRemoteControls.end();
12936 it = mData->mSession.mRemoteControls.begin())
12937 {
12938 ComPtr<IInternalSessionControl> pControl = *it;
12939 mData->mSession.mRemoteControls.erase(it);
12940 multilock.release();
12941 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12942 HRESULT rc = pControl->Uninitialize();
12943 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12944 if (FAILED(rc))
12945 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12946 multilock.acquire();
12947 }
12948 mData->mSession.mRemoteControls.clear();
12949 }
12950
12951 /* Remove all references to the NAT network service. The service will stop
12952 * if all references (also from other VMs) are removed. */
12953 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12954 {
12955 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12956 {
12957 BOOL enabled;
12958 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12959 if ( FAILED(hrc)
12960 || !enabled)
12961 continue;
12962
12963 NetworkAttachmentType_T type;
12964 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12965 if ( SUCCEEDED(hrc)
12966 && type == NetworkAttachmentType_NATNetwork)
12967 {
12968 Bstr name;
12969 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12970 if (SUCCEEDED(hrc))
12971 {
12972 multilock.release();
12973 Utf8Str strName(name);
12974 LogRel(("VM '%s' stops using NAT network '%s'\n",
12975 mUserData->s.strName.c_str(), strName.c_str()));
12976 mParent->i_natNetworkRefDec(strName);
12977 multilock.acquire();
12978 }
12979 }
12980 }
12981 }
12982
12983 /*
12984 * An expected uninitialization can come only from #i_checkForDeath().
12985 * Otherwise it means that something's gone really wrong (for example,
12986 * the Session implementation has released the VirtualBox reference
12987 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12988 * etc). However, it's also possible, that the client releases the IPC
12989 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12990 * but the VirtualBox release event comes first to the server process.
12991 * This case is practically possible, so we should not assert on an
12992 * unexpected uninit, just log a warning.
12993 */
12994
12995 if (aReason == Uninit::Unexpected)
12996 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12997
12998 if (aReason != Uninit::Normal)
12999 {
13000 mData->mSession.mDirectControl.setNull();
13001 }
13002 else
13003 {
13004 /* this must be null here (see #OnSessionEnd()) */
13005 Assert(mData->mSession.mDirectControl.isNull());
13006 Assert(mData->mSession.mState == SessionState_Unlocking);
13007 Assert(!mData->mSession.mProgress.isNull());
13008 }
13009 if (mData->mSession.mProgress)
13010 {
13011 if (aReason == Uninit::Normal)
13012 mData->mSession.mProgress->i_notifyComplete(S_OK);
13013 else
13014 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
13015 COM_IIDOF(ISession),
13016 getComponentName(),
13017 tr("The VM session was aborted"));
13018 mData->mSession.mProgress.setNull();
13019 }
13020
13021 if (mConsoleTaskData.mProgress)
13022 {
13023 Assert(aReason == Uninit::Abnormal);
13024 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
13025 COM_IIDOF(ISession),
13026 getComponentName(),
13027 tr("The VM session was aborted"));
13028 mConsoleTaskData.mProgress.setNull();
13029 }
13030
13031 /* remove the association between the peer machine and this session machine */
13032 Assert( (SessionMachine*)mData->mSession.mMachine == this
13033 || aReason == Uninit::Unexpected);
13034
13035 /* reset the rest of session data */
13036 mData->mSession.mLockType = LockType_Null;
13037 mData->mSession.mMachine.setNull();
13038 mData->mSession.mState = SessionState_Unlocked;
13039 mData->mSession.mName.setNull();
13040
13041 /* destroy the machine client token before leaving the exclusive lock */
13042 if (mClientToken)
13043 {
13044 delete mClientToken;
13045 mClientToken = NULL;
13046 }
13047
13048 /* fire an event */
13049 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
13050
13051 uninitDataAndChildObjects();
13052
13053 /* free the essential data structure last */
13054 mData.free();
13055
13056 /* release the exclusive lock before setting the below two to NULL */
13057 multilock.release();
13058
13059 unconst(mParent) = NULL;
13060 unconst(mPeer) = NULL;
13061
13062 AuthLibUnload(&mAuthLibCtx);
13063
13064 LogFlowThisFuncLeave();
13065}
13066
13067// util::Lockable interface
13068////////////////////////////////////////////////////////////////////////////////
13069
13070/**
13071 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
13072 * with the primary Machine instance (mPeer).
13073 */
13074RWLockHandle *SessionMachine::lockHandle() const
13075{
13076 AssertReturn(mPeer != NULL, NULL);
13077 return mPeer->lockHandle();
13078}
13079
13080// IInternalMachineControl methods
13081////////////////////////////////////////////////////////////////////////////////
13082
13083/**
13084 * Passes collected guest statistics to performance collector object
13085 */
13086HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
13087 ULONG aCpuKernel, ULONG aCpuIdle,
13088 ULONG aMemTotal, ULONG aMemFree,
13089 ULONG aMemBalloon, ULONG aMemShared,
13090 ULONG aMemCache, ULONG aPageTotal,
13091 ULONG aAllocVMM, ULONG aFreeVMM,
13092 ULONG aBalloonedVMM, ULONG aSharedVMM,
13093 ULONG aVmNetRx, ULONG aVmNetTx)
13094{
13095#ifdef VBOX_WITH_RESOURCE_USAGE_API
13096 if (mCollectorGuest)
13097 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
13098 aMemTotal, aMemFree, aMemBalloon, aMemShared,
13099 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
13100 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
13101
13102 return S_OK;
13103#else
13104 NOREF(aValidStats);
13105 NOREF(aCpuUser);
13106 NOREF(aCpuKernel);
13107 NOREF(aCpuIdle);
13108 NOREF(aMemTotal);
13109 NOREF(aMemFree);
13110 NOREF(aMemBalloon);
13111 NOREF(aMemShared);
13112 NOREF(aMemCache);
13113 NOREF(aPageTotal);
13114 NOREF(aAllocVMM);
13115 NOREF(aFreeVMM);
13116 NOREF(aBalloonedVMM);
13117 NOREF(aSharedVMM);
13118 NOREF(aVmNetRx);
13119 NOREF(aVmNetTx);
13120 return E_NOTIMPL;
13121#endif
13122}
13123
13124////////////////////////////////////////////////////////////////////////////////
13125//
13126// SessionMachine task records
13127//
13128////////////////////////////////////////////////////////////////////////////////
13129
13130/**
13131 * Task record for saving the machine state.
13132 */
13133class SessionMachine::SaveStateTask
13134 : public Machine::Task
13135{
13136public:
13137 SaveStateTask(SessionMachine *m,
13138 Progress *p,
13139 const Utf8Str &t,
13140 Reason_T enmReason,
13141 const Utf8Str &strStateFilePath)
13142 : Task(m, p, t),
13143 m_enmReason(enmReason),
13144 m_strStateFilePath(strStateFilePath)
13145 {}
13146
13147private:
13148 void handler()
13149 {
13150 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
13151 }
13152
13153 Reason_T m_enmReason;
13154 Utf8Str m_strStateFilePath;
13155
13156 friend class SessionMachine;
13157};
13158
13159/**
13160 * Task thread implementation for SessionMachine::SaveState(), called from
13161 * SessionMachine::taskHandler().
13162 *
13163 * @note Locks this object for writing.
13164 *
13165 * @param task
13166 * @return
13167 */
13168void SessionMachine::i_saveStateHandler(SaveStateTask &task)
13169{
13170 LogFlowThisFuncEnter();
13171
13172 AutoCaller autoCaller(this);
13173 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
13174 if (FAILED(autoCaller.rc()))
13175 {
13176 /* we might have been uninitialized because the session was accidentally
13177 * closed by the client, so don't assert */
13178 HRESULT rc = setError(E_FAIL,
13179 tr("The session has been accidentally closed"));
13180 task.m_pProgress->i_notifyComplete(rc);
13181 LogFlowThisFuncLeave();
13182 return;
13183 }
13184
13185 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13186
13187 HRESULT rc = S_OK;
13188
13189 try
13190 {
13191 ComPtr<IInternalSessionControl> directControl;
13192 if (mData->mSession.mLockType == LockType_VM)
13193 directControl = mData->mSession.mDirectControl;
13194 if (directControl.isNull())
13195 throw setError(VBOX_E_INVALID_VM_STATE,
13196 tr("Trying to save state without a running VM"));
13197 alock.release();
13198 BOOL fSuspendedBySave;
13199 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
13200 Assert(!fSuspendedBySave);
13201 alock.acquire();
13202
13203 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
13204 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
13205 throw E_FAIL);
13206
13207 if (SUCCEEDED(rc))
13208 {
13209 mSSData->strStateFilePath = task.m_strStateFilePath;
13210
13211 /* save all VM settings */
13212 rc = i_saveSettings(NULL);
13213 // no need to check whether VirtualBox.xml needs saving also since
13214 // we can't have a name change pending at this point
13215 }
13216 else
13217 {
13218 // On failure, set the state to the state we had at the beginning.
13219 i_setMachineState(task.m_machineStateBackup);
13220 i_updateMachineStateOnClient();
13221
13222 // Delete the saved state file (might have been already created).
13223 // No need to check whether this is shared with a snapshot here
13224 // because we certainly created a fresh saved state file here.
13225 RTFileDelete(task.m_strStateFilePath.c_str());
13226 }
13227 }
13228 catch (HRESULT aRC) { rc = aRC; }
13229
13230 task.m_pProgress->i_notifyComplete(rc);
13231
13232 LogFlowThisFuncLeave();
13233}
13234
13235/**
13236 * @note Locks this object for writing.
13237 */
13238HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
13239{
13240 return i_saveStateWithReason(Reason_Unspecified, aProgress);
13241}
13242
13243HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
13244{
13245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13246
13247 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13248 if (FAILED(rc)) return rc;
13249
13250 if ( mData->mMachineState != MachineState_Running
13251 && mData->mMachineState != MachineState_Paused
13252 )
13253 return setError(VBOX_E_INVALID_VM_STATE,
13254 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13255 Global::stringifyMachineState(mData->mMachineState));
13256
13257 ComObjPtr<Progress> pProgress;
13258 pProgress.createObject();
13259 rc = pProgress->init(i_getVirtualBox(),
13260 static_cast<IMachine *>(this) /* aInitiator */,
13261 tr("Saving the execution state of the virtual machine"),
13262 FALSE /* aCancelable */);
13263 if (FAILED(rc))
13264 return rc;
13265
13266 Utf8Str strStateFilePath;
13267 i_composeSavedStateFilename(strStateFilePath);
13268
13269 /* create and start the task on a separate thread (note that it will not
13270 * start working until we release alock) */
13271 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13272 rc = pTask->createThread();
13273 if (FAILED(rc))
13274 return rc;
13275
13276 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13277 i_setMachineState(MachineState_Saving);
13278 i_updateMachineStateOnClient();
13279
13280 pProgress.queryInterfaceTo(aProgress.asOutParam());
13281
13282 return S_OK;
13283}
13284
13285/**
13286 * @note Locks this object for writing.
13287 */
13288HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13289{
13290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13291
13292 HRESULT rc = i_checkStateDependency(MutableStateDep);
13293 if (FAILED(rc)) return rc;
13294
13295 if ( mData->mMachineState != MachineState_PoweredOff
13296 && mData->mMachineState != MachineState_Teleported
13297 && mData->mMachineState != MachineState_Aborted
13298 )
13299 return setError(VBOX_E_INVALID_VM_STATE,
13300 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13301 Global::stringifyMachineState(mData->mMachineState));
13302
13303 com::Utf8Str stateFilePathFull;
13304 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13305 if (RT_FAILURE(vrc))
13306 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13307 tr("Invalid saved state file path '%s' (%Rrc)"),
13308 aSavedStateFile.c_str(),
13309 vrc);
13310
13311 mSSData->strStateFilePath = stateFilePathFull;
13312
13313 /* The below i_setMachineState() will detect the state transition and will
13314 * update the settings file */
13315
13316 return i_setMachineState(MachineState_Saved);
13317}
13318
13319/**
13320 * @note Locks this object for writing.
13321 */
13322HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13323{
13324 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13325
13326 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13327 if (FAILED(rc)) return rc;
13328
13329 if (mData->mMachineState != MachineState_Saved)
13330 return setError(VBOX_E_INVALID_VM_STATE,
13331 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13332 Global::stringifyMachineState(mData->mMachineState));
13333
13334 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13335
13336 /*
13337 * Saved -> PoweredOff transition will be detected in the SessionMachine
13338 * and properly handled.
13339 */
13340 rc = i_setMachineState(MachineState_PoweredOff);
13341 return rc;
13342}
13343
13344
13345/**
13346 * @note Locks the same as #i_setMachineState() does.
13347 */
13348HRESULT SessionMachine::updateState(MachineState_T aState)
13349{
13350 return i_setMachineState(aState);
13351}
13352
13353/**
13354 * @note Locks this object for writing.
13355 */
13356HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13357{
13358 IProgress *pProgress(aProgress);
13359
13360 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13361
13362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13363
13364 if (mData->mSession.mState != SessionState_Locked)
13365 return VBOX_E_INVALID_OBJECT_STATE;
13366
13367 if (!mData->mSession.mProgress.isNull())
13368 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13369
13370 /* If we didn't reference the NAT network service yet, add a reference to
13371 * force a start */
13372 if (miNATNetworksStarted < 1)
13373 {
13374 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13375 {
13376 BOOL enabled;
13377 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13378 if ( FAILED(hrc)
13379 || !enabled)
13380 continue;
13381
13382 NetworkAttachmentType_T type;
13383 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13384 if ( SUCCEEDED(hrc)
13385 && type == NetworkAttachmentType_NATNetwork)
13386 {
13387 Bstr name;
13388 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13389 if (SUCCEEDED(hrc))
13390 {
13391 Utf8Str strName(name);
13392 LogRel(("VM '%s' starts using NAT network '%s'\n",
13393 mUserData->s.strName.c_str(), strName.c_str()));
13394 mPeer->lockHandle()->unlockWrite();
13395 mParent->i_natNetworkRefInc(strName);
13396#ifdef RT_LOCK_STRICT
13397 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13398#else
13399 mPeer->lockHandle()->lockWrite();
13400#endif
13401 }
13402 }
13403 }
13404 miNATNetworksStarted++;
13405 }
13406
13407 LogFlowThisFunc(("returns S_OK.\n"));
13408 return S_OK;
13409}
13410
13411/**
13412 * @note Locks this object for writing.
13413 */
13414HRESULT SessionMachine::endPowerUp(LONG aResult)
13415{
13416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13417
13418 if (mData->mSession.mState != SessionState_Locked)
13419 return VBOX_E_INVALID_OBJECT_STATE;
13420
13421 /* Finalize the LaunchVMProcess progress object. */
13422 if (mData->mSession.mProgress)
13423 {
13424 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13425 mData->mSession.mProgress.setNull();
13426 }
13427
13428 if (SUCCEEDED((HRESULT)aResult))
13429 {
13430#ifdef VBOX_WITH_RESOURCE_USAGE_API
13431 /* The VM has been powered up successfully, so it makes sense
13432 * now to offer the performance metrics for a running machine
13433 * object. Doing it earlier wouldn't be safe. */
13434 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13435 mData->mSession.mPID);
13436#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13437 }
13438
13439 return S_OK;
13440}
13441
13442/**
13443 * @note Locks this object for writing.
13444 */
13445HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13446{
13447 LogFlowThisFuncEnter();
13448
13449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13450
13451 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13452 E_FAIL);
13453
13454 /* create a progress object to track operation completion */
13455 ComObjPtr<Progress> pProgress;
13456 pProgress.createObject();
13457 pProgress->init(i_getVirtualBox(),
13458 static_cast<IMachine *>(this) /* aInitiator */,
13459 tr("Stopping the virtual machine"),
13460 FALSE /* aCancelable */);
13461
13462 /* fill in the console task data */
13463 mConsoleTaskData.mLastState = mData->mMachineState;
13464 mConsoleTaskData.mProgress = pProgress;
13465
13466 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13467 i_setMachineState(MachineState_Stopping);
13468
13469 pProgress.queryInterfaceTo(aProgress.asOutParam());
13470
13471 return S_OK;
13472}
13473
13474/**
13475 * @note Locks this object for writing.
13476 */
13477HRESULT SessionMachine::endPoweringDown(LONG aResult,
13478 const com::Utf8Str &aErrMsg)
13479{
13480 LogFlowThisFuncEnter();
13481
13482 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13483
13484 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13485 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13486 && mConsoleTaskData.mLastState != MachineState_Null,
13487 E_FAIL);
13488
13489 /*
13490 * On failure, set the state to the state we had when BeginPoweringDown()
13491 * was called (this is expected by Console::PowerDown() and the associated
13492 * task). On success the VM process already changed the state to
13493 * MachineState_PoweredOff, so no need to do anything.
13494 */
13495 if (FAILED(aResult))
13496 i_setMachineState(mConsoleTaskData.mLastState);
13497
13498 /* notify the progress object about operation completion */
13499 Assert(mConsoleTaskData.mProgress);
13500 if (SUCCEEDED(aResult))
13501 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13502 else
13503 {
13504 if (aErrMsg.length())
13505 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13506 COM_IIDOF(ISession),
13507 getComponentName(),
13508 aErrMsg.c_str());
13509 else
13510 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13511 }
13512
13513 /* clear out the temporary saved state data */
13514 mConsoleTaskData.mLastState = MachineState_Null;
13515 mConsoleTaskData.mProgress.setNull();
13516
13517 LogFlowThisFuncLeave();
13518 return S_OK;
13519}
13520
13521
13522/**
13523 * Goes through the USB filters of the given machine to see if the given
13524 * device matches any filter or not.
13525 *
13526 * @note Locks the same as USBController::hasMatchingFilter() does.
13527 */
13528HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13529 BOOL *aMatched,
13530 ULONG *aMaskedInterfaces)
13531{
13532 LogFlowThisFunc(("\n"));
13533
13534#ifdef VBOX_WITH_USB
13535 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13536#else
13537 NOREF(aDevice);
13538 NOREF(aMaskedInterfaces);
13539 *aMatched = FALSE;
13540#endif
13541
13542 return S_OK;
13543}
13544
13545/**
13546 * @note Locks the same as Host::captureUSBDevice() does.
13547 */
13548HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13549{
13550 LogFlowThisFunc(("\n"));
13551
13552#ifdef VBOX_WITH_USB
13553 /* if captureDeviceForVM() fails, it must have set extended error info */
13554 clearError();
13555 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13556 if (FAILED(rc)) return rc;
13557
13558 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13559 AssertReturn(service, E_FAIL);
13560 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13561#else
13562 NOREF(aId);
13563 return E_NOTIMPL;
13564#endif
13565}
13566
13567/**
13568 * @note Locks the same as Host::detachUSBDevice() does.
13569 */
13570HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13571 BOOL aDone)
13572{
13573 LogFlowThisFunc(("\n"));
13574
13575#ifdef VBOX_WITH_USB
13576 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13577 AssertReturn(service, E_FAIL);
13578 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13579#else
13580 NOREF(aId);
13581 NOREF(aDone);
13582 return E_NOTIMPL;
13583#endif
13584}
13585
13586/**
13587 * Inserts all machine filters to the USB proxy service and then calls
13588 * Host::autoCaptureUSBDevices().
13589 *
13590 * Called by Console from the VM process upon VM startup.
13591 *
13592 * @note Locks what called methods lock.
13593 */
13594HRESULT SessionMachine::autoCaptureUSBDevices()
13595{
13596 LogFlowThisFunc(("\n"));
13597
13598#ifdef VBOX_WITH_USB
13599 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13600 AssertComRC(rc);
13601 NOREF(rc);
13602
13603 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13604 AssertReturn(service, E_FAIL);
13605 return service->autoCaptureDevicesForVM(this);
13606#else
13607 return S_OK;
13608#endif
13609}
13610
13611/**
13612 * Removes all machine filters from the USB proxy service and then calls
13613 * Host::detachAllUSBDevices().
13614 *
13615 * Called by Console from the VM process upon normal VM termination or by
13616 * SessionMachine::uninit() upon abnormal VM termination (from under the
13617 * Machine/SessionMachine lock).
13618 *
13619 * @note Locks what called methods lock.
13620 */
13621HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13622{
13623 LogFlowThisFunc(("\n"));
13624
13625#ifdef VBOX_WITH_USB
13626 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13627 AssertComRC(rc);
13628 NOREF(rc);
13629
13630 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13631 AssertReturn(service, E_FAIL);
13632 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13633#else
13634 NOREF(aDone);
13635 return S_OK;
13636#endif
13637}
13638
13639/**
13640 * @note Locks this object for writing.
13641 */
13642HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13643 ComPtr<IProgress> &aProgress)
13644{
13645 LogFlowThisFuncEnter();
13646
13647 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13648 /*
13649 * We don't assert below because it might happen that a non-direct session
13650 * informs us it is closed right after we've been uninitialized -- it's ok.
13651 */
13652
13653 /* get IInternalSessionControl interface */
13654 ComPtr<IInternalSessionControl> control(aSession);
13655
13656 ComAssertRet(!control.isNull(), E_INVALIDARG);
13657
13658 /* Creating a Progress object requires the VirtualBox lock, and
13659 * thus locking it here is required by the lock order rules. */
13660 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13661
13662 if (control == mData->mSession.mDirectControl)
13663 {
13664 /* The direct session is being normally closed by the client process
13665 * ----------------------------------------------------------------- */
13666
13667 /* go to the closing state (essential for all open*Session() calls and
13668 * for #i_checkForDeath()) */
13669 Assert(mData->mSession.mState == SessionState_Locked);
13670 mData->mSession.mState = SessionState_Unlocking;
13671
13672 /* set direct control to NULL to release the remote instance */
13673 mData->mSession.mDirectControl.setNull();
13674 LogFlowThisFunc(("Direct control is set to NULL\n"));
13675
13676 if (mData->mSession.mProgress)
13677 {
13678 /* finalize the progress, someone might wait if a frontend
13679 * closes the session before powering on the VM. */
13680 mData->mSession.mProgress->notifyComplete(E_FAIL,
13681 COM_IIDOF(ISession),
13682 getComponentName(),
13683 tr("The VM session was closed before any attempt to power it on"));
13684 mData->mSession.mProgress.setNull();
13685 }
13686
13687 /* Create the progress object the client will use to wait until
13688 * #i_checkForDeath() is called to uninitialize this session object after
13689 * it releases the IPC semaphore.
13690 * Note! Because we're "reusing" mProgress here, this must be a proxy
13691 * object just like for LaunchVMProcess. */
13692 Assert(mData->mSession.mProgress.isNull());
13693 ComObjPtr<ProgressProxy> progress;
13694 progress.createObject();
13695 ComPtr<IUnknown> pPeer(mPeer);
13696 progress->init(mParent, pPeer,
13697 Bstr(tr("Closing session")).raw(),
13698 FALSE /* aCancelable */);
13699 progress.queryInterfaceTo(aProgress.asOutParam());
13700 mData->mSession.mProgress = progress;
13701 }
13702 else
13703 {
13704 /* the remote session is being normally closed */
13705 bool found = false;
13706 for (Data::Session::RemoteControlList::iterator
13707 it = mData->mSession.mRemoteControls.begin();
13708 it != mData->mSession.mRemoteControls.end();
13709 ++it)
13710 {
13711 if (control == *it)
13712 {
13713 found = true;
13714 // This MUST be erase(it), not remove(*it) as the latter
13715 // triggers a very nasty use after free due to the place where
13716 // the value "lives".
13717 mData->mSession.mRemoteControls.erase(it);
13718 break;
13719 }
13720 }
13721 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13722 E_INVALIDARG);
13723 }
13724
13725 /* signal the client watcher thread, because the client is going away */
13726 mParent->i_updateClientWatcher();
13727
13728 LogFlowThisFuncLeave();
13729 return S_OK;
13730}
13731
13732HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13733 std::vector<com::Utf8Str> &aValues,
13734 std::vector<LONG64> &aTimestamps,
13735 std::vector<com::Utf8Str> &aFlags)
13736{
13737 LogFlowThisFunc(("\n"));
13738
13739#ifdef VBOX_WITH_GUEST_PROPS
13740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13741
13742 size_t cEntries = mHWData->mGuestProperties.size();
13743 aNames.resize(cEntries);
13744 aValues.resize(cEntries);
13745 aTimestamps.resize(cEntries);
13746 aFlags.resize(cEntries);
13747
13748 size_t i = 0;
13749 for (HWData::GuestPropertyMap::const_iterator
13750 it = mHWData->mGuestProperties.begin();
13751 it != mHWData->mGuestProperties.end();
13752 ++it, ++i)
13753 {
13754 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13755 aNames[i] = it->first;
13756 aValues[i] = it->second.strValue;
13757 aTimestamps[i] = it->second.mTimestamp;
13758
13759 /* If it is NULL, keep it NULL. */
13760 if (it->second.mFlags)
13761 {
13762 GuestPropWriteFlags(it->second.mFlags, szFlags);
13763 aFlags[i] = szFlags;
13764 }
13765 else
13766 aFlags[i] = "";
13767 }
13768 return S_OK;
13769#else
13770 ReturnComNotImplemented();
13771#endif
13772}
13773
13774HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13775 const com::Utf8Str &aValue,
13776 LONG64 aTimestamp,
13777 const com::Utf8Str &aFlags)
13778{
13779 LogFlowThisFunc(("\n"));
13780
13781#ifdef VBOX_WITH_GUEST_PROPS
13782 try
13783 {
13784 /*
13785 * Convert input up front.
13786 */
13787 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13788 if (aFlags.length())
13789 {
13790 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13791 AssertRCReturn(vrc, E_INVALIDARG);
13792 }
13793
13794 /*
13795 * Now grab the object lock, validate the state and do the update.
13796 */
13797
13798 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13799
13800 if (!Global::IsOnline(mData->mMachineState))
13801 {
13802 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13803 VBOX_E_INVALID_VM_STATE);
13804 }
13805
13806 i_setModified(IsModified_MachineData);
13807 mHWData.backup();
13808
13809 bool fDelete = !aValue.length();
13810 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13811 if (it != mHWData->mGuestProperties.end())
13812 {
13813 if (!fDelete)
13814 {
13815 it->second.strValue = aValue;
13816 it->second.mTimestamp = aTimestamp;
13817 it->second.mFlags = fFlags;
13818 }
13819 else
13820 mHWData->mGuestProperties.erase(it);
13821
13822 mData->mGuestPropertiesModified = TRUE;
13823 }
13824 else if (!fDelete)
13825 {
13826 HWData::GuestProperty prop;
13827 prop.strValue = aValue;
13828 prop.mTimestamp = aTimestamp;
13829 prop.mFlags = fFlags;
13830
13831 mHWData->mGuestProperties[aName] = prop;
13832 mData->mGuestPropertiesModified = TRUE;
13833 }
13834
13835 alock.release();
13836
13837 mParent->i_onGuestPropertyChange(mData->mUuid,
13838 Bstr(aName).raw(),
13839 Bstr(aValue).raw(),
13840 Bstr(aFlags).raw());
13841 }
13842 catch (...)
13843 {
13844 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13845 }
13846 return S_OK;
13847#else
13848 ReturnComNotImplemented();
13849#endif
13850}
13851
13852
13853HRESULT SessionMachine::lockMedia()
13854{
13855 AutoMultiWriteLock2 alock(this->lockHandle(),
13856 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13857
13858 AssertReturn( mData->mMachineState == MachineState_Starting
13859 || mData->mMachineState == MachineState_Restoring
13860 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13861
13862 clearError();
13863 alock.release();
13864 return i_lockMedia();
13865}
13866
13867HRESULT SessionMachine::unlockMedia()
13868{
13869 HRESULT hrc = i_unlockMedia();
13870 return hrc;
13871}
13872
13873HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13874 ComPtr<IMediumAttachment> &aNewAttachment)
13875{
13876 // request the host lock first, since might be calling Host methods for getting host drives;
13877 // next, protect the media tree all the while we're in here, as well as our member variables
13878 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13879 this->lockHandle(),
13880 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13881
13882 IMediumAttachment *iAttach = aAttachment;
13883 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13884
13885 Utf8Str ctrlName;
13886 LONG lPort;
13887 LONG lDevice;
13888 bool fTempEject;
13889 {
13890 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13891
13892 /* Need to query the details first, as the IMediumAttachment reference
13893 * might be to the original settings, which we are going to change. */
13894 ctrlName = pAttach->i_getControllerName();
13895 lPort = pAttach->i_getPort();
13896 lDevice = pAttach->i_getDevice();
13897 fTempEject = pAttach->i_getTempEject();
13898 }
13899
13900 if (!fTempEject)
13901 {
13902 /* Remember previously mounted medium. The medium before taking the
13903 * backup is not necessarily the same thing. */
13904 ComObjPtr<Medium> oldmedium;
13905 oldmedium = pAttach->i_getMedium();
13906
13907 i_setModified(IsModified_Storage);
13908 mMediumAttachments.backup();
13909
13910 // The backup operation makes the pAttach reference point to the
13911 // old settings. Re-get the correct reference.
13912 pAttach = i_findAttachment(*mMediumAttachments.data(),
13913 ctrlName,
13914 lPort,
13915 lDevice);
13916
13917 {
13918 AutoCaller autoAttachCaller(this);
13919 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13920
13921 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13922 if (!oldmedium.isNull())
13923 oldmedium->i_removeBackReference(mData->mUuid);
13924
13925 pAttach->i_updateMedium(NULL);
13926 pAttach->i_updateEjected();
13927 }
13928
13929 i_setModified(IsModified_Storage);
13930 }
13931 else
13932 {
13933 {
13934 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13935 pAttach->i_updateEjected();
13936 }
13937 }
13938
13939 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13940
13941 return S_OK;
13942}
13943
13944HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13945 com::Utf8Str &aResult)
13946{
13947 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13948
13949 HRESULT hr = S_OK;
13950
13951 if (!mAuthLibCtx.hAuthLibrary)
13952 {
13953 /* Load the external authentication library. */
13954 Bstr authLibrary;
13955 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13956
13957 Utf8Str filename = authLibrary;
13958
13959 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13960 if (RT_FAILURE(vrc))
13961 hr = setErrorBoth(E_FAIL, vrc,
13962 tr("Could not load the external authentication library '%s' (%Rrc)"),
13963 filename.c_str(), vrc);
13964 }
13965
13966 /* The auth library might need the machine lock. */
13967 alock.release();
13968
13969 if (FAILED(hr))
13970 return hr;
13971
13972 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13973 {
13974 enum VRDEAuthParams
13975 {
13976 parmUuid = 1,
13977 parmGuestJudgement,
13978 parmUser,
13979 parmPassword,
13980 parmDomain,
13981 parmClientId
13982 };
13983
13984 AuthResult result = AuthResultAccessDenied;
13985
13986 Guid uuid(aAuthParams[parmUuid]);
13987 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13988 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13989
13990 result = AuthLibAuthenticate(&mAuthLibCtx,
13991 uuid.raw(), guestJudgement,
13992 aAuthParams[parmUser].c_str(),
13993 aAuthParams[parmPassword].c_str(),
13994 aAuthParams[parmDomain].c_str(),
13995 u32ClientId);
13996
13997 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13998 size_t cbPassword = aAuthParams[parmPassword].length();
13999 if (cbPassword)
14000 {
14001 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
14002 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
14003 }
14004
14005 if (result == AuthResultAccessGranted)
14006 aResult = "granted";
14007 else
14008 aResult = "denied";
14009
14010 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
14011 aAuthParams[parmUser].c_str(), aResult.c_str()));
14012 }
14013 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
14014 {
14015 enum VRDEAuthDisconnectParams
14016 {
14017 parmUuid = 1,
14018 parmClientId
14019 };
14020
14021 Guid uuid(aAuthParams[parmUuid]);
14022 uint32_t u32ClientId = 0;
14023 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
14024 }
14025 else
14026 {
14027 hr = E_INVALIDARG;
14028 }
14029
14030 return hr;
14031}
14032
14033// public methods only for internal purposes
14034/////////////////////////////////////////////////////////////////////////////
14035
14036#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
14037/**
14038 * Called from the client watcher thread to check for expected or unexpected
14039 * death of the client process that has a direct session to this machine.
14040 *
14041 * On Win32 and on OS/2, this method is called only when we've got the
14042 * mutex (i.e. the client has either died or terminated normally) so it always
14043 * returns @c true (the client is terminated, the session machine is
14044 * uninitialized).
14045 *
14046 * On other platforms, the method returns @c true if the client process has
14047 * terminated normally or abnormally and the session machine was uninitialized,
14048 * and @c false if the client process is still alive.
14049 *
14050 * @note Locks this object for writing.
14051 */
14052bool SessionMachine::i_checkForDeath()
14053{
14054 Uninit::Reason reason;
14055 bool terminated = false;
14056
14057 /* Enclose autoCaller with a block because calling uninit() from under it
14058 * will deadlock. */
14059 {
14060 AutoCaller autoCaller(this);
14061 if (!autoCaller.isOk())
14062 {
14063 /* return true if not ready, to cause the client watcher to exclude
14064 * the corresponding session from watching */
14065 LogFlowThisFunc(("Already uninitialized!\n"));
14066 return true;
14067 }
14068
14069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14070
14071 /* Determine the reason of death: if the session state is Closing here,
14072 * everything is fine. Otherwise it means that the client did not call
14073 * OnSessionEnd() before it released the IPC semaphore. This may happen
14074 * either because the client process has abnormally terminated, or
14075 * because it simply forgot to call ISession::Close() before exiting. We
14076 * threat the latter also as an abnormal termination (see
14077 * Session::uninit() for details). */
14078 reason = mData->mSession.mState == SessionState_Unlocking ?
14079 Uninit::Normal :
14080 Uninit::Abnormal;
14081
14082 if (mClientToken)
14083 terminated = mClientToken->release();
14084 } /* AutoCaller block */
14085
14086 if (terminated)
14087 uninit(reason);
14088
14089 return terminated;
14090}
14091
14092void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
14093{
14094 LogFlowThisFunc(("\n"));
14095
14096 strTokenId.setNull();
14097
14098 AutoCaller autoCaller(this);
14099 AssertComRCReturnVoid(autoCaller.rc());
14100
14101 Assert(mClientToken);
14102 if (mClientToken)
14103 mClientToken->getId(strTokenId);
14104}
14105#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14106IToken *SessionMachine::i_getToken()
14107{
14108 LogFlowThisFunc(("\n"));
14109
14110 AutoCaller autoCaller(this);
14111 AssertComRCReturn(autoCaller.rc(), NULL);
14112
14113 Assert(mClientToken);
14114 if (mClientToken)
14115 return mClientToken->getToken();
14116 else
14117 return NULL;
14118}
14119#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
14120
14121Machine::ClientToken *SessionMachine::i_getClientToken()
14122{
14123 LogFlowThisFunc(("\n"));
14124
14125 AutoCaller autoCaller(this);
14126 AssertComRCReturn(autoCaller.rc(), NULL);
14127
14128 return mClientToken;
14129}
14130
14131
14132/**
14133 * @note Locks this object for reading.
14134 */
14135HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
14136{
14137 LogFlowThisFunc(("\n"));
14138
14139 AutoCaller autoCaller(this);
14140 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14141
14142 ComPtr<IInternalSessionControl> directControl;
14143 {
14144 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14145 if (mData->mSession.mLockType == LockType_VM)
14146 directControl = mData->mSession.mDirectControl;
14147 }
14148
14149 /* ignore notifications sent after #OnSessionEnd() is called */
14150 if (!directControl)
14151 return S_OK;
14152
14153 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
14154}
14155
14156/**
14157 * @note Locks this object for reading.
14158 */
14159HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
14160 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
14161 IN_BSTR aGuestIp, LONG aGuestPort)
14162{
14163 LogFlowThisFunc(("\n"));
14164
14165 AutoCaller autoCaller(this);
14166 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14167
14168 ComPtr<IInternalSessionControl> directControl;
14169 {
14170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14171 if (mData->mSession.mLockType == LockType_VM)
14172 directControl = mData->mSession.mDirectControl;
14173 }
14174
14175 /* ignore notifications sent after #OnSessionEnd() is called */
14176 if (!directControl)
14177 return S_OK;
14178 /*
14179 * instead acting like callback we ask IVirtualBox deliver corresponding event
14180 */
14181
14182 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
14183 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
14184 return S_OK;
14185}
14186
14187/**
14188 * @note Locks this object for reading.
14189 */
14190HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
14191{
14192 LogFlowThisFunc(("\n"));
14193
14194 AutoCaller autoCaller(this);
14195 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14196
14197 ComPtr<IInternalSessionControl> directControl;
14198 {
14199 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14200 if (mData->mSession.mLockType == LockType_VM)
14201 directControl = mData->mSession.mDirectControl;
14202 }
14203
14204 /* ignore notifications sent after #OnSessionEnd() is called */
14205 if (!directControl)
14206 return S_OK;
14207
14208 return directControl->OnAudioAdapterChange(audioAdapter);
14209}
14210
14211/**
14212 * @note Locks this object for reading.
14213 */
14214HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
14215{
14216 LogFlowThisFunc(("\n"));
14217
14218 AutoCaller autoCaller(this);
14219 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14220
14221 ComPtr<IInternalSessionControl> directControl;
14222 {
14223 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14224 if (mData->mSession.mLockType == LockType_VM)
14225 directControl = mData->mSession.mDirectControl;
14226 }
14227
14228 /* ignore notifications sent after #OnSessionEnd() is called */
14229 if (!directControl)
14230 return S_OK;
14231
14232 return directControl->OnSerialPortChange(serialPort);
14233}
14234
14235/**
14236 * @note Locks this object for reading.
14237 */
14238HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14239{
14240 LogFlowThisFunc(("\n"));
14241
14242 AutoCaller autoCaller(this);
14243 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14244
14245 ComPtr<IInternalSessionControl> directControl;
14246 {
14247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14248 if (mData->mSession.mLockType == LockType_VM)
14249 directControl = mData->mSession.mDirectControl;
14250 }
14251
14252 /* ignore notifications sent after #OnSessionEnd() is called */
14253 if (!directControl)
14254 return S_OK;
14255
14256 return directControl->OnParallelPortChange(parallelPort);
14257}
14258
14259/**
14260 * @note Locks this object for reading.
14261 */
14262HRESULT SessionMachine::i_onStorageControllerChange()
14263{
14264 LogFlowThisFunc(("\n"));
14265
14266 AutoCaller autoCaller(this);
14267 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14268
14269 ComPtr<IInternalSessionControl> directControl;
14270 {
14271 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14272 if (mData->mSession.mLockType == LockType_VM)
14273 directControl = mData->mSession.mDirectControl;
14274 }
14275
14276 /* ignore notifications sent after #OnSessionEnd() is called */
14277 if (!directControl)
14278 return S_OK;
14279
14280 return directControl->OnStorageControllerChange();
14281}
14282
14283/**
14284 * @note Locks this object for reading.
14285 */
14286HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14287{
14288 LogFlowThisFunc(("\n"));
14289
14290 AutoCaller autoCaller(this);
14291 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14292
14293 ComPtr<IInternalSessionControl> directControl;
14294 {
14295 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14296 if (mData->mSession.mLockType == LockType_VM)
14297 directControl = mData->mSession.mDirectControl;
14298 }
14299
14300 /* ignore notifications sent after #OnSessionEnd() is called */
14301 if (!directControl)
14302 return S_OK;
14303
14304 return directControl->OnMediumChange(aAttachment, aForce);
14305}
14306
14307/**
14308 * @note Locks this object for reading.
14309 */
14310HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14316
14317 ComPtr<IInternalSessionControl> directControl;
14318 {
14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14320 if (mData->mSession.mLockType == LockType_VM)
14321 directControl = mData->mSession.mDirectControl;
14322 }
14323
14324 /* ignore notifications sent after #OnSessionEnd() is called */
14325 if (!directControl)
14326 return S_OK;
14327
14328 return directControl->OnCPUChange(aCPU, aRemove);
14329}
14330
14331HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14332{
14333 LogFlowThisFunc(("\n"));
14334
14335 AutoCaller autoCaller(this);
14336 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14337
14338 ComPtr<IInternalSessionControl> directControl;
14339 {
14340 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14341 if (mData->mSession.mLockType == LockType_VM)
14342 directControl = mData->mSession.mDirectControl;
14343 }
14344
14345 /* ignore notifications sent after #OnSessionEnd() is called */
14346 if (!directControl)
14347 return S_OK;
14348
14349 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14350}
14351
14352/**
14353 * @note Locks this object for reading.
14354 */
14355HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14356{
14357 LogFlowThisFunc(("\n"));
14358
14359 AutoCaller autoCaller(this);
14360 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14361
14362 ComPtr<IInternalSessionControl> directControl;
14363 {
14364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14365 if (mData->mSession.mLockType == LockType_VM)
14366 directControl = mData->mSession.mDirectControl;
14367 }
14368
14369 /* ignore notifications sent after #OnSessionEnd() is called */
14370 if (!directControl)
14371 return S_OK;
14372
14373 return directControl->OnVRDEServerChange(aRestart);
14374}
14375
14376/**
14377 * @note Locks this object for reading.
14378 */
14379HRESULT SessionMachine::i_onVideoCaptureChange()
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14385
14386 ComPtr<IInternalSessionControl> directControl;
14387 {
14388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14389 if (mData->mSession.mLockType == LockType_VM)
14390 directControl = mData->mSession.mDirectControl;
14391 }
14392
14393 /* ignore notifications sent after #OnSessionEnd() is called */
14394 if (!directControl)
14395 return S_OK;
14396
14397 return directControl->OnVideoCaptureChange();
14398}
14399
14400/**
14401 * @note Locks this object for reading.
14402 */
14403HRESULT SessionMachine::i_onUSBControllerChange()
14404{
14405 LogFlowThisFunc(("\n"));
14406
14407 AutoCaller autoCaller(this);
14408 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14409
14410 ComPtr<IInternalSessionControl> directControl;
14411 {
14412 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14413 if (mData->mSession.mLockType == LockType_VM)
14414 directControl = mData->mSession.mDirectControl;
14415 }
14416
14417 /* ignore notifications sent after #OnSessionEnd() is called */
14418 if (!directControl)
14419 return S_OK;
14420
14421 return directControl->OnUSBControllerChange();
14422}
14423
14424/**
14425 * @note Locks this object for reading.
14426 */
14427HRESULT SessionMachine::i_onSharedFolderChange()
14428{
14429 LogFlowThisFunc(("\n"));
14430
14431 AutoCaller autoCaller(this);
14432 AssertComRCReturnRC(autoCaller.rc());
14433
14434 ComPtr<IInternalSessionControl> directControl;
14435 {
14436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14437 if (mData->mSession.mLockType == LockType_VM)
14438 directControl = mData->mSession.mDirectControl;
14439 }
14440
14441 /* ignore notifications sent after #OnSessionEnd() is called */
14442 if (!directControl)
14443 return S_OK;
14444
14445 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14446}
14447
14448/**
14449 * @note Locks this object for reading.
14450 */
14451HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14452{
14453 LogFlowThisFunc(("\n"));
14454
14455 AutoCaller autoCaller(this);
14456 AssertComRCReturnRC(autoCaller.rc());
14457
14458 ComPtr<IInternalSessionControl> directControl;
14459 {
14460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14461 if (mData->mSession.mLockType == LockType_VM)
14462 directControl = mData->mSession.mDirectControl;
14463 }
14464
14465 /* ignore notifications sent after #OnSessionEnd() is called */
14466 if (!directControl)
14467 return S_OK;
14468
14469 return directControl->OnClipboardModeChange(aClipboardMode);
14470}
14471
14472/**
14473 * @note Locks this object for reading.
14474 */
14475HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14476{
14477 LogFlowThisFunc(("\n"));
14478
14479 AutoCaller autoCaller(this);
14480 AssertComRCReturnRC(autoCaller.rc());
14481
14482 ComPtr<IInternalSessionControl> directControl;
14483 {
14484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14485 if (mData->mSession.mLockType == LockType_VM)
14486 directControl = mData->mSession.mDirectControl;
14487 }
14488
14489 /* ignore notifications sent after #OnSessionEnd() is called */
14490 if (!directControl)
14491 return S_OK;
14492
14493 return directControl->OnDnDModeChange(aDnDMode);
14494}
14495
14496/**
14497 * @note Locks this object for reading.
14498 */
14499HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14500{
14501 LogFlowThisFunc(("\n"));
14502
14503 AutoCaller autoCaller(this);
14504 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14505
14506 ComPtr<IInternalSessionControl> directControl;
14507 {
14508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14509 if (mData->mSession.mLockType == LockType_VM)
14510 directControl = mData->mSession.mDirectControl;
14511 }
14512
14513 /* ignore notifications sent after #OnSessionEnd() is called */
14514 if (!directControl)
14515 return S_OK;
14516
14517 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14518}
14519
14520/**
14521 * @note Locks this object for reading.
14522 */
14523HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14524{
14525 LogFlowThisFunc(("\n"));
14526
14527 AutoCaller autoCaller(this);
14528 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14529
14530 ComPtr<IInternalSessionControl> directControl;
14531 {
14532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14533 if (mData->mSession.mLockType == LockType_VM)
14534 directControl = mData->mSession.mDirectControl;
14535 }
14536
14537 /* ignore notifications sent after #OnSessionEnd() is called */
14538 if (!directControl)
14539 return S_OK;
14540
14541 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14542}
14543
14544/**
14545 * Returns @c true if this machine's USB controller reports it has a matching
14546 * filter for the given USB device and @c false otherwise.
14547 *
14548 * @note locks this object for reading.
14549 */
14550bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14551{
14552 AutoCaller autoCaller(this);
14553 /* silently return if not ready -- this method may be called after the
14554 * direct machine session has been called */
14555 if (!autoCaller.isOk())
14556 return false;
14557
14558#ifdef VBOX_WITH_USB
14559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14560
14561 switch (mData->mMachineState)
14562 {
14563 case MachineState_Starting:
14564 case MachineState_Restoring:
14565 case MachineState_TeleportingIn:
14566 case MachineState_Paused:
14567 case MachineState_Running:
14568 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14569 * elsewhere... */
14570 alock.release();
14571 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14572 default: break;
14573 }
14574#else
14575 NOREF(aDevice);
14576 NOREF(aMaskedIfs);
14577#endif
14578 return false;
14579}
14580
14581/**
14582 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14583 */
14584HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14585 IVirtualBoxErrorInfo *aError,
14586 ULONG aMaskedIfs,
14587 const com::Utf8Str &aCaptureFilename)
14588{
14589 LogFlowThisFunc(("\n"));
14590
14591 AutoCaller autoCaller(this);
14592
14593 /* This notification may happen after the machine object has been
14594 * uninitialized (the session was closed), so don't assert. */
14595 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14596
14597 ComPtr<IInternalSessionControl> directControl;
14598 {
14599 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14600 if (mData->mSession.mLockType == LockType_VM)
14601 directControl = mData->mSession.mDirectControl;
14602 }
14603
14604 /* fail on notifications sent after #OnSessionEnd() is called, it is
14605 * expected by the caller */
14606 if (!directControl)
14607 return E_FAIL;
14608
14609 /* No locks should be held at this point. */
14610 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14611 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14612
14613 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14614}
14615
14616/**
14617 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14618 */
14619HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14620 IVirtualBoxErrorInfo *aError)
14621{
14622 LogFlowThisFunc(("\n"));
14623
14624 AutoCaller autoCaller(this);
14625
14626 /* This notification may happen after the machine object has been
14627 * uninitialized (the session was closed), so don't assert. */
14628 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14629
14630 ComPtr<IInternalSessionControl> directControl;
14631 {
14632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14633 if (mData->mSession.mLockType == LockType_VM)
14634 directControl = mData->mSession.mDirectControl;
14635 }
14636
14637 /* fail on notifications sent after #OnSessionEnd() is called, it is
14638 * expected by the caller */
14639 if (!directControl)
14640 return E_FAIL;
14641
14642 /* No locks should be held at this point. */
14643 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14644 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14645
14646 return directControl->OnUSBDeviceDetach(aId, aError);
14647}
14648
14649// protected methods
14650/////////////////////////////////////////////////////////////////////////////
14651
14652/**
14653 * Deletes the given file if it is no longer in use by either the current machine state
14654 * (if the machine is "saved") or any of the machine's snapshots.
14655 *
14656 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14657 * but is different for each SnapshotMachine. When calling this, the order of calling this
14658 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14659 * is therefore critical. I know, it's all rather messy.
14660 *
14661 * @param strStateFile
14662 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14663 * the test for whether the saved state file is in use.
14664 */
14665void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14666 Snapshot *pSnapshotToIgnore)
14667{
14668 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14669 if ( (strStateFile.isNotEmpty())
14670 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14671 )
14672 // ... and it must also not be shared with other snapshots
14673 if ( !mData->mFirstSnapshot
14674 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14675 // this checks the SnapshotMachine's state file paths
14676 )
14677 RTFileDelete(strStateFile.c_str());
14678}
14679
14680/**
14681 * Locks the attached media.
14682 *
14683 * All attached hard disks are locked for writing and DVD/floppy are locked for
14684 * reading. Parents of attached hard disks (if any) are locked for reading.
14685 *
14686 * This method also performs accessibility check of all media it locks: if some
14687 * media is inaccessible, the method will return a failure and a bunch of
14688 * extended error info objects per each inaccessible medium.
14689 *
14690 * Note that this method is atomic: if it returns a success, all media are
14691 * locked as described above; on failure no media is locked at all (all
14692 * succeeded individual locks will be undone).
14693 *
14694 * The caller is responsible for doing the necessary state sanity checks.
14695 *
14696 * The locks made by this method must be undone by calling #unlockMedia() when
14697 * no more needed.
14698 */
14699HRESULT SessionMachine::i_lockMedia()
14700{
14701 AutoCaller autoCaller(this);
14702 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14703
14704 AutoMultiWriteLock2 alock(this->lockHandle(),
14705 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14706
14707 /* bail out if trying to lock things with already set up locking */
14708 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14709
14710 MultiResult mrc(S_OK);
14711
14712 /* Collect locking information for all medium objects attached to the VM. */
14713 for (MediumAttachmentList::const_iterator
14714 it = mMediumAttachments->begin();
14715 it != mMediumAttachments->end();
14716 ++it)
14717 {
14718 MediumAttachment *pAtt = *it;
14719 DeviceType_T devType = pAtt->i_getType();
14720 Medium *pMedium = pAtt->i_getMedium();
14721
14722 MediumLockList *pMediumLockList(new MediumLockList());
14723 // There can be attachments without a medium (floppy/dvd), and thus
14724 // it's impossible to create a medium lock list. It still makes sense
14725 // to have the empty medium lock list in the map in case a medium is
14726 // attached later.
14727 if (pMedium != NULL)
14728 {
14729 MediumType_T mediumType = pMedium->i_getType();
14730 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14731 || mediumType == MediumType_Shareable;
14732 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14733
14734 alock.release();
14735 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14736 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14737 false /* fMediumLockWriteAll */,
14738 NULL,
14739 *pMediumLockList);
14740 alock.acquire();
14741 if (FAILED(mrc))
14742 {
14743 delete pMediumLockList;
14744 mData->mSession.mLockedMedia.Clear();
14745 break;
14746 }
14747 }
14748
14749 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14750 if (FAILED(rc))
14751 {
14752 mData->mSession.mLockedMedia.Clear();
14753 mrc = setError(rc,
14754 tr("Collecting locking information for all attached media failed"));
14755 break;
14756 }
14757 }
14758
14759 if (SUCCEEDED(mrc))
14760 {
14761 /* Now lock all media. If this fails, nothing is locked. */
14762 alock.release();
14763 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14764 alock.acquire();
14765 if (FAILED(rc))
14766 {
14767 mrc = setError(rc,
14768 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14769 }
14770 }
14771
14772 return mrc;
14773}
14774
14775/**
14776 * Undoes the locks made by by #lockMedia().
14777 */
14778HRESULT SessionMachine::i_unlockMedia()
14779{
14780 AutoCaller autoCaller(this);
14781 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14782
14783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14784
14785 /* we may be holding important error info on the current thread;
14786 * preserve it */
14787 ErrorInfoKeeper eik;
14788
14789 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14790 AssertComRC(rc);
14791 return rc;
14792}
14793
14794/**
14795 * Helper to change the machine state (reimplementation).
14796 *
14797 * @note Locks this object for writing.
14798 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14799 * it can cause crashes in random places due to unexpectedly committing
14800 * the current settings. The caller is responsible for that. The call
14801 * to saveStateSettings is fine, because this method does not commit.
14802 */
14803HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14804{
14805 LogFlowThisFuncEnter();
14806 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14807
14808 AutoCaller autoCaller(this);
14809 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14810
14811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14812
14813 MachineState_T oldMachineState = mData->mMachineState;
14814
14815 AssertMsgReturn(oldMachineState != aMachineState,
14816 ("oldMachineState=%s, aMachineState=%s\n",
14817 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14818 E_FAIL);
14819
14820 HRESULT rc = S_OK;
14821
14822 int stsFlags = 0;
14823 bool deleteSavedState = false;
14824
14825 /* detect some state transitions */
14826
14827 if ( ( oldMachineState == MachineState_Saved
14828 && aMachineState == MachineState_Restoring)
14829 || ( ( oldMachineState == MachineState_PoweredOff
14830 || oldMachineState == MachineState_Teleported
14831 || oldMachineState == MachineState_Aborted
14832 )
14833 && ( aMachineState == MachineState_TeleportingIn
14834 || aMachineState == MachineState_Starting
14835 )
14836 )
14837 )
14838 {
14839 /* The EMT thread is about to start */
14840
14841 /* Nothing to do here for now... */
14842
14843 /// @todo NEWMEDIA don't let mDVDDrive and other children
14844 /// change anything when in the Starting/Restoring state
14845 }
14846 else if ( ( oldMachineState == MachineState_Running
14847 || oldMachineState == MachineState_Paused
14848 || oldMachineState == MachineState_Teleporting
14849 || oldMachineState == MachineState_OnlineSnapshotting
14850 || oldMachineState == MachineState_LiveSnapshotting
14851 || oldMachineState == MachineState_Stuck
14852 || oldMachineState == MachineState_Starting
14853 || oldMachineState == MachineState_Stopping
14854 || oldMachineState == MachineState_Saving
14855 || oldMachineState == MachineState_Restoring
14856 || oldMachineState == MachineState_TeleportingPausedVM
14857 || oldMachineState == MachineState_TeleportingIn
14858 )
14859 && ( aMachineState == MachineState_PoweredOff
14860 || aMachineState == MachineState_Saved
14861 || aMachineState == MachineState_Teleported
14862 || aMachineState == MachineState_Aborted
14863 )
14864 )
14865 {
14866 /* The EMT thread has just stopped, unlock attached media. Note that as
14867 * opposed to locking that is done from Console, we do unlocking here
14868 * because the VM process may have aborted before having a chance to
14869 * properly unlock all media it locked. */
14870
14871 unlockMedia();
14872 }
14873
14874 if (oldMachineState == MachineState_Restoring)
14875 {
14876 if (aMachineState != MachineState_Saved)
14877 {
14878 /*
14879 * delete the saved state file once the machine has finished
14880 * restoring from it (note that Console sets the state from
14881 * Restoring to Saved if the VM couldn't restore successfully,
14882 * to give the user an ability to fix an error and retry --
14883 * we keep the saved state file in this case)
14884 */
14885 deleteSavedState = true;
14886 }
14887 }
14888 else if ( oldMachineState == MachineState_Saved
14889 && ( aMachineState == MachineState_PoweredOff
14890 || aMachineState == MachineState_Aborted
14891 || aMachineState == MachineState_Teleported
14892 )
14893 )
14894 {
14895 /*
14896 * delete the saved state after SessionMachine::ForgetSavedState() is called
14897 * or if the VM process (owning a direct VM session) crashed while the
14898 * VM was Saved
14899 */
14900
14901 /// @todo (dmik)
14902 // Not sure that deleting the saved state file just because of the
14903 // client death before it attempted to restore the VM is a good
14904 // thing. But when it crashes we need to go to the Aborted state
14905 // which cannot have the saved state file associated... The only
14906 // way to fix this is to make the Aborted condition not a VM state
14907 // but a bool flag: i.e., when a crash occurs, set it to true and
14908 // change the state to PoweredOff or Saved depending on the
14909 // saved state presence.
14910
14911 deleteSavedState = true;
14912 mData->mCurrentStateModified = TRUE;
14913 stsFlags |= SaveSTS_CurStateModified;
14914 }
14915
14916 if ( aMachineState == MachineState_Starting
14917 || aMachineState == MachineState_Restoring
14918 || aMachineState == MachineState_TeleportingIn
14919 )
14920 {
14921 /* set the current state modified flag to indicate that the current
14922 * state is no more identical to the state in the
14923 * current snapshot */
14924 if (!mData->mCurrentSnapshot.isNull())
14925 {
14926 mData->mCurrentStateModified = TRUE;
14927 stsFlags |= SaveSTS_CurStateModified;
14928 }
14929 }
14930
14931 if (deleteSavedState)
14932 {
14933 if (mRemoveSavedState)
14934 {
14935 Assert(!mSSData->strStateFilePath.isEmpty());
14936
14937 // it is safe to delete the saved state file if ...
14938 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14939 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14940 // ... none of the snapshots share the saved state file
14941 )
14942 RTFileDelete(mSSData->strStateFilePath.c_str());
14943 }
14944
14945 mSSData->strStateFilePath.setNull();
14946 stsFlags |= SaveSTS_StateFilePath;
14947 }
14948
14949 /* redirect to the underlying peer machine */
14950 mPeer->i_setMachineState(aMachineState);
14951
14952 if ( oldMachineState != MachineState_RestoringSnapshot
14953 && ( aMachineState == MachineState_PoweredOff
14954 || aMachineState == MachineState_Teleported
14955 || aMachineState == MachineState_Aborted
14956 || aMachineState == MachineState_Saved))
14957 {
14958 /* the machine has stopped execution
14959 * (or the saved state file was adopted) */
14960 stsFlags |= SaveSTS_StateTimeStamp;
14961 }
14962
14963 if ( ( oldMachineState == MachineState_PoweredOff
14964 || oldMachineState == MachineState_Aborted
14965 || oldMachineState == MachineState_Teleported
14966 )
14967 && aMachineState == MachineState_Saved)
14968 {
14969 /* the saved state file was adopted */
14970 Assert(!mSSData->strStateFilePath.isEmpty());
14971 stsFlags |= SaveSTS_StateFilePath;
14972 }
14973
14974#ifdef VBOX_WITH_GUEST_PROPS
14975 if ( aMachineState == MachineState_PoweredOff
14976 || aMachineState == MachineState_Aborted
14977 || aMachineState == MachineState_Teleported)
14978 {
14979 /* Make sure any transient guest properties get removed from the
14980 * property store on shutdown. */
14981 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14982
14983 /* remove it from the settings representation */
14984 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14985 for (settings::GuestPropertiesList::iterator
14986 it = llGuestProperties.begin();
14987 it != llGuestProperties.end();
14988 /*nothing*/)
14989 {
14990 const settings::GuestProperty &prop = *it;
14991 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14992 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14993 {
14994 it = llGuestProperties.erase(it);
14995 fNeedsSaving = true;
14996 }
14997 else
14998 {
14999 ++it;
15000 }
15001 }
15002
15003 /* Additionally remove it from the HWData representation. Required to
15004 * keep everything in sync, as this is what the API keeps using. */
15005 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
15006 for (HWData::GuestPropertyMap::iterator
15007 it = llHWGuestProperties.begin();
15008 it != llHWGuestProperties.end();
15009 /*nothing*/)
15010 {
15011 uint32_t fFlags = it->second.mFlags;
15012 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
15013 {
15014 /* iterator where we need to continue after the erase call
15015 * (C++03 is a fact still, and it doesn't return the iterator
15016 * which would allow continuing) */
15017 HWData::GuestPropertyMap::iterator it2 = it;
15018 ++it2;
15019 llHWGuestProperties.erase(it);
15020 it = it2;
15021 fNeedsSaving = true;
15022 }
15023 else
15024 {
15025 ++it;
15026 }
15027 }
15028
15029 if (fNeedsSaving)
15030 {
15031 mData->mCurrentStateModified = TRUE;
15032 stsFlags |= SaveSTS_CurStateModified;
15033 }
15034 }
15035#endif /* VBOX_WITH_GUEST_PROPS */
15036
15037 rc = i_saveStateSettings(stsFlags);
15038
15039 if ( ( oldMachineState != MachineState_PoweredOff
15040 && oldMachineState != MachineState_Aborted
15041 && oldMachineState != MachineState_Teleported
15042 )
15043 && ( aMachineState == MachineState_PoweredOff
15044 || aMachineState == MachineState_Aborted
15045 || aMachineState == MachineState_Teleported
15046 )
15047 )
15048 {
15049 /* we've been shut down for any reason */
15050 /* no special action so far */
15051 }
15052
15053 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
15054 LogFlowThisFuncLeave();
15055 return rc;
15056}
15057
15058/**
15059 * Sends the current machine state value to the VM process.
15060 *
15061 * @note Locks this object for reading, then calls a client process.
15062 */
15063HRESULT SessionMachine::i_updateMachineStateOnClient()
15064{
15065 AutoCaller autoCaller(this);
15066 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
15067
15068 ComPtr<IInternalSessionControl> directControl;
15069 {
15070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
15071 AssertReturn(!!mData, E_FAIL);
15072 if (mData->mSession.mLockType == LockType_VM)
15073 directControl = mData->mSession.mDirectControl;
15074
15075 /* directControl may be already set to NULL here in #OnSessionEnd()
15076 * called too early by the direct session process while there is still
15077 * some operation (like deleting the snapshot) in progress. The client
15078 * process in this case is waiting inside Session::close() for the
15079 * "end session" process object to complete, while #uninit() called by
15080 * #i_checkForDeath() on the Watcher thread is waiting for the pending
15081 * operation to complete. For now, we accept this inconsistent behavior
15082 * and simply do nothing here. */
15083
15084 if (mData->mSession.mState == SessionState_Unlocking)
15085 return S_OK;
15086 }
15087
15088 /* ignore notifications sent after #OnSessionEnd() is called */
15089 if (!directControl)
15090 return S_OK;
15091
15092 return directControl->UpdateMachineState(mData->mMachineState);
15093}
15094
15095
15096/*static*/
15097HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
15098{
15099 va_list args;
15100 va_start(args, pcszMsg);
15101 HRESULT rc = setErrorInternal(aResultCode,
15102 getStaticClassIID(),
15103 getStaticComponentName(),
15104 Utf8Str(pcszMsg, args),
15105 false /* aWarning */,
15106 true /* aLogIt */);
15107 va_end(args);
15108 return rc;
15109}
15110
15111
15112HRESULT Machine::updateState(MachineState_T aState)
15113{
15114 NOREF(aState);
15115 ReturnComNotImplemented();
15116}
15117
15118HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
15119{
15120 NOREF(aProgress);
15121 ReturnComNotImplemented();
15122}
15123
15124HRESULT Machine::endPowerUp(LONG aResult)
15125{
15126 NOREF(aResult);
15127 ReturnComNotImplemented();
15128}
15129
15130HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
15131{
15132 NOREF(aProgress);
15133 ReturnComNotImplemented();
15134}
15135
15136HRESULT Machine::endPoweringDown(LONG aResult,
15137 const com::Utf8Str &aErrMsg)
15138{
15139 NOREF(aResult);
15140 NOREF(aErrMsg);
15141 ReturnComNotImplemented();
15142}
15143
15144HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
15145 BOOL *aMatched,
15146 ULONG *aMaskedInterfaces)
15147{
15148 NOREF(aDevice);
15149 NOREF(aMatched);
15150 NOREF(aMaskedInterfaces);
15151 ReturnComNotImplemented();
15152
15153}
15154
15155HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
15156{
15157 NOREF(aId); NOREF(aCaptureFilename);
15158 ReturnComNotImplemented();
15159}
15160
15161HRESULT Machine::detachUSBDevice(const com::Guid &aId,
15162 BOOL aDone)
15163{
15164 NOREF(aId);
15165 NOREF(aDone);
15166 ReturnComNotImplemented();
15167}
15168
15169HRESULT Machine::autoCaptureUSBDevices()
15170{
15171 ReturnComNotImplemented();
15172}
15173
15174HRESULT Machine::detachAllUSBDevices(BOOL aDone)
15175{
15176 NOREF(aDone);
15177 ReturnComNotImplemented();
15178}
15179
15180HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
15181 ComPtr<IProgress> &aProgress)
15182{
15183 NOREF(aSession);
15184 NOREF(aProgress);
15185 ReturnComNotImplemented();
15186}
15187
15188HRESULT Machine::finishOnlineMergeMedium()
15189{
15190 ReturnComNotImplemented();
15191}
15192
15193HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15194 std::vector<com::Utf8Str> &aValues,
15195 std::vector<LONG64> &aTimestamps,
15196 std::vector<com::Utf8Str> &aFlags)
15197{
15198 NOREF(aNames);
15199 NOREF(aValues);
15200 NOREF(aTimestamps);
15201 NOREF(aFlags);
15202 ReturnComNotImplemented();
15203}
15204
15205HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15206 const com::Utf8Str &aValue,
15207 LONG64 aTimestamp,
15208 const com::Utf8Str &aFlags)
15209{
15210 NOREF(aName);
15211 NOREF(aValue);
15212 NOREF(aTimestamp);
15213 NOREF(aFlags);
15214 ReturnComNotImplemented();
15215}
15216
15217HRESULT Machine::lockMedia()
15218{
15219 ReturnComNotImplemented();
15220}
15221
15222HRESULT Machine::unlockMedia()
15223{
15224 ReturnComNotImplemented();
15225}
15226
15227HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15228 ComPtr<IMediumAttachment> &aNewAttachment)
15229{
15230 NOREF(aAttachment);
15231 NOREF(aNewAttachment);
15232 ReturnComNotImplemented();
15233}
15234
15235HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15236 ULONG aCpuUser,
15237 ULONG aCpuKernel,
15238 ULONG aCpuIdle,
15239 ULONG aMemTotal,
15240 ULONG aMemFree,
15241 ULONG aMemBalloon,
15242 ULONG aMemShared,
15243 ULONG aMemCache,
15244 ULONG aPagedTotal,
15245 ULONG aMemAllocTotal,
15246 ULONG aMemFreeTotal,
15247 ULONG aMemBalloonTotal,
15248 ULONG aMemSharedTotal,
15249 ULONG aVmNetRx,
15250 ULONG aVmNetTx)
15251{
15252 NOREF(aValidStats);
15253 NOREF(aCpuUser);
15254 NOREF(aCpuKernel);
15255 NOREF(aCpuIdle);
15256 NOREF(aMemTotal);
15257 NOREF(aMemFree);
15258 NOREF(aMemBalloon);
15259 NOREF(aMemShared);
15260 NOREF(aMemCache);
15261 NOREF(aPagedTotal);
15262 NOREF(aMemAllocTotal);
15263 NOREF(aMemFreeTotal);
15264 NOREF(aMemBalloonTotal);
15265 NOREF(aMemSharedTotal);
15266 NOREF(aVmNetRx);
15267 NOREF(aVmNetTx);
15268 ReturnComNotImplemented();
15269}
15270
15271HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15272 com::Utf8Str &aResult)
15273{
15274 NOREF(aAuthParams);
15275 NOREF(aResult);
15276 ReturnComNotImplemented();
15277}
15278
15279HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15280{
15281 NOREF(aFlags);
15282 ReturnComNotImplemented();
15283}
15284
15285/* This isn't handled entirely by the wrapper generator yet. */
15286#ifdef VBOX_WITH_XPCOM
15287NS_DECL_CLASSINFO(SessionMachine)
15288NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15289
15290NS_DECL_CLASSINFO(SnapshotMachine)
15291NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15292#endif
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette