VirtualBox

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

Last change on this file since 76900 was 76818, checked in by vboxsync, 6 years ago

Config.kmk, HostDrivers/Support, Main, FE/Qt, Installer: bugref:9049: Wipe out VBOX_GUI_WITH_SHARED_LIBRARY, leaving split GUI build as the only possible.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 531.0 KB
Line 
1/* $Id: MachineImpl.cpp 76818 2019-01-14 16:17:07Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51
52// generated header
53#include "VBoxEvents.h"
54
55#ifdef VBOX_WITH_USB
56# include "USBProxyService.h"
57#endif
58
59#include "AutoCaller.h"
60#include "HashedPw.h"
61#include "Performance.h"
62
63#include <iprt/asm.h>
64#include <iprt/path.h>
65#include <iprt/dir.h>
66#include <iprt/env.h>
67#include <iprt/lockvalidator.h>
68#include <iprt/process.h>
69#include <iprt/cpp/utils.h>
70#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
71#include <iprt/sha.h>
72#include <iprt/string.h>
73#include <iprt/ctype.h>
74
75#include <VBox/com/array.h>
76#include <VBox/com/list.h>
77
78#include <VBox/err.h>
79#include <VBox/param.h>
80#include <VBox/settings.h>
81#include <VBox/VMMDev.h>
82#include <VBox/vmm/ssm.h>
83
84#ifdef VBOX_WITH_GUEST_PROPS
85# include <VBox/HostServices/GuestPropertySvc.h>
86# include <VBox/com/array.h>
87#endif
88
89#include "VBox/com/MultiResult.h"
90
91#include <algorithm>
92
93#ifdef VBOX_WITH_DTRACE_R3_MAIN
94# include "dtrace/VBoxAPI.h"
95#endif
96
97#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
98# define HOSTSUFF_EXE ".exe"
99#else /* !RT_OS_WINDOWS */
100# define HOSTSUFF_EXE ""
101#endif /* !RT_OS_WINDOWS */
102
103// defines / prototypes
104/////////////////////////////////////////////////////////////////////////////
105
106/////////////////////////////////////////////////////////////////////////////
107// Machine::Data structure
108/////////////////////////////////////////////////////////////////////////////
109
110Machine::Data::Data()
111{
112 mRegistered = FALSE;
113 pMachineConfigFile = NULL;
114 /* Contains hints on what has changed when the user is using the VM (config
115 * changes, running the VM, ...). This is used to decide if a config needs
116 * to be written to disk. */
117 flModifications = 0;
118 /* VM modification usually also trigger setting the current state to
119 * "Modified". Although this is not always the case. An e.g. is the VM
120 * initialization phase or when snapshot related data is changed. The
121 * actually behavior is controlled by the following flag. */
122 m_fAllowStateModification = false;
123 mAccessible = FALSE;
124 /* mUuid is initialized in Machine::init() */
125
126 mMachineState = MachineState_PoweredOff;
127 RTTimeNow(&mLastStateChange);
128
129 mMachineStateDeps = 0;
130 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
131 mMachineStateChangePending = 0;
132
133 mCurrentStateModified = TRUE;
134 mGuestPropertiesModified = FALSE;
135
136 mSession.mPID = NIL_RTPROCESS;
137 mSession.mLockType = LockType_Null;
138 mSession.mState = SessionState_Unlocked;
139}
140
141Machine::Data::~Data()
142{
143 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
144 {
145 RTSemEventMultiDestroy(mMachineStateDepsSem);
146 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
147 }
148 if (pMachineConfigFile)
149 {
150 delete pMachineConfigFile;
151 pMachineConfigFile = NULL;
152 }
153}
154
155/////////////////////////////////////////////////////////////////////////////
156// Machine::HWData structure
157/////////////////////////////////////////////////////////////////////////////
158
159Machine::HWData::HWData()
160{
161 /* default values for a newly created machine */
162 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
163 mMemorySize = 128;
164 mCPUCount = 1;
165 mCPUHotPlugEnabled = false;
166 mMemoryBalloonSize = 0;
167 mPageFusionEnabled = false;
168 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
169 mVRAMSize = 8;
170 mAccelerate3DEnabled = false;
171 mAccelerate2DVideoEnabled = false;
172 mMonitorCount = 1;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mNestedHWVirt = false;
201 mHPETEnabled = false;
202 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
203 mCpuIdPortabilityLevel = 0;
204 mCpuProfile = "host";
205
206 /* default boot order: floppy - DVD - HDD */
207 mBootOrder[0] = DeviceType_Floppy;
208 mBootOrder[1] = DeviceType_DVD;
209 mBootOrder[2] = DeviceType_HardDisk;
210 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
211 mBootOrder[i] = DeviceType_Null;
212
213 mClipboardMode = ClipboardMode_Disabled;
214 mDnDMode = DnDMode_Disabled;
215
216 mFirmwareType = FirmwareType_BIOS;
217 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
218 mPointingHIDType = PointingHIDType_PS2Mouse;
219 mChipsetType = ChipsetType_PIIX3;
220 mParavirtProvider = ParavirtProvider_Default;
221 mEmulatedUSBCardReaderEnabled = FALSE;
222
223 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
224 mCPUAttached[i] = false;
225
226 mIOCacheEnabled = true;
227 mIOCacheSize = 5; /* 5MB */
228}
229
230Machine::HWData::~HWData()
231{
232}
233
234/////////////////////////////////////////////////////////////////////////////
235// Machine class
236/////////////////////////////////////////////////////////////////////////////
237
238// constructor / destructor
239/////////////////////////////////////////////////////////////////////////////
240
241Machine::Machine() :
242#ifdef VBOX_WITH_RESOURCE_USAGE_API
243 mCollectorGuest(NULL),
244#endif
245 mPeer(NULL),
246 mParent(NULL),
247 mSerialPorts(),
248 mParallelPorts(),
249 uRegistryNeedsSaving(0)
250{}
251
252Machine::~Machine()
253{}
254
255HRESULT Machine::FinalConstruct()
256{
257 LogFlowThisFunc(("\n"));
258 return BaseFinalConstruct();
259}
260
261void Machine::FinalRelease()
262{
263 LogFlowThisFunc(("\n"));
264 uninit();
265 BaseFinalRelease();
266}
267
268/**
269 * Initializes a new machine instance; this init() variant creates a new, empty machine.
270 * This gets called from VirtualBox::CreateMachine().
271 *
272 * @param aParent Associated parent object
273 * @param strConfigFile Local file system path to the VM settings file (can
274 * be relative to the VirtualBox config directory).
275 * @param strName name for the machine
276 * @param llGroups list of groups for the machine
277 * @param strOsType OS Type string (stored as is if aOsType is NULL).
278 * @param aOsType OS Type of this machine or NULL.
279 * @param aId UUID for the new machine.
280 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
281 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
282 * scheme (includes the UUID).
283 *
284 * @return Success indicator. if not S_OK, the machine object is invalid
285 */
286HRESULT Machine::init(VirtualBox *aParent,
287 const Utf8Str &strConfigFile,
288 const Utf8Str &strName,
289 const StringsList &llGroups,
290 const Utf8Str &strOsType,
291 GuestOSType *aOsType,
292 const Guid &aId,
293 bool fForceOverwrite,
294 bool fDirectoryIncludesUUID)
295{
296 LogFlowThisFuncEnter();
297 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
298
299 /* Enclose the state transition NotReady->InInit->Ready */
300 AutoInitSpan autoInitSpan(this);
301 AssertReturn(autoInitSpan.isOk(), E_FAIL);
302
303 HRESULT rc = initImpl(aParent, strConfigFile);
304 if (FAILED(rc)) return rc;
305
306 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
307 if (FAILED(rc)) return rc;
308
309 if (SUCCEEDED(rc))
310 {
311 // create an empty machine config
312 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
313
314 rc = initDataAndChildObjects();
315 }
316
317 if (SUCCEEDED(rc))
318 {
319 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
320 mData->mAccessible = TRUE;
321
322 unconst(mData->mUuid) = aId;
323
324 mUserData->s.strName = strName;
325
326 if (llGroups.size())
327 mUserData->s.llGroups = llGroups;
328
329 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
330 // the "name sync" flag determines whether the machine directory gets renamed along
331 // with the machine file; say so if the settings file name is the same as the
332 // settings file parent directory (machine directory)
333 mUserData->s.fNameSync = i_isInOwnDir();
334
335 // initialize the default snapshots folder
336 rc = COMSETTER(SnapshotFolder)(NULL);
337 AssertComRC(rc);
338
339 if (aOsType)
340 {
341 /* Store OS type */
342 mUserData->s.strOsType = aOsType->i_id();
343
344 /* Let the OS type select 64-bit ness. */
345 mHWData->mLongMode = aOsType->i_is64Bit()
346 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
347
348 /* Let the OS type enable the X2APIC */
349 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
350 }
351 else if (!strOsType.isEmpty())
352 {
353 /* Store OS type */
354 mUserData->s.strOsType = strOsType;
355
356 /* No guest OS type object. Pick some plausible defaults which the
357 * host can handle. There's no way to know or validate anything. */
358 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 mHWData->mX2APIC = false;
360 }
361
362 /* Apply BIOS defaults. */
363 mBIOSSettings->i_applyDefaults(aOsType);
364
365 /* Apply record defaults. */
366 mRecordingSettings->i_applyDefaults();
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::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1733{
1734 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1735
1736 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1737
1738 return S_OK;
1739}
1740
1741HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1742{
1743 switch (aGraphicsControllerType)
1744 {
1745 case GraphicsControllerType_Null:
1746 case GraphicsControllerType_VBoxVGA:
1747#ifdef VBOX_WITH_VMSVGA
1748 case GraphicsControllerType_VMSVGA:
1749 case GraphicsControllerType_VBoxSVGA:
1750#endif
1751 break;
1752 default:
1753 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1754 }
1755
1756 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1757
1758 HRESULT rc = i_checkStateDependency(MutableStateDep);
1759 if (FAILED(rc)) return rc;
1760
1761 i_setModified(IsModified_MachineData);
1762 mHWData.backup();
1763 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1764
1765 return S_OK;
1766}
1767
1768HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1769{
1770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1771
1772 *aVRAMSize = mHWData->mVRAMSize;
1773
1774 return S_OK;
1775}
1776
1777HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1778{
1779 /* check VRAM limits */
1780 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1781 return setError(E_INVALIDARG,
1782 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1783 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1784
1785 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1786
1787 HRESULT rc = i_checkStateDependency(MutableStateDep);
1788 if (FAILED(rc)) return rc;
1789
1790 i_setModified(IsModified_MachineData);
1791 mHWData.backup();
1792 mHWData->mVRAMSize = aVRAMSize;
1793
1794 return S_OK;
1795}
1796
1797/** @todo this method should not be public */
1798HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1799{
1800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1801
1802 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1803
1804 return S_OK;
1805}
1806
1807/**
1808 * Set the memory balloon size.
1809 *
1810 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1811 * we have to make sure that we never call IGuest from here.
1812 */
1813HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1814{
1815 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1816#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1817 /* check limits */
1818 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1819 return setError(E_INVALIDARG,
1820 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1821 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1822
1823 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1824
1825 i_setModified(IsModified_MachineData);
1826 mHWData.backup();
1827 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1828
1829 return S_OK;
1830#else
1831 NOREF(aMemoryBalloonSize);
1832 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1833#endif
1834}
1835
1836HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1837{
1838 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1839
1840 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1841 return S_OK;
1842}
1843
1844HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1845{
1846#ifdef VBOX_WITH_PAGE_SHARING
1847 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1848
1849 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1850 i_setModified(IsModified_MachineData);
1851 mHWData.backup();
1852 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1853 return S_OK;
1854#else
1855 NOREF(aPageFusionEnabled);
1856 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1857#endif
1858}
1859
1860HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1861{
1862 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1863
1864 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1870{
1871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1872
1873 HRESULT rc = i_checkStateDependency(MutableStateDep);
1874 if (FAILED(rc)) return rc;
1875
1876 /** @todo check validity! */
1877
1878 i_setModified(IsModified_MachineData);
1879 mHWData.backup();
1880 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1881
1882 return S_OK;
1883}
1884
1885
1886HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1887{
1888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1889
1890 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1891
1892 return S_OK;
1893}
1894
1895HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1896{
1897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1898
1899 HRESULT rc = i_checkStateDependency(MutableStateDep);
1900 if (FAILED(rc)) return rc;
1901
1902 /** @todo check validity! */
1903 i_setModified(IsModified_MachineData);
1904 mHWData.backup();
1905 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1906
1907 return S_OK;
1908}
1909
1910HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1911{
1912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1913
1914 *aMonitorCount = mHWData->mMonitorCount;
1915
1916 return S_OK;
1917}
1918
1919HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1920{
1921 /* make sure monitor count is a sensible number */
1922 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1923 return setError(E_INVALIDARG,
1924 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1925 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1926
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 i_setModified(IsModified_MachineData);
1933 mHWData.backup();
1934 mHWData->mMonitorCount = aMonitorCount;
1935
1936 return S_OK;
1937}
1938
1939HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1940{
1941 /* mBIOSSettings is constant during life time, no need to lock */
1942 aBIOSSettings = mBIOSSettings;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1948{
1949 /* mRecordingSettings is constant during life time, no need to lock */
1950 aRecordingSettings = mRecordingSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1956{
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 switch (aProperty)
1960 {
1961 case CPUPropertyType_PAE:
1962 *aValue = mHWData->mPAEEnabled;
1963 break;
1964
1965 case CPUPropertyType_LongMode:
1966 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1967 *aValue = TRUE;
1968 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1969 *aValue = FALSE;
1970#if HC_ARCH_BITS == 64
1971 else
1972 *aValue = TRUE;
1973#else
1974 else
1975 {
1976 *aValue = FALSE;
1977
1978 ComObjPtr<GuestOSType> pGuestOSType;
1979 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1980 pGuestOSType);
1981 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1982 {
1983 if (pGuestOSType->i_is64Bit())
1984 {
1985 ComObjPtr<Host> pHost = mParent->i_host();
1986 alock.release();
1987
1988 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1989 if (FAILED(hrc2))
1990 *aValue = FALSE;
1991 }
1992 }
1993 }
1994#endif
1995 break;
1996
1997 case CPUPropertyType_TripleFaultReset:
1998 *aValue = mHWData->mTripleFaultReset;
1999 break;
2000
2001 case CPUPropertyType_APIC:
2002 *aValue = mHWData->mAPIC;
2003 break;
2004
2005 case CPUPropertyType_X2APIC:
2006 *aValue = mHWData->mX2APIC;
2007 break;
2008
2009 case CPUPropertyType_IBPBOnVMExit:
2010 *aValue = mHWData->mIBPBOnVMExit;
2011 break;
2012
2013 case CPUPropertyType_IBPBOnVMEntry:
2014 *aValue = mHWData->mIBPBOnVMEntry;
2015 break;
2016
2017 case CPUPropertyType_SpecCtrl:
2018 *aValue = mHWData->mSpecCtrl;
2019 break;
2020
2021 case CPUPropertyType_SpecCtrlByHost:
2022 *aValue = mHWData->mSpecCtrlByHost;
2023 break;
2024
2025 case CPUPropertyType_HWVirt:
2026 *aValue = mHWData->mNestedHWVirt;
2027 break;
2028
2029 case CPUPropertyType_L1DFlushOnEMTScheduling:
2030 *aValue = mHWData->mL1DFlushOnSched;
2031 break;
2032
2033 case CPUPropertyType_L1DFlushOnVMEntry:
2034 *aValue = mHWData->mL1DFlushOnVMEntry;
2035 break;
2036
2037 default:
2038 return E_INVALIDARG;
2039 }
2040 return S_OK;
2041}
2042
2043HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2044{
2045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2046
2047 HRESULT rc = i_checkStateDependency(MutableStateDep);
2048 if (FAILED(rc)) return rc;
2049
2050 switch (aProperty)
2051 {
2052 case CPUPropertyType_PAE:
2053 i_setModified(IsModified_MachineData);
2054 mHWData.backup();
2055 mHWData->mPAEEnabled = !!aValue;
2056 break;
2057
2058 case CPUPropertyType_LongMode:
2059 i_setModified(IsModified_MachineData);
2060 mHWData.backup();
2061 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2062 break;
2063
2064 case CPUPropertyType_TripleFaultReset:
2065 i_setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mTripleFaultReset = !!aValue;
2068 break;
2069
2070 case CPUPropertyType_APIC:
2071 if (mHWData->mX2APIC)
2072 aValue = TRUE;
2073 i_setModified(IsModified_MachineData);
2074 mHWData.backup();
2075 mHWData->mAPIC = !!aValue;
2076 break;
2077
2078 case CPUPropertyType_X2APIC:
2079 i_setModified(IsModified_MachineData);
2080 mHWData.backup();
2081 mHWData->mX2APIC = !!aValue;
2082 if (aValue)
2083 mHWData->mAPIC = !!aValue;
2084 break;
2085
2086 case CPUPropertyType_IBPBOnVMExit:
2087 i_setModified(IsModified_MachineData);
2088 mHWData.backup();
2089 mHWData->mIBPBOnVMExit = !!aValue;
2090 break;
2091
2092 case CPUPropertyType_IBPBOnVMEntry:
2093 i_setModified(IsModified_MachineData);
2094 mHWData.backup();
2095 mHWData->mIBPBOnVMEntry = !!aValue;
2096 break;
2097
2098 case CPUPropertyType_SpecCtrl:
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mSpecCtrl = !!aValue;
2102 break;
2103
2104 case CPUPropertyType_SpecCtrlByHost:
2105 i_setModified(IsModified_MachineData);
2106 mHWData.backup();
2107 mHWData->mSpecCtrlByHost = !!aValue;
2108 break;
2109
2110 case CPUPropertyType_HWVirt:
2111 i_setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mNestedHWVirt = !!aValue;
2114 break;
2115
2116 case CPUPropertyType_L1DFlushOnEMTScheduling:
2117 i_setModified(IsModified_MachineData);
2118 mHWData.backup();
2119 mHWData->mL1DFlushOnSched = !!aValue;
2120 break;
2121
2122 case CPUPropertyType_L1DFlushOnVMEntry:
2123 i_setModified(IsModified_MachineData);
2124 mHWData.backup();
2125 mHWData->mL1DFlushOnVMEntry = !!aValue;
2126 break;
2127
2128 default:
2129 return E_INVALIDARG;
2130 }
2131 return S_OK;
2132}
2133
2134HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2135 ULONG *aValEcx, ULONG *aValEdx)
2136{
2137 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2138 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2139 {
2140 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2141 it != mHWData->mCpuIdLeafList.end();
2142 ++it)
2143 {
2144 if (aOrdinal == 0)
2145 {
2146 const settings::CpuIdLeaf &rLeaf= *it;
2147 *aIdx = rLeaf.idx;
2148 *aSubIdx = rLeaf.idxSub;
2149 *aValEax = rLeaf.uEax;
2150 *aValEbx = rLeaf.uEbx;
2151 *aValEcx = rLeaf.uEcx;
2152 *aValEdx = rLeaf.uEdx;
2153 return S_OK;
2154 }
2155 aOrdinal--;
2156 }
2157 }
2158 return E_INVALIDARG;
2159}
2160
2161HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2162{
2163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2164
2165 /*
2166 * Search the list.
2167 */
2168 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2169 {
2170 const settings::CpuIdLeaf &rLeaf= *it;
2171 if ( rLeaf.idx == aIdx
2172 && ( aSubIdx == UINT32_MAX
2173 || rLeaf.idxSub == aSubIdx) )
2174 {
2175 *aValEax = rLeaf.uEax;
2176 *aValEbx = rLeaf.uEbx;
2177 *aValEcx = rLeaf.uEcx;
2178 *aValEdx = rLeaf.uEdx;
2179 return S_OK;
2180 }
2181 }
2182
2183 return E_INVALIDARG;
2184}
2185
2186
2187HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2188{
2189 /*
2190 * Validate input before taking locks and checking state.
2191 */
2192 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2193 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2194 if ( aIdx >= UINT32_C(0x20)
2195 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2196 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2197 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2198
2199 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2200 HRESULT rc = i_checkStateDependency(MutableStateDep);
2201 if (FAILED(rc)) return rc;
2202
2203 /*
2204 * Impose a maximum number of leaves.
2205 */
2206 if (mHWData->mCpuIdLeafList.size() > 256)
2207 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2208
2209 /*
2210 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2211 */
2212 i_setModified(IsModified_MachineData);
2213 mHWData.backup();
2214
2215 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2216 {
2217 settings::CpuIdLeaf &rLeaf= *it;
2218 if ( rLeaf.idx == aIdx
2219 && ( aSubIdx == UINT32_MAX
2220 || rLeaf.idxSub == aSubIdx) )
2221 it = mHWData->mCpuIdLeafList.erase(it);
2222 else
2223 ++it;
2224 }
2225
2226 settings::CpuIdLeaf NewLeaf;
2227 NewLeaf.idx = aIdx;
2228 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2229 NewLeaf.uEax = aValEax;
2230 NewLeaf.uEbx = aValEbx;
2231 NewLeaf.uEcx = aValEcx;
2232 NewLeaf.uEdx = aValEdx;
2233 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2234 return S_OK;
2235}
2236
2237HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2238{
2239 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2240
2241 HRESULT rc = i_checkStateDependency(MutableStateDep);
2242 if (FAILED(rc)) return rc;
2243
2244 /*
2245 * Do the removal.
2246 */
2247 bool fModified = false;
2248 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2249 {
2250 settings::CpuIdLeaf &rLeaf= *it;
2251 if ( rLeaf.idx == aIdx
2252 && ( aSubIdx == UINT32_MAX
2253 || rLeaf.idxSub == aSubIdx) )
2254 {
2255 if (!fModified)
2256 {
2257 fModified = true;
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 }
2261 it = mHWData->mCpuIdLeafList.erase(it);
2262 }
2263 else
2264 ++it;
2265 }
2266
2267 return S_OK;
2268}
2269
2270HRESULT Machine::removeAllCPUIDLeaves()
2271{
2272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2273
2274 HRESULT rc = i_checkStateDependency(MutableStateDep);
2275 if (FAILED(rc)) return rc;
2276
2277 if (mHWData->mCpuIdLeafList.size() > 0)
2278 {
2279 i_setModified(IsModified_MachineData);
2280 mHWData.backup();
2281
2282 mHWData->mCpuIdLeafList.clear();
2283 }
2284
2285 return S_OK;
2286}
2287HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2288{
2289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2290
2291 switch(aProperty)
2292 {
2293 case HWVirtExPropertyType_Enabled:
2294 *aValue = mHWData->mHWVirtExEnabled;
2295 break;
2296
2297 case HWVirtExPropertyType_VPID:
2298 *aValue = mHWData->mHWVirtExVPIDEnabled;
2299 break;
2300
2301 case HWVirtExPropertyType_NestedPaging:
2302 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2303 break;
2304
2305 case HWVirtExPropertyType_UnrestrictedExecution:
2306 *aValue = mHWData->mHWVirtExUXEnabled;
2307 break;
2308
2309 case HWVirtExPropertyType_LargePages:
2310 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2311#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2312 *aValue = FALSE;
2313#endif
2314 break;
2315
2316 case HWVirtExPropertyType_Force:
2317 *aValue = mHWData->mHWVirtExForceEnabled;
2318 break;
2319
2320 case HWVirtExPropertyType_UseNativeApi:
2321 *aValue = mHWData->mHWVirtExUseNativeApi;
2322 break;
2323
2324 default:
2325 return E_INVALIDARG;
2326 }
2327 return S_OK;
2328}
2329
2330HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2331{
2332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2333
2334 HRESULT rc = i_checkStateDependency(MutableStateDep);
2335 if (FAILED(rc)) return rc;
2336
2337 switch (aProperty)
2338 {
2339 case HWVirtExPropertyType_Enabled:
2340 i_setModified(IsModified_MachineData);
2341 mHWData.backup();
2342 mHWData->mHWVirtExEnabled = !!aValue;
2343 break;
2344
2345 case HWVirtExPropertyType_VPID:
2346 i_setModified(IsModified_MachineData);
2347 mHWData.backup();
2348 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2349 break;
2350
2351 case HWVirtExPropertyType_NestedPaging:
2352 i_setModified(IsModified_MachineData);
2353 mHWData.backup();
2354 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2355 break;
2356
2357 case HWVirtExPropertyType_UnrestrictedExecution:
2358 i_setModified(IsModified_MachineData);
2359 mHWData.backup();
2360 mHWData->mHWVirtExUXEnabled = !!aValue;
2361 break;
2362
2363 case HWVirtExPropertyType_LargePages:
2364 i_setModified(IsModified_MachineData);
2365 mHWData.backup();
2366 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2367 break;
2368
2369 case HWVirtExPropertyType_Force:
2370 i_setModified(IsModified_MachineData);
2371 mHWData.backup();
2372 mHWData->mHWVirtExForceEnabled = !!aValue;
2373 break;
2374
2375 case HWVirtExPropertyType_UseNativeApi:
2376 i_setModified(IsModified_MachineData);
2377 mHWData.backup();
2378 mHWData->mHWVirtExUseNativeApi = !!aValue;
2379 break;
2380
2381 default:
2382 return E_INVALIDARG;
2383 }
2384
2385 return S_OK;
2386}
2387
2388HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2389{
2390 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2391
2392 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2393
2394 return S_OK;
2395}
2396
2397HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2398{
2399 /** @todo (r=dmik):
2400 * 1. Allow to change the name of the snapshot folder containing snapshots
2401 * 2. Rename the folder on disk instead of just changing the property
2402 * value (to be smart and not to leave garbage). Note that it cannot be
2403 * done here because the change may be rolled back. Thus, the right
2404 * place is #saveSettings().
2405 */
2406
2407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2408
2409 HRESULT rc = i_checkStateDependency(MutableStateDep);
2410 if (FAILED(rc)) return rc;
2411
2412 if (!mData->mCurrentSnapshot.isNull())
2413 return setError(E_FAIL,
2414 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2415
2416 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2417
2418 if (strSnapshotFolder.isEmpty())
2419 strSnapshotFolder = "Snapshots";
2420 int vrc = i_calculateFullPath(strSnapshotFolder,
2421 strSnapshotFolder);
2422 if (RT_FAILURE(vrc))
2423 return setErrorBoth(E_FAIL, vrc,
2424 tr("Invalid snapshot folder '%s' (%Rrc)"),
2425 strSnapshotFolder.c_str(), vrc);
2426
2427 i_setModified(IsModified_MachineData);
2428 mUserData.backup();
2429
2430 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2431
2432 return S_OK;
2433}
2434
2435HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2436{
2437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 aMediumAttachments.resize(mMediumAttachments->size());
2440 size_t i = 0;
2441 for (MediumAttachmentList::const_iterator
2442 it = mMediumAttachments->begin();
2443 it != mMediumAttachments->end();
2444 ++it, ++i)
2445 aMediumAttachments[i] = *it;
2446
2447 return S_OK;
2448}
2449
2450HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2451{
2452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2453
2454 Assert(!!mVRDEServer);
2455
2456 aVRDEServer = mVRDEServer;
2457
2458 return S_OK;
2459}
2460
2461HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2462{
2463 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 aAudioAdapter = mAudioAdapter;
2466
2467 return S_OK;
2468}
2469
2470HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2471{
2472#ifdef VBOX_WITH_VUSB
2473 clearError();
2474 MultiResult rc(S_OK);
2475
2476# ifdef VBOX_WITH_USB
2477 rc = mParent->i_host()->i_checkUSBProxyService();
2478 if (FAILED(rc)) return rc;
2479# endif
2480
2481 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2482
2483 aUSBControllers.resize(mUSBControllers->size());
2484 size_t i = 0;
2485 for (USBControllerList::const_iterator
2486 it = mUSBControllers->begin();
2487 it != mUSBControllers->end();
2488 ++it, ++i)
2489 aUSBControllers[i] = *it;
2490
2491 return S_OK;
2492#else
2493 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2494 * extended error info to indicate that USB is simply not available
2495 * (w/o treating it as a failure), for example, as in OSE */
2496 NOREF(aUSBControllers);
2497 ReturnComNotImplemented();
2498#endif /* VBOX_WITH_VUSB */
2499}
2500
2501HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2502{
2503#ifdef VBOX_WITH_VUSB
2504 clearError();
2505 MultiResult rc(S_OK);
2506
2507# ifdef VBOX_WITH_USB
2508 rc = mParent->i_host()->i_checkUSBProxyService();
2509 if (FAILED(rc)) return rc;
2510# endif
2511
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 aUSBDeviceFilters = mUSBDeviceFilters;
2515 return rc;
2516#else
2517 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2518 * extended error info to indicate that USB is simply not available
2519 * (w/o treating it as a failure), for example, as in OSE */
2520 NOREF(aUSBDeviceFilters);
2521 ReturnComNotImplemented();
2522#endif /* VBOX_WITH_VUSB */
2523}
2524
2525HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2526{
2527 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2528
2529 aSettingsFilePath = mData->m_strConfigFileFull;
2530
2531 return S_OK;
2532}
2533
2534HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2535{
2536 RT_NOREF(aSettingsFilePath);
2537 ReturnComNotImplemented();
2538}
2539
2540HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2541{
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2545 if (FAILED(rc)) return rc;
2546
2547 if (!mData->pMachineConfigFile->fileExists())
2548 // this is a new machine, and no config file exists yet:
2549 *aSettingsModified = TRUE;
2550 else
2551 *aSettingsModified = (mData->flModifications != 0);
2552
2553 return S_OK;
2554}
2555
2556HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2557{
2558 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2559
2560 *aSessionState = mData->mSession.mState;
2561
2562 return S_OK;
2563}
2564
2565HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2566{
2567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2568
2569 aSessionName = mData->mSession.mName;
2570
2571 return S_OK;
2572}
2573
2574HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2575{
2576 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2577
2578 *aSessionPID = mData->mSession.mPID;
2579
2580 return S_OK;
2581}
2582
2583HRESULT Machine::getState(MachineState_T *aState)
2584{
2585 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2586
2587 *aState = mData->mMachineState;
2588 Assert(mData->mMachineState != MachineState_Null);
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2594{
2595 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2596
2597 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2598
2599 return S_OK;
2600}
2601
2602HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2603{
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 aStateFilePath = mSSData->strStateFilePath;
2607
2608 return S_OK;
2609}
2610
2611HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2612{
2613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 i_getLogFolder(aLogFolder);
2616
2617 return S_OK;
2618}
2619
2620HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2621{
2622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2623
2624 aCurrentSnapshot = mData->mCurrentSnapshot;
2625
2626 return S_OK;
2627}
2628
2629HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2630{
2631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2632
2633 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2634 ? 0
2635 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2641{
2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2643
2644 /* Note: for machines with no snapshots, we always return FALSE
2645 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2646 * reasons :) */
2647
2648 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2649 ? FALSE
2650 : mData->mCurrentStateModified;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 aSharedFolders.resize(mHWData->mSharedFolders.size());
2660 size_t i = 0;
2661 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2662 it = mHWData->mSharedFolders.begin();
2663 it != mHWData->mSharedFolders.end();
2664 ++it, ++i)
2665 aSharedFolders[i] = *it;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 *aClipboardMode = mHWData->mClipboardMode;
2675
2676 return S_OK;
2677}
2678
2679HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2680{
2681 HRESULT rc = S_OK;
2682
2683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2684
2685 alock.release();
2686 rc = i_onClipboardModeChange(aClipboardMode);
2687 alock.acquire();
2688 if (FAILED(rc)) return rc;
2689
2690 i_setModified(IsModified_MachineData);
2691 mHWData.backup();
2692 mHWData->mClipboardMode = aClipboardMode;
2693
2694 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2695 if (Global::IsOnline(mData->mMachineState))
2696 i_saveSettings(NULL);
2697
2698 return S_OK;
2699}
2700
2701HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 *aDnDMode = mHWData->mDnDMode;
2706
2707 return S_OK;
2708}
2709
2710HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2711{
2712 HRESULT rc = S_OK;
2713
2714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2715
2716 alock.release();
2717 rc = i_onDnDModeChange(aDnDMode);
2718
2719 alock.acquire();
2720 if (FAILED(rc)) return rc;
2721
2722 i_setModified(IsModified_MachineData);
2723 mHWData.backup();
2724 mHWData->mDnDMode = aDnDMode;
2725
2726 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2727 if (Global::IsOnline(mData->mMachineState))
2728 i_saveSettings(NULL);
2729
2730 return S_OK;
2731}
2732
2733HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2734{
2735 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2736
2737 aStorageControllers.resize(mStorageControllers->size());
2738 size_t i = 0;
2739 for (StorageControllerList::const_iterator
2740 it = mStorageControllers->begin();
2741 it != mStorageControllers->end();
2742 ++it, ++i)
2743 aStorageControllers[i] = *it;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2749{
2750 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 *aEnabled = mUserData->s.fTeleporterEnabled;
2753
2754 return S_OK;
2755}
2756
2757HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2758{
2759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2760
2761 /* Only allow it to be set to true when PoweredOff or Aborted.
2762 (Clearing it is always permitted.) */
2763 if ( aTeleporterEnabled
2764 && mData->mRegistered
2765 && ( !i_isSessionMachine()
2766 || ( mData->mMachineState != MachineState_PoweredOff
2767 && mData->mMachineState != MachineState_Teleported
2768 && mData->mMachineState != MachineState_Aborted
2769 )
2770 )
2771 )
2772 return setError(VBOX_E_INVALID_VM_STATE,
2773 tr("The machine is not powered off (state is %s)"),
2774 Global::stringifyMachineState(mData->mMachineState));
2775
2776 i_setModified(IsModified_MachineData);
2777 mUserData.backup();
2778 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2793{
2794 if (aTeleporterPort >= _64K)
2795 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2796
2797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2798
2799 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2800 if (FAILED(rc)) return rc;
2801
2802 i_setModified(IsModified_MachineData);
2803 mUserData.backup();
2804 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2805
2806 return S_OK;
2807}
2808
2809HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2810{
2811 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2812
2813 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2819{
2820 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2823 if (FAILED(rc)) return rc;
2824
2825 i_setModified(IsModified_MachineData);
2826 mUserData.backup();
2827 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2828
2829 return S_OK;
2830}
2831
2832HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2833{
2834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2835 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2836
2837 return S_OK;
2838}
2839
2840HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2841{
2842 /*
2843 * Hash the password first.
2844 */
2845 com::Utf8Str aT = aTeleporterPassword;
2846
2847 if (!aT.isEmpty())
2848 {
2849 if (VBoxIsPasswordHashed(&aT))
2850 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2851 VBoxHashPassword(&aT);
2852 }
2853
2854 /*
2855 * Do the update.
2856 */
2857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2858 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2859 if (SUCCEEDED(hrc))
2860 {
2861 i_setModified(IsModified_MachineData);
2862 mUserData.backup();
2863 mUserData->s.strTeleporterPassword = aT;
2864 }
2865
2866 return hrc;
2867}
2868
2869HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2870{
2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2872
2873 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2874 return S_OK;
2875}
2876
2877HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2878{
2879 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2880
2881 /** @todo deal with running state change. */
2882 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2883 if (FAILED(rc)) return rc;
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2888 return S_OK;
2889}
2890
2891HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
2892{
2893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2894
2895 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
2896 return S_OK;
2897}
2898
2899HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
2900{
2901 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 /** @todo deal with running state change. */
2904 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2905 if (FAILED(rc)) return rc;
2906
2907 i_setModified(IsModified_MachineData);
2908 mUserData.backup();
2909 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
2910 return S_OK;
2911}
2912
2913HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
2914{
2915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
2918 return S_OK;
2919}
2920
2921HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
2922{
2923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2924
2925 /** @todo deal with running state change. */
2926 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2927 if (FAILED(rc)) return rc;
2928
2929 i_setModified(IsModified_MachineData);
2930 mUserData.backup();
2931 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
2932 return S_OK;
2933}
2934
2935HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
2936{
2937 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2938
2939 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
2940
2941 return S_OK;
2942}
2943
2944HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
2945{
2946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2947
2948 /** @todo deal with running state change. */
2949 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2950 if (FAILED(rc)) return rc;
2951
2952 i_setModified(IsModified_MachineData);
2953 mUserData.backup();
2954 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
2955
2956 return S_OK;
2957}
2958
2959HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
2960{
2961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2962
2963 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
2964 return S_OK;
2965}
2966
2967HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
2968{
2969 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2970
2971 /** @todo deal with running state change. */
2972 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2973 if (FAILED(rc)) return rc;
2974
2975 i_setModified(IsModified_MachineData);
2976 mUserData.backup();
2977 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
2978 return S_OK;
2979}
2980
2981HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2982{
2983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2984
2985 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2986
2987 return S_OK;
2988}
2989
2990HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2991{
2992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2993
2994 /* Only allow it to be set to true when PoweredOff or Aborted.
2995 (Clearing it is always permitted.) */
2996 if ( aRTCUseUTC
2997 && mData->mRegistered
2998 && ( !i_isSessionMachine()
2999 || ( mData->mMachineState != MachineState_PoweredOff
3000 && mData->mMachineState != MachineState_Teleported
3001 && mData->mMachineState != MachineState_Aborted
3002 )
3003 )
3004 )
3005 return setError(VBOX_E_INVALID_VM_STATE,
3006 tr("The machine is not powered off (state is %s)"),
3007 Global::stringifyMachineState(mData->mMachineState));
3008
3009 i_setModified(IsModified_MachineData);
3010 mUserData.backup();
3011 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3012
3013 return S_OK;
3014}
3015
3016HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3017{
3018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3019
3020 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3021
3022 return S_OK;
3023}
3024
3025HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3026{
3027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3028
3029 HRESULT rc = i_checkStateDependency(MutableStateDep);
3030 if (FAILED(rc)) return rc;
3031
3032 i_setModified(IsModified_MachineData);
3033 mHWData.backup();
3034 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3035
3036 return S_OK;
3037}
3038
3039HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3040{
3041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3042
3043 *aIOCacheSize = mHWData->mIOCacheSize;
3044
3045 return S_OK;
3046}
3047
3048HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3049{
3050 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3051
3052 HRESULT rc = i_checkStateDependency(MutableStateDep);
3053 if (FAILED(rc)) return rc;
3054
3055 i_setModified(IsModified_MachineData);
3056 mHWData.backup();
3057 mHWData->mIOCacheSize = aIOCacheSize;
3058
3059 return S_OK;
3060}
3061
3062
3063/**
3064 * @note Locks objects!
3065 */
3066HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3067 LockType_T aLockType)
3068{
3069 /* check the session state */
3070 SessionState_T state;
3071 HRESULT rc = aSession->COMGETTER(State)(&state);
3072 if (FAILED(rc)) return rc;
3073
3074 if (state != SessionState_Unlocked)
3075 return setError(VBOX_E_INVALID_OBJECT_STATE,
3076 tr("The given session is busy"));
3077
3078 // get the client's IInternalSessionControl interface
3079 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3080 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3081 E_INVALIDARG);
3082
3083 // session name (only used in some code paths)
3084 Utf8Str strSessionName;
3085
3086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3087
3088 if (!mData->mRegistered)
3089 return setError(E_UNEXPECTED,
3090 tr("The machine '%s' is not registered"),
3091 mUserData->s.strName.c_str());
3092
3093 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3094
3095 SessionState_T oldState = mData->mSession.mState;
3096 /* Hack: in case the session is closing and there is a progress object
3097 * which allows waiting for the session to be closed, take the opportunity
3098 * and do a limited wait (max. 1 second). This helps a lot when the system
3099 * is busy and thus session closing can take a little while. */
3100 if ( mData->mSession.mState == SessionState_Unlocking
3101 && mData->mSession.mProgress)
3102 {
3103 alock.release();
3104 mData->mSession.mProgress->WaitForCompletion(1000);
3105 alock.acquire();
3106 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3107 }
3108
3109 // try again now
3110 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3111 // (i.e. session machine exists)
3112 && (aLockType == LockType_Shared) // caller wants a shared link to the
3113 // existing session that holds the write lock:
3114 )
3115 {
3116 // OK, share the session... we are now dealing with three processes:
3117 // 1) VBoxSVC (where this code runs);
3118 // 2) process C: the caller's client process (who wants a shared session);
3119 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3120
3121 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3122 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3123 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3124 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3125 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3126
3127 /*
3128 * Release the lock before calling the client process. It's safe here
3129 * since the only thing to do after we get the lock again is to add
3130 * the remote control to the list (which doesn't directly influence
3131 * anything).
3132 */
3133 alock.release();
3134
3135 // get the console of the session holding the write lock (this is a remote call)
3136 ComPtr<IConsole> pConsoleW;
3137 if (mData->mSession.mLockType == LockType_VM)
3138 {
3139 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3140 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3141 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3142 if (FAILED(rc))
3143 // the failure may occur w/o any error info (from RPC), so provide one
3144 return setError(VBOX_E_VM_ERROR,
3145 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3146 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3147 }
3148
3149 // share the session machine and W's console with the caller's session
3150 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3151 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3152 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3153
3154 if (FAILED(rc))
3155 // the failure may occur w/o any error info (from RPC), so provide one
3156 return setError(VBOX_E_VM_ERROR,
3157 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3158 alock.acquire();
3159
3160 // need to revalidate the state after acquiring the lock again
3161 if (mData->mSession.mState != SessionState_Locked)
3162 {
3163 pSessionControl->Uninitialize();
3164 return setError(VBOX_E_INVALID_SESSION_STATE,
3165 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3166 mUserData->s.strName.c_str());
3167 }
3168
3169 // add the caller's session to the list
3170 mData->mSession.mRemoteControls.push_back(pSessionControl);
3171 }
3172 else if ( mData->mSession.mState == SessionState_Locked
3173 || mData->mSession.mState == SessionState_Unlocking
3174 )
3175 {
3176 // sharing not permitted, or machine still unlocking:
3177 return setError(VBOX_E_INVALID_OBJECT_STATE,
3178 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3179 mUserData->s.strName.c_str());
3180 }
3181 else
3182 {
3183 // machine is not locked: then write-lock the machine (create the session machine)
3184
3185 // must not be busy
3186 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3187
3188 // get the caller's session PID
3189 RTPROCESS pid = NIL_RTPROCESS;
3190 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3191 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3192 Assert(pid != NIL_RTPROCESS);
3193
3194 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3195
3196 if (fLaunchingVMProcess)
3197 {
3198 if (mData->mSession.mPID == NIL_RTPROCESS)
3199 {
3200 // two or more clients racing for a lock, the one which set the
3201 // session state to Spawning will win, the others will get an
3202 // error as we can't decide here if waiting a little would help
3203 // (only for shared locks this would avoid an error)
3204 return setError(VBOX_E_INVALID_OBJECT_STATE,
3205 tr("The machine '%s' already has a lock request pending"),
3206 mUserData->s.strName.c_str());
3207 }
3208
3209 // this machine is awaiting for a spawning session to be opened:
3210 // then the calling process must be the one that got started by
3211 // LaunchVMProcess()
3212
3213 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3214 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3215
3216#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3217 /* Hardened windows builds spawns three processes when a VM is
3218 launched, the 3rd one is the one that will end up here. */
3219 RTPROCESS ppid;
3220 int rc = RTProcQueryParent(pid, &ppid);
3221 if (RT_SUCCESS(rc))
3222 rc = RTProcQueryParent(ppid, &ppid);
3223 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3224 || rc == VERR_ACCESS_DENIED)
3225 {
3226 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3227 mData->mSession.mPID = pid;
3228 }
3229#endif
3230
3231 if (mData->mSession.mPID != pid)
3232 return setError(E_ACCESSDENIED,
3233 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3234 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3235 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3236 }
3237
3238 // create the mutable SessionMachine from the current machine
3239 ComObjPtr<SessionMachine> sessionMachine;
3240 sessionMachine.createObject();
3241 rc = sessionMachine->init(this);
3242 AssertComRC(rc);
3243
3244 /* NOTE: doing return from this function after this point but
3245 * before the end is forbidden since it may call SessionMachine::uninit()
3246 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3247 * lock while still holding the Machine lock in alock so that a deadlock
3248 * is possible due to the wrong lock order. */
3249
3250 if (SUCCEEDED(rc))
3251 {
3252 /*
3253 * Set the session state to Spawning to protect against subsequent
3254 * attempts to open a session and to unregister the machine after
3255 * we release the lock.
3256 */
3257 SessionState_T origState = mData->mSession.mState;
3258 mData->mSession.mState = SessionState_Spawning;
3259
3260#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3261 /* Get the client token ID to be passed to the client process */
3262 Utf8Str strTokenId;
3263 sessionMachine->i_getTokenId(strTokenId);
3264 Assert(!strTokenId.isEmpty());
3265#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3266 /* Get the client token to be passed to the client process */
3267 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3268 /* The token is now "owned" by pToken, fix refcount */
3269 if (!pToken.isNull())
3270 pToken->Release();
3271#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3272
3273 /*
3274 * Release the lock before calling the client process -- it will call
3275 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3276 * because the state is Spawning, so that LaunchVMProcess() and
3277 * LockMachine() calls will fail. This method, called before we
3278 * acquire the lock again, will fail because of the wrong PID.
3279 *
3280 * Note that mData->mSession.mRemoteControls accessed outside
3281 * the lock may not be modified when state is Spawning, so it's safe.
3282 */
3283 alock.release();
3284
3285 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3286#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3287 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3288#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3289 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3290 /* Now the token is owned by the client process. */
3291 pToken.setNull();
3292#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3293 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3294
3295 /* The failure may occur w/o any error info (from RPC), so provide one */
3296 if (FAILED(rc))
3297 setError(VBOX_E_VM_ERROR,
3298 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3299
3300 // get session name, either to remember or to compare against
3301 // the already known session name.
3302 {
3303 Bstr bstrSessionName;
3304 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3305 if (SUCCEEDED(rc2))
3306 strSessionName = bstrSessionName;
3307 }
3308
3309 if ( SUCCEEDED(rc)
3310 && fLaunchingVMProcess
3311 )
3312 {
3313 /* complete the remote session initialization */
3314
3315 /* get the console from the direct session */
3316 ComPtr<IConsole> console;
3317 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3318 ComAssertComRC(rc);
3319
3320 if (SUCCEEDED(rc) && !console)
3321 {
3322 ComAssert(!!console);
3323 rc = E_FAIL;
3324 }
3325
3326 /* assign machine & console to the remote session */
3327 if (SUCCEEDED(rc))
3328 {
3329 /*
3330 * after LaunchVMProcess(), the first and the only
3331 * entry in remoteControls is that remote session
3332 */
3333 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3334 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3335 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3336
3337 /* The failure may occur w/o any error info (from RPC), so provide one */
3338 if (FAILED(rc))
3339 setError(VBOX_E_VM_ERROR,
3340 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3341 }
3342
3343 if (FAILED(rc))
3344 pSessionControl->Uninitialize();
3345 }
3346
3347 /* acquire the lock again */
3348 alock.acquire();
3349
3350 /* Restore the session state */
3351 mData->mSession.mState = origState;
3352 }
3353
3354 // finalize spawning anyway (this is why we don't return on errors above)
3355 if (fLaunchingVMProcess)
3356 {
3357 Assert(mData->mSession.mName == strSessionName);
3358 /* Note that the progress object is finalized later */
3359 /** @todo Consider checking mData->mSession.mProgress for cancellation
3360 * around here. */
3361
3362 /* We don't reset mSession.mPID here because it is necessary for
3363 * SessionMachine::uninit() to reap the child process later. */
3364
3365 if (FAILED(rc))
3366 {
3367 /* Close the remote session, remove the remote control from the list
3368 * and reset session state to Closed (@note keep the code in sync
3369 * with the relevant part in checkForSpawnFailure()). */
3370
3371 Assert(mData->mSession.mRemoteControls.size() == 1);
3372 if (mData->mSession.mRemoteControls.size() == 1)
3373 {
3374 ErrorInfoKeeper eik;
3375 mData->mSession.mRemoteControls.front()->Uninitialize();
3376 }
3377
3378 mData->mSession.mRemoteControls.clear();
3379 mData->mSession.mState = SessionState_Unlocked;
3380 }
3381 }
3382 else
3383 {
3384 /* memorize PID of the directly opened session */
3385 if (SUCCEEDED(rc))
3386 mData->mSession.mPID = pid;
3387 }
3388
3389 if (SUCCEEDED(rc))
3390 {
3391 mData->mSession.mLockType = aLockType;
3392 /* memorize the direct session control and cache IUnknown for it */
3393 mData->mSession.mDirectControl = pSessionControl;
3394 mData->mSession.mState = SessionState_Locked;
3395 if (!fLaunchingVMProcess)
3396 mData->mSession.mName = strSessionName;
3397 /* associate the SessionMachine with this Machine */
3398 mData->mSession.mMachine = sessionMachine;
3399
3400 /* request an IUnknown pointer early from the remote party for later
3401 * identity checks (it will be internally cached within mDirectControl
3402 * at least on XPCOM) */
3403 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3404 NOREF(unk);
3405 }
3406
3407 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3408 * would break the lock order */
3409 alock.release();
3410
3411 /* uninitialize the created session machine on failure */
3412 if (FAILED(rc))
3413 sessionMachine->uninit();
3414 }
3415
3416 if (SUCCEEDED(rc))
3417 {
3418 /*
3419 * tell the client watcher thread to update the set of
3420 * machines that have open sessions
3421 */
3422 mParent->i_updateClientWatcher();
3423
3424 if (oldState != SessionState_Locked)
3425 /* fire an event */
3426 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3427 }
3428
3429 return rc;
3430}
3431
3432/**
3433 * @note Locks objects!
3434 */
3435HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3436 const com::Utf8Str &aName,
3437 const com::Utf8Str &aEnvironment,
3438 ComPtr<IProgress> &aProgress)
3439{
3440 Utf8Str strFrontend(aName);
3441 /* "emergencystop" doesn't need the session, so skip the checks/interface
3442 * retrieval. This code doesn't quite fit in here, but introducing a
3443 * special API method would be even more effort, and would require explicit
3444 * support by every API client. It's better to hide the feature a bit. */
3445 if (strFrontend != "emergencystop")
3446 CheckComArgNotNull(aSession);
3447
3448 HRESULT rc = S_OK;
3449 if (strFrontend.isEmpty())
3450 {
3451 Bstr bstrFrontend;
3452 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3453 if (FAILED(rc))
3454 return rc;
3455 strFrontend = bstrFrontend;
3456 if (strFrontend.isEmpty())
3457 {
3458 ComPtr<ISystemProperties> systemProperties;
3459 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3460 if (FAILED(rc))
3461 return rc;
3462 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3463 if (FAILED(rc))
3464 return rc;
3465 strFrontend = bstrFrontend;
3466 }
3467 /* paranoia - emergencystop is not a valid default */
3468 if (strFrontend == "emergencystop")
3469 strFrontend = Utf8Str::Empty;
3470 }
3471 /* default frontend: Qt GUI */
3472 if (strFrontend.isEmpty())
3473 strFrontend = "GUI/Qt";
3474
3475 if (strFrontend != "emergencystop")
3476 {
3477 /* check the session state */
3478 SessionState_T state;
3479 rc = aSession->COMGETTER(State)(&state);
3480 if (FAILED(rc))
3481 return rc;
3482
3483 if (state != SessionState_Unlocked)
3484 return setError(VBOX_E_INVALID_OBJECT_STATE,
3485 tr("The given session is busy"));
3486
3487 /* get the IInternalSessionControl interface */
3488 ComPtr<IInternalSessionControl> control(aSession);
3489 ComAssertMsgRet(!control.isNull(),
3490 ("No IInternalSessionControl interface"),
3491 E_INVALIDARG);
3492
3493 /* get the teleporter enable state for the progress object init. */
3494 BOOL fTeleporterEnabled;
3495 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3496 if (FAILED(rc))
3497 return rc;
3498
3499 /* create a progress object */
3500 ComObjPtr<ProgressProxy> progress;
3501 progress.createObject();
3502 rc = progress->init(mParent,
3503 static_cast<IMachine*>(this),
3504 Bstr(tr("Starting VM")).raw(),
3505 TRUE /* aCancelable */,
3506 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3507 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3508 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3509 2 /* uFirstOperationWeight */,
3510 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3511
3512 if (SUCCEEDED(rc))
3513 {
3514 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3515 if (SUCCEEDED(rc))
3516 {
3517 aProgress = progress;
3518
3519 /* signal the client watcher thread */
3520 mParent->i_updateClientWatcher();
3521
3522 /* fire an event */
3523 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3524 }
3525 }
3526 }
3527 else
3528 {
3529 /* no progress object - either instant success or failure */
3530 aProgress = NULL;
3531
3532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3533
3534 if (mData->mSession.mState != SessionState_Locked)
3535 return setError(VBOX_E_INVALID_OBJECT_STATE,
3536 tr("The machine '%s' is not locked by a session"),
3537 mUserData->s.strName.c_str());
3538
3539 /* must have a VM process associated - do not kill normal API clients
3540 * with an open session */
3541 if (!Global::IsOnline(mData->mMachineState))
3542 return setError(VBOX_E_INVALID_OBJECT_STATE,
3543 tr("The machine '%s' does not have a VM process"),
3544 mUserData->s.strName.c_str());
3545
3546 /* forcibly terminate the VM process */
3547 if (mData->mSession.mPID != NIL_RTPROCESS)
3548 RTProcTerminate(mData->mSession.mPID);
3549
3550 /* signal the client watcher thread, as most likely the client has
3551 * been terminated */
3552 mParent->i_updateClientWatcher();
3553 }
3554
3555 return rc;
3556}
3557
3558HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3559{
3560 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3561 return setError(E_INVALIDARG,
3562 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3563 aPosition, SchemaDefs::MaxBootPosition);
3564
3565 if (aDevice == DeviceType_USB)
3566 return setError(E_NOTIMPL,
3567 tr("Booting from USB device is currently not supported"));
3568
3569 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3570
3571 HRESULT rc = i_checkStateDependency(MutableStateDep);
3572 if (FAILED(rc)) return rc;
3573
3574 i_setModified(IsModified_MachineData);
3575 mHWData.backup();
3576 mHWData->mBootOrder[aPosition - 1] = aDevice;
3577
3578 return S_OK;
3579}
3580
3581HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3582{
3583 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3584 return setError(E_INVALIDARG,
3585 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3586 aPosition, SchemaDefs::MaxBootPosition);
3587
3588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3589
3590 *aDevice = mHWData->mBootOrder[aPosition - 1];
3591
3592 return S_OK;
3593}
3594
3595HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3596 LONG aControllerPort,
3597 LONG aDevice,
3598 DeviceType_T aType,
3599 const ComPtr<IMedium> &aMedium)
3600{
3601 IMedium *aM = aMedium;
3602 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3603 aName.c_str(), aControllerPort, aDevice, aType, aM));
3604
3605 // request the host lock first, since might be calling Host methods for getting host drives;
3606 // next, protect the media tree all the while we're in here, as well as our member variables
3607 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3608 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3609
3610 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3611 if (FAILED(rc)) return rc;
3612
3613 /// @todo NEWMEDIA implicit machine registration
3614 if (!mData->mRegistered)
3615 return setError(VBOX_E_INVALID_OBJECT_STATE,
3616 tr("Cannot attach storage devices to an unregistered machine"));
3617
3618 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3619
3620 /* Check for an existing controller. */
3621 ComObjPtr<StorageController> ctl;
3622 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3623 if (FAILED(rc)) return rc;
3624
3625 StorageControllerType_T ctrlType;
3626 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3627 if (FAILED(rc))
3628 return setError(E_FAIL,
3629 tr("Could not get type of controller '%s'"),
3630 aName.c_str());
3631
3632 bool fSilent = false;
3633 Utf8Str strReconfig;
3634
3635 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3636 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3637 if ( mData->mMachineState == MachineState_Paused
3638 && strReconfig == "1")
3639 fSilent = true;
3640
3641 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3642 bool fHotplug = false;
3643 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3644 fHotplug = true;
3645
3646 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3647 return setError(VBOX_E_INVALID_VM_STATE,
3648 tr("Controller '%s' does not support hotplugging"),
3649 aName.c_str());
3650
3651 // check that the port and device are not out of range
3652 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3653 if (FAILED(rc)) return rc;
3654
3655 /* check if the device slot is already busy */
3656 MediumAttachment *pAttachTemp;
3657 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3658 aName,
3659 aControllerPort,
3660 aDevice)))
3661 {
3662 Medium *pMedium = pAttachTemp->i_getMedium();
3663 if (pMedium)
3664 {
3665 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3666 return setError(VBOX_E_OBJECT_IN_USE,
3667 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3668 pMedium->i_getLocationFull().c_str(),
3669 aControllerPort,
3670 aDevice,
3671 aName.c_str());
3672 }
3673 else
3674 return setError(VBOX_E_OBJECT_IN_USE,
3675 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3676 aControllerPort, aDevice, aName.c_str());
3677 }
3678
3679 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3680 if (aMedium && medium.isNull())
3681 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3682
3683 AutoCaller mediumCaller(medium);
3684 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3685
3686 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3687
3688 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3689 && !medium.isNull()
3690 )
3691 return setError(VBOX_E_OBJECT_IN_USE,
3692 tr("Medium '%s' is already attached to this virtual machine"),
3693 medium->i_getLocationFull().c_str());
3694
3695 if (!medium.isNull())
3696 {
3697 MediumType_T mtype = medium->i_getType();
3698 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3699 // For DVDs it's not written to the config file, so needs no global config
3700 // version bump. For floppies it's a new attribute "type", which is ignored
3701 // by older VirtualBox version, so needs no global config version bump either.
3702 // For hard disks this type is not accepted.
3703 if (mtype == MediumType_MultiAttach)
3704 {
3705 // This type is new with VirtualBox 4.0 and therefore requires settings
3706 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3707 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3708 // two reasons: The medium type is a property of the media registry tree, which
3709 // can reside in the global config file (for pre-4.0 media); we would therefore
3710 // possibly need to bump the global config version. We don't want to do that though
3711 // because that might make downgrading to pre-4.0 impossible.
3712 // As a result, we can only use these two new types if the medium is NOT in the
3713 // global registry:
3714 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3715 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3716 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3717 )
3718 return setError(VBOX_E_INVALID_OBJECT_STATE,
3719 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3720 "to machines that were created with VirtualBox 4.0 or later"),
3721 medium->i_getLocationFull().c_str());
3722 }
3723 }
3724
3725 bool fIndirect = false;
3726 if (!medium.isNull())
3727 fIndirect = medium->i_isReadOnly();
3728 bool associate = true;
3729
3730 do
3731 {
3732 if ( aType == DeviceType_HardDisk
3733 && mMediumAttachments.isBackedUp())
3734 {
3735 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3736
3737 /* check if the medium was attached to the VM before we started
3738 * changing attachments in which case the attachment just needs to
3739 * be restored */
3740 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3741 {
3742 AssertReturn(!fIndirect, E_FAIL);
3743
3744 /* see if it's the same bus/channel/device */
3745 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3746 {
3747 /* the simplest case: restore the whole attachment
3748 * and return, nothing else to do */
3749 mMediumAttachments->push_back(pAttachTemp);
3750
3751 /* Reattach the medium to the VM. */
3752 if (fHotplug || fSilent)
3753 {
3754 mediumLock.release();
3755 treeLock.release();
3756 alock.release();
3757
3758 MediumLockList *pMediumLockList(new MediumLockList());
3759
3760 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3761 medium /* pToLockWrite */,
3762 false /* fMediumLockWriteAll */,
3763 NULL,
3764 *pMediumLockList);
3765 alock.acquire();
3766 if (FAILED(rc))
3767 delete pMediumLockList;
3768 else
3769 {
3770 mData->mSession.mLockedMedia.Unlock();
3771 alock.release();
3772 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3773 mData->mSession.mLockedMedia.Lock();
3774 alock.acquire();
3775 }
3776 alock.release();
3777
3778 if (SUCCEEDED(rc))
3779 {
3780 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3781 /* Remove lock list in case of error. */
3782 if (FAILED(rc))
3783 {
3784 mData->mSession.mLockedMedia.Unlock();
3785 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3786 mData->mSession.mLockedMedia.Lock();
3787 }
3788 }
3789 }
3790
3791 return S_OK;
3792 }
3793
3794 /* bus/channel/device differ; we need a new attachment object,
3795 * but don't try to associate it again */
3796 associate = false;
3797 break;
3798 }
3799 }
3800
3801 /* go further only if the attachment is to be indirect */
3802 if (!fIndirect)
3803 break;
3804
3805 /* perform the so called smart attachment logic for indirect
3806 * attachments. Note that smart attachment is only applicable to base
3807 * hard disks. */
3808
3809 if (medium->i_getParent().isNull())
3810 {
3811 /* first, investigate the backup copy of the current hard disk
3812 * attachments to make it possible to re-attach existing diffs to
3813 * another device slot w/o losing their contents */
3814 if (mMediumAttachments.isBackedUp())
3815 {
3816 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3817
3818 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3819 uint32_t foundLevel = 0;
3820
3821 for (MediumAttachmentList::const_iterator
3822 it = oldAtts.begin();
3823 it != oldAtts.end();
3824 ++it)
3825 {
3826 uint32_t level = 0;
3827 MediumAttachment *pAttach = *it;
3828 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3829 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3830 if (pMedium.isNull())
3831 continue;
3832
3833 if (pMedium->i_getBase(&level) == medium)
3834 {
3835 /* skip the hard disk if its currently attached (we
3836 * cannot attach the same hard disk twice) */
3837 if (i_findAttachment(*mMediumAttachments.data(),
3838 pMedium))
3839 continue;
3840
3841 /* matched device, channel and bus (i.e. attached to the
3842 * same place) will win and immediately stop the search;
3843 * otherwise the attachment that has the youngest
3844 * descendant of medium will be used
3845 */
3846 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3847 {
3848 /* the simplest case: restore the whole attachment
3849 * and return, nothing else to do */
3850 mMediumAttachments->push_back(*it);
3851
3852 /* Reattach the medium to the VM. */
3853 if (fHotplug || fSilent)
3854 {
3855 mediumLock.release();
3856 treeLock.release();
3857 alock.release();
3858
3859 MediumLockList *pMediumLockList(new MediumLockList());
3860
3861 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3862 medium /* pToLockWrite */,
3863 false /* fMediumLockWriteAll */,
3864 NULL,
3865 *pMediumLockList);
3866 alock.acquire();
3867 if (FAILED(rc))
3868 delete pMediumLockList;
3869 else
3870 {
3871 mData->mSession.mLockedMedia.Unlock();
3872 alock.release();
3873 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3874 mData->mSession.mLockedMedia.Lock();
3875 alock.acquire();
3876 }
3877 alock.release();
3878
3879 if (SUCCEEDED(rc))
3880 {
3881 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3882 /* Remove lock list in case of error. */
3883 if (FAILED(rc))
3884 {
3885 mData->mSession.mLockedMedia.Unlock();
3886 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3887 mData->mSession.mLockedMedia.Lock();
3888 }
3889 }
3890 }
3891
3892 return S_OK;
3893 }
3894 else if ( foundIt == oldAtts.end()
3895 || level > foundLevel /* prefer younger */
3896 )
3897 {
3898 foundIt = it;
3899 foundLevel = level;
3900 }
3901 }
3902 }
3903
3904 if (foundIt != oldAtts.end())
3905 {
3906 /* use the previously attached hard disk */
3907 medium = (*foundIt)->i_getMedium();
3908 mediumCaller.attach(medium);
3909 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3910 mediumLock.attach(medium);
3911 /* not implicit, doesn't require association with this VM */
3912 fIndirect = false;
3913 associate = false;
3914 /* go right to the MediumAttachment creation */
3915 break;
3916 }
3917 }
3918
3919 /* must give up the medium lock and medium tree lock as below we
3920 * go over snapshots, which needs a lock with higher lock order. */
3921 mediumLock.release();
3922 treeLock.release();
3923
3924 /* then, search through snapshots for the best diff in the given
3925 * hard disk's chain to base the new diff on */
3926
3927 ComObjPtr<Medium> base;
3928 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3929 while (snap)
3930 {
3931 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3932
3933 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3934
3935 MediumAttachment *pAttachFound = NULL;
3936 uint32_t foundLevel = 0;
3937
3938 for (MediumAttachmentList::const_iterator
3939 it = snapAtts.begin();
3940 it != snapAtts.end();
3941 ++it)
3942 {
3943 MediumAttachment *pAttach = *it;
3944 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3945 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3946 if (pMedium.isNull())
3947 continue;
3948
3949 uint32_t level = 0;
3950 if (pMedium->i_getBase(&level) == medium)
3951 {
3952 /* matched device, channel and bus (i.e. attached to the
3953 * same place) will win and immediately stop the search;
3954 * otherwise the attachment that has the youngest
3955 * descendant of medium will be used
3956 */
3957 if ( pAttach->i_getDevice() == aDevice
3958 && pAttach->i_getPort() == aControllerPort
3959 && pAttach->i_getControllerName() == aName
3960 )
3961 {
3962 pAttachFound = pAttach;
3963 break;
3964 }
3965 else if ( !pAttachFound
3966 || level > foundLevel /* prefer younger */
3967 )
3968 {
3969 pAttachFound = pAttach;
3970 foundLevel = level;
3971 }
3972 }
3973 }
3974
3975 if (pAttachFound)
3976 {
3977 base = pAttachFound->i_getMedium();
3978 break;
3979 }
3980
3981 snap = snap->i_getParent();
3982 }
3983
3984 /* re-lock medium tree and the medium, as we need it below */
3985 treeLock.acquire();
3986 mediumLock.acquire();
3987
3988 /* found a suitable diff, use it as a base */
3989 if (!base.isNull())
3990 {
3991 medium = base;
3992 mediumCaller.attach(medium);
3993 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3994 mediumLock.attach(medium);
3995 }
3996 }
3997
3998 Utf8Str strFullSnapshotFolder;
3999 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4000
4001 ComObjPtr<Medium> diff;
4002 diff.createObject();
4003 // store this diff in the same registry as the parent
4004 Guid uuidRegistryParent;
4005 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4006 {
4007 // parent image has no registry: this can happen if we're attaching a new immutable
4008 // image that has not yet been attached (medium then points to the base and we're
4009 // creating the diff image for the immutable, and the parent is not yet registered);
4010 // put the parent in the machine registry then
4011 mediumLock.release();
4012 treeLock.release();
4013 alock.release();
4014 i_addMediumToRegistry(medium);
4015 alock.acquire();
4016 treeLock.acquire();
4017 mediumLock.acquire();
4018 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4019 }
4020 rc = diff->init(mParent,
4021 medium->i_getPreferredDiffFormat(),
4022 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4023 uuidRegistryParent,
4024 DeviceType_HardDisk);
4025 if (FAILED(rc)) return rc;
4026
4027 /* Apply the normal locking logic to the entire chain. */
4028 MediumLockList *pMediumLockList(new MediumLockList());
4029 mediumLock.release();
4030 treeLock.release();
4031 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4032 diff /* pToLockWrite */,
4033 false /* fMediumLockWriteAll */,
4034 medium,
4035 *pMediumLockList);
4036 treeLock.acquire();
4037 mediumLock.acquire();
4038 if (SUCCEEDED(rc))
4039 {
4040 mediumLock.release();
4041 treeLock.release();
4042 rc = pMediumLockList->Lock();
4043 treeLock.acquire();
4044 mediumLock.acquire();
4045 if (FAILED(rc))
4046 setError(rc,
4047 tr("Could not lock medium when creating diff '%s'"),
4048 diff->i_getLocationFull().c_str());
4049 else
4050 {
4051 /* will release the lock before the potentially lengthy
4052 * operation, so protect with the special state */
4053 MachineState_T oldState = mData->mMachineState;
4054 i_setMachineState(MachineState_SettingUp);
4055
4056 mediumLock.release();
4057 treeLock.release();
4058 alock.release();
4059
4060 rc = medium->i_createDiffStorage(diff,
4061 medium->i_getPreferredDiffVariant(),
4062 pMediumLockList,
4063 NULL /* aProgress */,
4064 true /* aWait */,
4065 false /* aNotify */);
4066
4067 alock.acquire();
4068 treeLock.acquire();
4069 mediumLock.acquire();
4070
4071 i_setMachineState(oldState);
4072 }
4073 }
4074
4075 /* Unlock the media and free the associated memory. */
4076 delete pMediumLockList;
4077
4078 if (FAILED(rc)) return rc;
4079
4080 /* use the created diff for the actual attachment */
4081 medium = diff;
4082 mediumCaller.attach(medium);
4083 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4084 mediumLock.attach(medium);
4085 }
4086 while (0);
4087
4088 ComObjPtr<MediumAttachment> attachment;
4089 attachment.createObject();
4090 rc = attachment->init(this,
4091 medium,
4092 aName,
4093 aControllerPort,
4094 aDevice,
4095 aType,
4096 fIndirect,
4097 false /* fPassthrough */,
4098 false /* fTempEject */,
4099 false /* fNonRotational */,
4100 false /* fDiscard */,
4101 fHotplug /* fHotPluggable */,
4102 Utf8Str::Empty);
4103 if (FAILED(rc)) return rc;
4104
4105 if (associate && !medium.isNull())
4106 {
4107 // as the last step, associate the medium to the VM
4108 rc = medium->i_addBackReference(mData->mUuid);
4109 // here we can fail because of Deleting, or being in process of creating a Diff
4110 if (FAILED(rc)) return rc;
4111
4112 mediumLock.release();
4113 treeLock.release();
4114 alock.release();
4115 i_addMediumToRegistry(medium);
4116 alock.acquire();
4117 treeLock.acquire();
4118 mediumLock.acquire();
4119 }
4120
4121 /* success: finally remember the attachment */
4122 i_setModified(IsModified_Storage);
4123 mMediumAttachments.backup();
4124 mMediumAttachments->push_back(attachment);
4125
4126 mediumLock.release();
4127 treeLock.release();
4128 alock.release();
4129
4130 if (fHotplug || fSilent)
4131 {
4132 if (!medium.isNull())
4133 {
4134 MediumLockList *pMediumLockList(new MediumLockList());
4135
4136 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4137 medium /* pToLockWrite */,
4138 false /* fMediumLockWriteAll */,
4139 NULL,
4140 *pMediumLockList);
4141 alock.acquire();
4142 if (FAILED(rc))
4143 delete pMediumLockList;
4144 else
4145 {
4146 mData->mSession.mLockedMedia.Unlock();
4147 alock.release();
4148 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4149 mData->mSession.mLockedMedia.Lock();
4150 alock.acquire();
4151 }
4152 alock.release();
4153 }
4154
4155 if (SUCCEEDED(rc))
4156 {
4157 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4158 /* Remove lock list in case of error. */
4159 if (FAILED(rc))
4160 {
4161 mData->mSession.mLockedMedia.Unlock();
4162 mData->mSession.mLockedMedia.Remove(attachment);
4163 mData->mSession.mLockedMedia.Lock();
4164 }
4165 }
4166 }
4167
4168 /* Save modified registries, but skip this machine as it's the caller's
4169 * job to save its settings like all other settings changes. */
4170 mParent->i_unmarkRegistryModified(i_getId());
4171 mParent->i_saveModifiedRegistries();
4172
4173 if (aM)
4174 mParent->i_onMediumConfigChanged(aM);
4175 return rc;
4176}
4177
4178HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4179 LONG aDevice)
4180{
4181 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4182 aName.c_str(), aControllerPort, aDevice));
4183
4184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4185
4186 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4187 if (FAILED(rc)) return rc;
4188
4189 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4190
4191 /* Check for an existing controller. */
4192 ComObjPtr<StorageController> ctl;
4193 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4194 if (FAILED(rc)) return rc;
4195
4196 StorageControllerType_T ctrlType;
4197 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4198 if (FAILED(rc))
4199 return setError(E_FAIL,
4200 tr("Could not get type of controller '%s'"),
4201 aName.c_str());
4202
4203 bool fSilent = false;
4204 Utf8Str strReconfig;
4205
4206 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4207 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4208 if ( mData->mMachineState == MachineState_Paused
4209 && strReconfig == "1")
4210 fSilent = true;
4211
4212 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4213 bool fHotplug = false;
4214 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4215 fHotplug = true;
4216
4217 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4218 return setError(VBOX_E_INVALID_VM_STATE,
4219 tr("Controller '%s' does not support hotplugging"),
4220 aName.c_str());
4221
4222 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4223 aName,
4224 aControllerPort,
4225 aDevice);
4226 if (!pAttach)
4227 return setError(VBOX_E_OBJECT_NOT_FOUND,
4228 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4229 aDevice, aControllerPort, aName.c_str());
4230
4231 if (fHotplug && !pAttach->i_getHotPluggable())
4232 return setError(VBOX_E_NOT_SUPPORTED,
4233 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4234 aDevice, aControllerPort, aName.c_str());
4235
4236 /*
4237 * The VM has to detach the device before we delete any implicit diffs.
4238 * If this fails we can roll back without loosing data.
4239 */
4240 if (fHotplug || fSilent)
4241 {
4242 alock.release();
4243 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4244 alock.acquire();
4245 }
4246 if (FAILED(rc)) return rc;
4247
4248 /* If we are here everything went well and we can delete the implicit now. */
4249 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4250
4251 alock.release();
4252
4253 /* Save modified registries, but skip this machine as it's the caller's
4254 * job to save its settings like all other settings changes. */
4255 mParent->i_unmarkRegistryModified(i_getId());
4256 mParent->i_saveModifiedRegistries();
4257
4258 return rc;
4259}
4260
4261HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4262 LONG aDevice, BOOL aPassthrough)
4263{
4264 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4265 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4266
4267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4268
4269 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4270 if (FAILED(rc)) return rc;
4271
4272 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4273
4274 /* Check for an existing controller. */
4275 ComObjPtr<StorageController> ctl;
4276 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4277 if (FAILED(rc)) return rc;
4278
4279 StorageControllerType_T ctrlType;
4280 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4281 if (FAILED(rc))
4282 return setError(E_FAIL,
4283 tr("Could not get type of controller '%s'"),
4284 aName.c_str());
4285
4286 bool fSilent = false;
4287 Utf8Str strReconfig;
4288
4289 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4290 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4291 if ( mData->mMachineState == MachineState_Paused
4292 && strReconfig == "1")
4293 fSilent = true;
4294
4295 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4296 bool fHotplug = false;
4297 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4298 fHotplug = true;
4299
4300 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4301 return setError(VBOX_E_INVALID_VM_STATE,
4302 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4303 aName.c_str());
4304
4305 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4306 aName,
4307 aControllerPort,
4308 aDevice);
4309 if (!pAttach)
4310 return setError(VBOX_E_OBJECT_NOT_FOUND,
4311 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4312 aDevice, aControllerPort, aName.c_str());
4313
4314
4315 i_setModified(IsModified_Storage);
4316 mMediumAttachments.backup();
4317
4318 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4319
4320 if (pAttach->i_getType() != DeviceType_DVD)
4321 return setError(E_INVALIDARG,
4322 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4323 aDevice, aControllerPort, aName.c_str());
4324 pAttach->i_updatePassthrough(!!aPassthrough);
4325
4326 attLock.release();
4327 alock.release();
4328 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4329
4330 return rc;
4331}
4332
4333HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4334 LONG aDevice, BOOL aTemporaryEject)
4335{
4336
4337 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4338 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4339
4340 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4341
4342 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4343 if (FAILED(rc)) return rc;
4344
4345 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4346 aName,
4347 aControllerPort,
4348 aDevice);
4349 if (!pAttach)
4350 return setError(VBOX_E_OBJECT_NOT_FOUND,
4351 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4352 aDevice, aControllerPort, aName.c_str());
4353
4354
4355 i_setModified(IsModified_Storage);
4356 mMediumAttachments.backup();
4357
4358 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4359
4360 if (pAttach->i_getType() != DeviceType_DVD)
4361 return setError(E_INVALIDARG,
4362 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4363 aDevice, aControllerPort, aName.c_str());
4364 pAttach->i_updateTempEject(!!aTemporaryEject);
4365
4366 return S_OK;
4367}
4368
4369HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4370 LONG aDevice, BOOL aNonRotational)
4371{
4372
4373 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4374 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4375
4376 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4377
4378 HRESULT rc = i_checkStateDependency(MutableStateDep);
4379 if (FAILED(rc)) return rc;
4380
4381 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4382
4383 if (Global::IsOnlineOrTransient(mData->mMachineState))
4384 return setError(VBOX_E_INVALID_VM_STATE,
4385 tr("Invalid machine state: %s"),
4386 Global::stringifyMachineState(mData->mMachineState));
4387
4388 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4389 aName,
4390 aControllerPort,
4391 aDevice);
4392 if (!pAttach)
4393 return setError(VBOX_E_OBJECT_NOT_FOUND,
4394 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4395 aDevice, aControllerPort, aName.c_str());
4396
4397
4398 i_setModified(IsModified_Storage);
4399 mMediumAttachments.backup();
4400
4401 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4402
4403 if (pAttach->i_getType() != DeviceType_HardDisk)
4404 return setError(E_INVALIDARG,
4405 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"),
4406 aDevice, aControllerPort, aName.c_str());
4407 pAttach->i_updateNonRotational(!!aNonRotational);
4408
4409 return S_OK;
4410}
4411
4412HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4413 LONG aDevice, BOOL aDiscard)
4414{
4415
4416 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4417 aName.c_str(), aControllerPort, aDevice, aDiscard));
4418
4419 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4420
4421 HRESULT rc = i_checkStateDependency(MutableStateDep);
4422 if (FAILED(rc)) return rc;
4423
4424 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4425
4426 if (Global::IsOnlineOrTransient(mData->mMachineState))
4427 return setError(VBOX_E_INVALID_VM_STATE,
4428 tr("Invalid machine state: %s"),
4429 Global::stringifyMachineState(mData->mMachineState));
4430
4431 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4432 aName,
4433 aControllerPort,
4434 aDevice);
4435 if (!pAttach)
4436 return setError(VBOX_E_OBJECT_NOT_FOUND,
4437 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4438 aDevice, aControllerPort, aName.c_str());
4439
4440
4441 i_setModified(IsModified_Storage);
4442 mMediumAttachments.backup();
4443
4444 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4445
4446 if (pAttach->i_getType() != DeviceType_HardDisk)
4447 return setError(E_INVALIDARG,
4448 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"),
4449 aDevice, aControllerPort, aName.c_str());
4450 pAttach->i_updateDiscard(!!aDiscard);
4451
4452 return S_OK;
4453}
4454
4455HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4456 LONG aDevice, BOOL aHotPluggable)
4457{
4458 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4459 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4460
4461 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4462
4463 HRESULT rc = i_checkStateDependency(MutableStateDep);
4464 if (FAILED(rc)) return rc;
4465
4466 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4467
4468 if (Global::IsOnlineOrTransient(mData->mMachineState))
4469 return setError(VBOX_E_INVALID_VM_STATE,
4470 tr("Invalid machine state: %s"),
4471 Global::stringifyMachineState(mData->mMachineState));
4472
4473 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4474 aName,
4475 aControllerPort,
4476 aDevice);
4477 if (!pAttach)
4478 return setError(VBOX_E_OBJECT_NOT_FOUND,
4479 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4480 aDevice, aControllerPort, aName.c_str());
4481
4482 /* Check for an existing controller. */
4483 ComObjPtr<StorageController> ctl;
4484 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4485 if (FAILED(rc)) return rc;
4486
4487 StorageControllerType_T ctrlType;
4488 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4489 if (FAILED(rc))
4490 return setError(E_FAIL,
4491 tr("Could not get type of controller '%s'"),
4492 aName.c_str());
4493
4494 if (!i_isControllerHotplugCapable(ctrlType))
4495 return setError(VBOX_E_NOT_SUPPORTED,
4496 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4497 aName.c_str());
4498
4499 i_setModified(IsModified_Storage);
4500 mMediumAttachments.backup();
4501
4502 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4503
4504 if (pAttach->i_getType() == DeviceType_Floppy)
4505 return setError(E_INVALIDARG,
4506 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"),
4507 aDevice, aControllerPort, aName.c_str());
4508 pAttach->i_updateHotPluggable(!!aHotPluggable);
4509
4510 return S_OK;
4511}
4512
4513HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4514 LONG aDevice)
4515{
4516 int rc = S_OK;
4517 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4518 aName.c_str(), aControllerPort, aDevice));
4519
4520 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4521
4522 return rc;
4523}
4524
4525HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4526 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4527{
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4529 aName.c_str(), aControllerPort, aDevice));
4530
4531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4532
4533 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4534 if (FAILED(rc)) return rc;
4535
4536 if (Global::IsOnlineOrTransient(mData->mMachineState))
4537 return setError(VBOX_E_INVALID_VM_STATE,
4538 tr("Invalid machine state: %s"),
4539 Global::stringifyMachineState(mData->mMachineState));
4540
4541 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4542 aName,
4543 aControllerPort,
4544 aDevice);
4545 if (!pAttach)
4546 return setError(VBOX_E_OBJECT_NOT_FOUND,
4547 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4548 aDevice, aControllerPort, aName.c_str());
4549
4550
4551 i_setModified(IsModified_Storage);
4552 mMediumAttachments.backup();
4553
4554 IBandwidthGroup *iB = aBandwidthGroup;
4555 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4556 if (aBandwidthGroup && group.isNull())
4557 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4558
4559 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4560
4561 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4562 if (strBandwidthGroupOld.isNotEmpty())
4563 {
4564 /* Get the bandwidth group object and release it - this must not fail. */
4565 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4566 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4567 Assert(SUCCEEDED(rc));
4568
4569 pBandwidthGroupOld->i_release();
4570 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4571 }
4572
4573 if (!group.isNull())
4574 {
4575 group->i_reference();
4576 pAttach->i_updateBandwidthGroup(group->i_getName());
4577 }
4578
4579 return S_OK;
4580}
4581
4582HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4583 LONG aControllerPort,
4584 LONG aDevice,
4585 DeviceType_T aType)
4586{
4587 HRESULT rc = S_OK;
4588
4589 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4590 aName.c_str(), aControllerPort, aDevice, aType));
4591
4592 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4593
4594 return rc;
4595}
4596
4597
4598HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4599 LONG aControllerPort,
4600 LONG aDevice,
4601 BOOL aForce)
4602{
4603 int rc = S_OK;
4604 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4605 aName.c_str(), aControllerPort, aForce));
4606
4607 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4608
4609 return rc;
4610}
4611
4612HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4613 LONG aControllerPort,
4614 LONG aDevice,
4615 const ComPtr<IMedium> &aMedium,
4616 BOOL aForce)
4617{
4618 int rc = S_OK;
4619 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4620 aName.c_str(), aControllerPort, aDevice, aForce));
4621
4622 // request the host lock first, since might be calling Host methods for getting host drives;
4623 // next, protect the media tree all the while we're in here, as well as our member variables
4624 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4625 this->lockHandle(),
4626 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4627
4628 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4629 aName,
4630 aControllerPort,
4631 aDevice);
4632 if (pAttach.isNull())
4633 return setError(VBOX_E_OBJECT_NOT_FOUND,
4634 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4635 aDevice, aControllerPort, aName.c_str());
4636
4637 /* Remember previously mounted medium. The medium before taking the
4638 * backup is not necessarily the same thing. */
4639 ComObjPtr<Medium> oldmedium;
4640 oldmedium = pAttach->i_getMedium();
4641
4642 IMedium *iM = aMedium;
4643 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4644 if (aMedium && pMedium.isNull())
4645 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4646
4647 AutoCaller mediumCaller(pMedium);
4648 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4649
4650 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4651 if (pMedium)
4652 {
4653 DeviceType_T mediumType = pAttach->i_getType();
4654 switch (mediumType)
4655 {
4656 case DeviceType_DVD:
4657 case DeviceType_Floppy:
4658 break;
4659
4660 default:
4661 return setError(VBOX_E_INVALID_OBJECT_STATE,
4662 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4663 aControllerPort,
4664 aDevice,
4665 aName.c_str());
4666 }
4667 }
4668
4669 i_setModified(IsModified_Storage);
4670 mMediumAttachments.backup();
4671
4672 {
4673 // The backup operation makes the pAttach reference point to the
4674 // old settings. Re-get the correct reference.
4675 pAttach = i_findAttachment(*mMediumAttachments.data(),
4676 aName,
4677 aControllerPort,
4678 aDevice);
4679 if (!oldmedium.isNull())
4680 oldmedium->i_removeBackReference(mData->mUuid);
4681 if (!pMedium.isNull())
4682 {
4683 pMedium->i_addBackReference(mData->mUuid);
4684
4685 mediumLock.release();
4686 multiLock.release();
4687 i_addMediumToRegistry(pMedium);
4688 multiLock.acquire();
4689 mediumLock.acquire();
4690 }
4691
4692 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4693 pAttach->i_updateMedium(pMedium);
4694 }
4695
4696 i_setModified(IsModified_Storage);
4697
4698 mediumLock.release();
4699 multiLock.release();
4700 rc = i_onMediumChange(pAttach, aForce);
4701 multiLock.acquire();
4702 mediumLock.acquire();
4703
4704 /* On error roll back this change only. */
4705 if (FAILED(rc))
4706 {
4707 if (!pMedium.isNull())
4708 pMedium->i_removeBackReference(mData->mUuid);
4709 pAttach = i_findAttachment(*mMediumAttachments.data(),
4710 aName,
4711 aControllerPort,
4712 aDevice);
4713 /* If the attachment is gone in the meantime, bail out. */
4714 if (pAttach.isNull())
4715 return rc;
4716 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4717 if (!oldmedium.isNull())
4718 oldmedium->i_addBackReference(mData->mUuid);
4719 pAttach->i_updateMedium(oldmedium);
4720 }
4721
4722 mediumLock.release();
4723 multiLock.release();
4724
4725 /* Save modified registries, but skip this machine as it's the caller's
4726 * job to save its settings like all other settings changes. */
4727 mParent->i_unmarkRegistryModified(i_getId());
4728 mParent->i_saveModifiedRegistries();
4729
4730 return rc;
4731}
4732HRESULT Machine::getMedium(const com::Utf8Str &aName,
4733 LONG aControllerPort,
4734 LONG aDevice,
4735 ComPtr<IMedium> &aMedium)
4736{
4737 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4738 aName.c_str(), aControllerPort, aDevice));
4739
4740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4741
4742 aMedium = NULL;
4743
4744 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4745 aName,
4746 aControllerPort,
4747 aDevice);
4748 if (pAttach.isNull())
4749 return setError(VBOX_E_OBJECT_NOT_FOUND,
4750 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4751 aDevice, aControllerPort, aName.c_str());
4752
4753 aMedium = pAttach->i_getMedium();
4754
4755 return S_OK;
4756}
4757
4758HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4759{
4760
4761 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4762
4763 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4764
4765 return S_OK;
4766}
4767
4768HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4769{
4770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4771
4772 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4773
4774 return S_OK;
4775}
4776
4777HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4778{
4779 /* Do not assert if slot is out of range, just return the advertised
4780 status. testdriver/vbox.py triggers this in logVmInfo. */
4781 if (aSlot >= mNetworkAdapters.size())
4782 return setError(E_INVALIDARG,
4783 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4784 aSlot, mNetworkAdapters.size());
4785
4786 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4787
4788 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4789
4790 return S_OK;
4791}
4792
4793HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4794{
4795 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4796
4797 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4798 size_t i = 0;
4799 for (settings::StringsMap::const_iterator
4800 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4801 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4802 ++it, ++i)
4803 aKeys[i] = it->first;
4804
4805 return S_OK;
4806}
4807
4808 /**
4809 * @note Locks this object for reading.
4810 */
4811HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4812 com::Utf8Str &aValue)
4813{
4814 /* start with nothing found */
4815 aValue = "";
4816
4817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4818
4819 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4820 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4821 // found:
4822 aValue = it->second; // source is a Utf8Str
4823
4824 /* return the result to caller (may be empty) */
4825 return S_OK;
4826}
4827
4828 /**
4829 * @note Locks mParent for writing + this object for writing.
4830 */
4831HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4832{
4833 /* Because non-ASCII characters in aKey have caused problems in the settings
4834 * they are rejected unless the key should be deleted. */
4835 if (!aValue.isEmpty())
4836 {
4837 for (size_t i = 0; i < aKey.length(); ++i)
4838 {
4839 char ch = aKey[i];
4840 if (!RTLocCIsPrint(ch))
4841 return E_INVALIDARG;
4842 }
4843 }
4844
4845 Utf8Str strOldValue; // empty
4846
4847 // locking note: we only hold the read lock briefly to look up the old value,
4848 // then release it and call the onExtraCanChange callbacks. There is a small
4849 // chance of a race insofar as the callback might be called twice if two callers
4850 // change the same key at the same time, but that's a much better solution
4851 // than the deadlock we had here before. The actual changing of the extradata
4852 // is then performed under the write lock and race-free.
4853
4854 // look up the old value first; if nothing has changed then we need not do anything
4855 {
4856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4857
4858 // For snapshots don't even think about allowing changes, extradata
4859 // is global for a machine, so there is nothing snapshot specific.
4860 if (i_isSnapshotMachine())
4861 return setError(VBOX_E_INVALID_VM_STATE,
4862 tr("Cannot set extradata for a snapshot"));
4863
4864 // check if the right IMachine instance is used
4865 if (mData->mRegistered && !i_isSessionMachine())
4866 return setError(VBOX_E_INVALID_VM_STATE,
4867 tr("Cannot set extradata for an immutable machine"));
4868
4869 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4870 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4871 strOldValue = it->second;
4872 }
4873
4874 bool fChanged;
4875 if ((fChanged = (strOldValue != aValue)))
4876 {
4877 // ask for permission from all listeners outside the locks;
4878 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4879 // lock to copy the list of callbacks to invoke
4880 Bstr error;
4881 Bstr bstrValue(aValue);
4882
4883 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4884 {
4885 const char *sep = error.isEmpty() ? "" : ": ";
4886 CBSTR err = error.raw();
4887 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4888 return setError(E_ACCESSDENIED,
4889 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4890 aKey.c_str(),
4891 aValue.c_str(),
4892 sep,
4893 err);
4894 }
4895
4896 // data is changing and change not vetoed: then write it out under the lock
4897 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4898
4899 if (aValue.isEmpty())
4900 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4901 else
4902 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4903 // creates a new key if needed
4904
4905 bool fNeedsGlobalSaveSettings = false;
4906 // This saving of settings is tricky: there is no "old state" for the
4907 // extradata items at all (unlike all other settings), so the old/new
4908 // settings comparison would give a wrong result!
4909 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4910
4911 if (fNeedsGlobalSaveSettings)
4912 {
4913 // save the global settings; for that we should hold only the VirtualBox lock
4914 alock.release();
4915 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4916 mParent->i_saveSettings();
4917 }
4918 }
4919
4920 // fire notification outside the lock
4921 if (fChanged)
4922 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4923
4924 return S_OK;
4925}
4926
4927HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4928{
4929 aProgress = NULL;
4930 NOREF(aSettingsFilePath);
4931 ReturnComNotImplemented();
4932}
4933
4934HRESULT Machine::saveSettings()
4935{
4936 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4937
4938 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4939 if (FAILED(rc)) return rc;
4940
4941 /* the settings file path may never be null */
4942 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4943
4944 /* save all VM data excluding snapshots */
4945 bool fNeedsGlobalSaveSettings = false;
4946 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4947 mlock.release();
4948
4949 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4950 {
4951 // save the global settings; for that we should hold only the VirtualBox lock
4952 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4953 rc = mParent->i_saveSettings();
4954 }
4955
4956 return rc;
4957}
4958
4959
4960HRESULT Machine::discardSettings()
4961{
4962 /*
4963 * We need to take the machine list lock here as well as the machine one
4964 * or we'll get into trouble should any media stuff require rolling back.
4965 *
4966 * Details:
4967 *
4968 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4969 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4970 * 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]
4971 * 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
4972 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4973 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4974 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4975 * 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
4976 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4977 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4978 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4979 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4980 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4981 * 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]
4982 * 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] (*)
4983 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4984 * 0:005> k
4985 * # Child-SP RetAddr Call Site
4986 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4987 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4988 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4989 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4990 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4991 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4992 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4993 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4994 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4995 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4996 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4997 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4998 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4999 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
5000 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
5001 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
5002 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
5003 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
5004 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
5005 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
5006 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
5007 *
5008 */
5009 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
5010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5011
5012 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5013 if (FAILED(rc)) return rc;
5014
5015 /*
5016 * during this rollback, the session will be notified if data has
5017 * been actually changed
5018 */
5019 i_rollback(true /* aNotify */);
5020
5021 return S_OK;
5022}
5023
5024/** @note Locks objects! */
5025HRESULT Machine::unregister(AutoCaller &autoCaller,
5026 CleanupMode_T aCleanupMode,
5027 std::vector<ComPtr<IMedium> > &aMedia)
5028{
5029 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5030
5031 Guid id(i_getId());
5032
5033 if (mData->mSession.mState != SessionState_Unlocked)
5034 return setError(VBOX_E_INVALID_OBJECT_STATE,
5035 tr("Cannot unregister the machine '%s' while it is locked"),
5036 mUserData->s.strName.c_str());
5037
5038 // wait for state dependents to drop to zero
5039 i_ensureNoStateDependencies();
5040
5041 if (!mData->mAccessible)
5042 {
5043 // inaccessible maschines can only be unregistered; uninitialize ourselves
5044 // here because currently there may be no unregistered that are inaccessible
5045 // (this state combination is not supported). Note releasing the caller and
5046 // leaving the lock before calling uninit()
5047 alock.release();
5048 autoCaller.release();
5049
5050 uninit();
5051
5052 mParent->i_unregisterMachine(this, id);
5053 // calls VirtualBox::i_saveSettings()
5054
5055 return S_OK;
5056 }
5057
5058 HRESULT rc = S_OK;
5059
5060 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5061 // discard saved state
5062 if (mData->mMachineState == MachineState_Saved)
5063 {
5064 // add the saved state file to the list of files the caller should delete
5065 Assert(!mSSData->strStateFilePath.isEmpty());
5066 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5067
5068 mSSData->strStateFilePath.setNull();
5069
5070 // unconditionally set the machine state to powered off, we now
5071 // know no session has locked the machine
5072 mData->mMachineState = MachineState_PoweredOff;
5073 }
5074
5075 size_t cSnapshots = 0;
5076 if (mData->mFirstSnapshot)
5077 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5078 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5079 // fail now before we start detaching media
5080 return setError(VBOX_E_INVALID_OBJECT_STATE,
5081 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5082 mUserData->s.strName.c_str(), cSnapshots);
5083
5084 // This list collects the medium objects from all medium attachments
5085 // which we will detach from the machine and its snapshots, in a specific
5086 // order which allows for closing all media without getting "media in use"
5087 // errors, simply by going through the list from the front to the back:
5088 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5089 // and must be closed before the parent media from the snapshots, or closing the parents
5090 // will fail because they still have children);
5091 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5092 // the root ("first") snapshot of the machine.
5093 MediaList llMedia;
5094
5095 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5096 && mMediumAttachments->size()
5097 )
5098 {
5099 // we have media attachments: detach them all and add the Medium objects to our list
5100 if (aCleanupMode != CleanupMode_UnregisterOnly)
5101 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5102 else
5103 return setError(VBOX_E_INVALID_OBJECT_STATE,
5104 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5105 mUserData->s.strName.c_str(), mMediumAttachments->size());
5106 }
5107
5108 if (cSnapshots)
5109 {
5110 // add the media from the medium attachments of the snapshots to llMedia
5111 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5112 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5113 // into the children first
5114
5115 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5116 MachineState_T oldState = mData->mMachineState;
5117 mData->mMachineState = MachineState_DeletingSnapshot;
5118
5119 // make a copy of the first snapshot so the refcount does not drop to 0
5120 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5121 // because of the AutoCaller voodoo)
5122 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5123
5124 // GO!
5125 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5126
5127 mData->mMachineState = oldState;
5128 }
5129
5130 if (FAILED(rc))
5131 {
5132 i_rollbackMedia();
5133 return rc;
5134 }
5135
5136 // commit all the media changes made above
5137 i_commitMedia();
5138
5139 mData->mRegistered = false;
5140
5141 // machine lock no longer needed
5142 alock.release();
5143
5144 // return media to caller
5145 aMedia.resize(llMedia.size());
5146 size_t i = 0;
5147 for (MediaList::const_iterator
5148 it = llMedia.begin();
5149 it != llMedia.end();
5150 ++it, ++i)
5151 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5152
5153 mParent->i_unregisterMachine(this, id);
5154 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5155
5156 return S_OK;
5157}
5158
5159/**
5160 * Task record for deleting a machine config.
5161 */
5162class Machine::DeleteConfigTask
5163 : public Machine::Task
5164{
5165public:
5166 DeleteConfigTask(Machine *m,
5167 Progress *p,
5168 const Utf8Str &t,
5169 const RTCList<ComPtr<IMedium> > &llMediums,
5170 const StringsList &llFilesToDelete)
5171 : Task(m, p, t),
5172 m_llMediums(llMediums),
5173 m_llFilesToDelete(llFilesToDelete)
5174 {}
5175
5176private:
5177 void handler()
5178 {
5179 try
5180 {
5181 m_pMachine->i_deleteConfigHandler(*this);
5182 }
5183 catch (...)
5184 {
5185 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5186 }
5187 }
5188
5189 RTCList<ComPtr<IMedium> > m_llMediums;
5190 StringsList m_llFilesToDelete;
5191
5192 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5193};
5194
5195/**
5196 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5197 * SessionMachine::taskHandler().
5198 *
5199 * @note Locks this object for writing.
5200 *
5201 * @param task
5202 * @return
5203 */
5204void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5205{
5206 LogFlowThisFuncEnter();
5207
5208 AutoCaller autoCaller(this);
5209 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5210 if (FAILED(autoCaller.rc()))
5211 {
5212 /* we might have been uninitialized because the session was accidentally
5213 * closed by the client, so don't assert */
5214 HRESULT rc = setError(E_FAIL,
5215 tr("The session has been accidentally closed"));
5216 task.m_pProgress->i_notifyComplete(rc);
5217 LogFlowThisFuncLeave();
5218 return;
5219 }
5220
5221 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5222
5223 HRESULT rc = S_OK;
5224
5225 try
5226 {
5227 ULONG uLogHistoryCount = 3;
5228 ComPtr<ISystemProperties> systemProperties;
5229 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5230 if (FAILED(rc)) throw rc;
5231
5232 if (!systemProperties.isNull())
5233 {
5234 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5235 if (FAILED(rc)) throw rc;
5236 }
5237
5238 MachineState_T oldState = mData->mMachineState;
5239 i_setMachineState(MachineState_SettingUp);
5240 alock.release();
5241 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5242 {
5243 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5244 {
5245 AutoCaller mac(pMedium);
5246 if (FAILED(mac.rc())) throw mac.rc();
5247 Utf8Str strLocation = pMedium->i_getLocationFull();
5248 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5249 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5250 if (FAILED(rc)) throw rc;
5251 }
5252 if (pMedium->i_isMediumFormatFile())
5253 {
5254 ComPtr<IProgress> pProgress2;
5255 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5256 if (FAILED(rc)) throw rc;
5257 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5258 if (FAILED(rc)) throw rc;
5259 }
5260
5261 /* Close the medium, deliberately without checking the return
5262 * code, and without leaving any trace in the error info, as
5263 * a failure here is a very minor issue, which shouldn't happen
5264 * as above we even managed to delete the medium. */
5265 {
5266 ErrorInfoKeeper eik;
5267 pMedium->Close();
5268 }
5269 }
5270 i_setMachineState(oldState);
5271 alock.acquire();
5272
5273 // delete the files pushed on the task list by Machine::Delete()
5274 // (this includes saved states of the machine and snapshots and
5275 // medium storage files from the IMedium list passed in, and the
5276 // machine XML file)
5277 for (StringsList::const_iterator
5278 it = task.m_llFilesToDelete.begin();
5279 it != task.m_llFilesToDelete.end();
5280 ++it)
5281 {
5282 const Utf8Str &strFile = *it;
5283 LogFunc(("Deleting file %s\n", strFile.c_str()));
5284 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5285 if (FAILED(rc)) throw rc;
5286
5287 int vrc = RTFileDelete(strFile.c_str());
5288 if (RT_FAILURE(vrc))
5289 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5290 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5291 }
5292
5293 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5294 if (FAILED(rc)) throw rc;
5295
5296 /* delete the settings only when the file actually exists */
5297 if (mData->pMachineConfigFile->fileExists())
5298 {
5299 /* Delete any backup or uncommitted XML files. Ignore failures.
5300 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5301 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5302 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5303 RTFileDelete(otherXml.c_str());
5304 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5305 RTFileDelete(otherXml.c_str());
5306
5307 /* delete the Logs folder, nothing important should be left
5308 * there (we don't check for errors because the user might have
5309 * some private files there that we don't want to delete) */
5310 Utf8Str logFolder;
5311 getLogFolder(logFolder);
5312 Assert(logFolder.length());
5313 if (RTDirExists(logFolder.c_str()))
5314 {
5315 /* Delete all VBox.log[.N] files from the Logs folder
5316 * (this must be in sync with the rotation logic in
5317 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5318 * files that may have been created by the GUI. */
5319 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5320 logFolder.c_str(), RTPATH_DELIMITER);
5321 RTFileDelete(log.c_str());
5322 log = Utf8StrFmt("%s%cVBox.png",
5323 logFolder.c_str(), RTPATH_DELIMITER);
5324 RTFileDelete(log.c_str());
5325 for (int i = uLogHistoryCount; i > 0; i--)
5326 {
5327 log = Utf8StrFmt("%s%cVBox.log.%d",
5328 logFolder.c_str(), RTPATH_DELIMITER, i);
5329 RTFileDelete(log.c_str());
5330 log = Utf8StrFmt("%s%cVBox.png.%d",
5331 logFolder.c_str(), RTPATH_DELIMITER, i);
5332 RTFileDelete(log.c_str());
5333 }
5334#if defined(RT_OS_WINDOWS)
5335 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5336 RTFileDelete(log.c_str());
5337 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5338 RTFileDelete(log.c_str());
5339#endif
5340
5341 RTDirRemove(logFolder.c_str());
5342 }
5343
5344 /* delete the Snapshots folder, nothing important should be left
5345 * there (we don't check for errors because the user might have
5346 * some private files there that we don't want to delete) */
5347 Utf8Str strFullSnapshotFolder;
5348 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5349 Assert(!strFullSnapshotFolder.isEmpty());
5350 if (RTDirExists(strFullSnapshotFolder.c_str()))
5351 RTDirRemove(strFullSnapshotFolder.c_str());
5352
5353 // delete the directory that contains the settings file, but only
5354 // if it matches the VM name
5355 Utf8Str settingsDir;
5356 if (i_isInOwnDir(&settingsDir))
5357 RTDirRemove(settingsDir.c_str());
5358 }
5359
5360 alock.release();
5361
5362 mParent->i_saveModifiedRegistries();
5363 }
5364 catch (HRESULT aRC) { rc = aRC; }
5365
5366 task.m_pProgress->i_notifyComplete(rc);
5367
5368 LogFlowThisFuncLeave();
5369}
5370
5371HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5372{
5373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5374
5375 HRESULT rc = i_checkStateDependency(MutableStateDep);
5376 if (FAILED(rc)) return rc;
5377
5378 if (mData->mRegistered)
5379 return setError(VBOX_E_INVALID_VM_STATE,
5380 tr("Cannot delete settings of a registered machine"));
5381
5382 // collect files to delete
5383 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5384 if (mData->pMachineConfigFile->fileExists())
5385 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5386
5387 RTCList<ComPtr<IMedium> > llMediums;
5388 for (size_t i = 0; i < aMedia.size(); ++i)
5389 {
5390 IMedium *pIMedium(aMedia[i]);
5391 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5392 if (pMedium.isNull())
5393 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5394 SafeArray<BSTR> ids;
5395 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5396 if (FAILED(rc)) return rc;
5397 /* At this point the medium should not have any back references
5398 * anymore. If it has it is attached to another VM and *must* not
5399 * deleted. */
5400 if (ids.size() < 1)
5401 llMediums.append(pMedium);
5402 }
5403
5404 ComObjPtr<Progress> pProgress;
5405 pProgress.createObject();
5406 rc = pProgress->init(i_getVirtualBox(),
5407 static_cast<IMachine*>(this) /* aInitiator */,
5408 tr("Deleting files"),
5409 true /* fCancellable */,
5410 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5411 tr("Collecting file inventory"));
5412 if (FAILED(rc))
5413 return rc;
5414
5415 /* create and start the task on a separate thread (note that it will not
5416 * start working until we release alock) */
5417 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5418 rc = pTask->createThread();
5419 if (FAILED(rc))
5420 return rc;
5421
5422 pProgress.queryInterfaceTo(aProgress.asOutParam());
5423
5424 LogFlowFuncLeave();
5425
5426 return S_OK;
5427}
5428
5429HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5430{
5431 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5432
5433 ComObjPtr<Snapshot> pSnapshot;
5434 HRESULT rc;
5435
5436 if (aNameOrId.isEmpty())
5437 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5438 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5439 else
5440 {
5441 Guid uuid(aNameOrId);
5442 if (uuid.isValid())
5443 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5444 else
5445 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5446 }
5447 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5448
5449 return rc;
5450}
5451
5452HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5453 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5454{
5455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5456
5457 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5458 if (FAILED(rc)) return rc;
5459
5460 ComObjPtr<SharedFolder> sharedFolder;
5461 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5462 if (SUCCEEDED(rc))
5463 return setError(VBOX_E_OBJECT_IN_USE,
5464 tr("Shared folder named '%s' already exists"),
5465 aName.c_str());
5466
5467 sharedFolder.createObject();
5468 rc = sharedFolder->init(i_getMachine(),
5469 aName,
5470 aHostPath,
5471 !!aWritable,
5472 !!aAutomount,
5473 aAutoMountPoint,
5474 true /* fFailOnError */);
5475 if (FAILED(rc)) return rc;
5476
5477 i_setModified(IsModified_SharedFolders);
5478 mHWData.backup();
5479 mHWData->mSharedFolders.push_back(sharedFolder);
5480
5481 /* inform the direct session if any */
5482 alock.release();
5483 i_onSharedFolderChange();
5484
5485 return S_OK;
5486}
5487
5488HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5489{
5490 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5491
5492 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5493 if (FAILED(rc)) return rc;
5494
5495 ComObjPtr<SharedFolder> sharedFolder;
5496 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5497 if (FAILED(rc)) return rc;
5498
5499 i_setModified(IsModified_SharedFolders);
5500 mHWData.backup();
5501 mHWData->mSharedFolders.remove(sharedFolder);
5502
5503 /* inform the direct session if any */
5504 alock.release();
5505 i_onSharedFolderChange();
5506
5507 return S_OK;
5508}
5509
5510HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5511{
5512 /* start with No */
5513 *aCanShow = FALSE;
5514
5515 ComPtr<IInternalSessionControl> directControl;
5516 {
5517 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5518
5519 if (mData->mSession.mState != SessionState_Locked)
5520 return setError(VBOX_E_INVALID_VM_STATE,
5521 tr("Machine is not locked for session (session state: %s)"),
5522 Global::stringifySessionState(mData->mSession.mState));
5523
5524 if (mData->mSession.mLockType == LockType_VM)
5525 directControl = mData->mSession.mDirectControl;
5526 }
5527
5528 /* ignore calls made after #OnSessionEnd() is called */
5529 if (!directControl)
5530 return S_OK;
5531
5532 LONG64 dummy;
5533 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5534}
5535
5536HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5537{
5538 ComPtr<IInternalSessionControl> directControl;
5539 {
5540 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5541
5542 if (mData->mSession.mState != SessionState_Locked)
5543 return setError(E_FAIL,
5544 tr("Machine is not locked for session (session state: %s)"),
5545 Global::stringifySessionState(mData->mSession.mState));
5546
5547 if (mData->mSession.mLockType == LockType_VM)
5548 directControl = mData->mSession.mDirectControl;
5549 }
5550
5551 /* ignore calls made after #OnSessionEnd() is called */
5552 if (!directControl)
5553 return S_OK;
5554
5555 BOOL dummy;
5556 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5557}
5558
5559#ifdef VBOX_WITH_GUEST_PROPS
5560/**
5561 * Look up a guest property in VBoxSVC's internal structures.
5562 */
5563HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5564 com::Utf8Str &aValue,
5565 LONG64 *aTimestamp,
5566 com::Utf8Str &aFlags) const
5567{
5568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5569
5570 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5571 if (it != mHWData->mGuestProperties.end())
5572 {
5573 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5574 aValue = it->second.strValue;
5575 *aTimestamp = it->second.mTimestamp;
5576 GuestPropWriteFlags(it->second.mFlags, szFlags);
5577 aFlags = Utf8Str(szFlags);
5578 }
5579
5580 return S_OK;
5581}
5582
5583/**
5584 * Query the VM that a guest property belongs to for the property.
5585 * @returns E_ACCESSDENIED if the VM process is not available or not
5586 * currently handling queries and the lookup should then be done in
5587 * VBoxSVC.
5588 */
5589HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5590 com::Utf8Str &aValue,
5591 LONG64 *aTimestamp,
5592 com::Utf8Str &aFlags) const
5593{
5594 HRESULT rc = S_OK;
5595 BSTR bValue = NULL;
5596 BSTR bFlags = NULL;
5597
5598 ComPtr<IInternalSessionControl> directControl;
5599 {
5600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5601 if (mData->mSession.mLockType == LockType_VM)
5602 directControl = mData->mSession.mDirectControl;
5603 }
5604
5605 /* ignore calls made after #OnSessionEnd() is called */
5606 if (!directControl)
5607 rc = E_ACCESSDENIED;
5608 else
5609 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5610 0 /* accessMode */,
5611 &bValue, aTimestamp, &bFlags);
5612
5613 aValue = bValue;
5614 aFlags = bFlags;
5615
5616 return rc;
5617}
5618#endif // VBOX_WITH_GUEST_PROPS
5619
5620HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5621 com::Utf8Str &aValue,
5622 LONG64 *aTimestamp,
5623 com::Utf8Str &aFlags)
5624{
5625#ifndef VBOX_WITH_GUEST_PROPS
5626 ReturnComNotImplemented();
5627#else // VBOX_WITH_GUEST_PROPS
5628
5629 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5630
5631 if (rc == E_ACCESSDENIED)
5632 /* The VM is not running or the service is not (yet) accessible */
5633 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5634 return rc;
5635#endif // VBOX_WITH_GUEST_PROPS
5636}
5637
5638HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5639{
5640 LONG64 dummyTimestamp;
5641 com::Utf8Str dummyFlags;
5642 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5643 return rc;
5644
5645}
5646HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5647{
5648 com::Utf8Str dummyFlags;
5649 com::Utf8Str dummyValue;
5650 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5651 return rc;
5652}
5653
5654#ifdef VBOX_WITH_GUEST_PROPS
5655/**
5656 * Set a guest property in VBoxSVC's internal structures.
5657 */
5658HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5659 const com::Utf8Str &aFlags, bool fDelete)
5660{
5661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5662 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5663 if (FAILED(rc)) return rc;
5664
5665 try
5666 {
5667 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5668 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5669 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5670
5671 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5672 if (it == mHWData->mGuestProperties.end())
5673 {
5674 if (!fDelete)
5675 {
5676 i_setModified(IsModified_MachineData);
5677 mHWData.backupEx();
5678
5679 RTTIMESPEC time;
5680 HWData::GuestProperty prop;
5681 prop.strValue = Bstr(aValue).raw();
5682 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5683 prop.mFlags = fFlags;
5684 mHWData->mGuestProperties[aName] = prop;
5685 }
5686 }
5687 else
5688 {
5689 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5690 {
5691 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5692 }
5693 else
5694 {
5695 i_setModified(IsModified_MachineData);
5696 mHWData.backupEx();
5697
5698 /* The backupEx() operation invalidates our iterator,
5699 * so get a new one. */
5700 it = mHWData->mGuestProperties.find(aName);
5701 Assert(it != mHWData->mGuestProperties.end());
5702
5703 if (!fDelete)
5704 {
5705 RTTIMESPEC time;
5706 it->second.strValue = aValue;
5707 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5708 it->second.mFlags = fFlags;
5709 }
5710 else
5711 mHWData->mGuestProperties.erase(it);
5712 }
5713 }
5714
5715 if (SUCCEEDED(rc))
5716 {
5717 alock.release();
5718
5719 mParent->i_onGuestPropertyChange(mData->mUuid,
5720 Bstr(aName).raw(),
5721 Bstr(aValue).raw(),
5722 Bstr(aFlags).raw());
5723 }
5724 }
5725 catch (std::bad_alloc &)
5726 {
5727 rc = E_OUTOFMEMORY;
5728 }
5729
5730 return rc;
5731}
5732
5733/**
5734 * Set a property on the VM that that property belongs to.
5735 * @returns E_ACCESSDENIED if the VM process is not available or not
5736 * currently handling queries and the setting should then be done in
5737 * VBoxSVC.
5738 */
5739HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5740 const com::Utf8Str &aFlags, bool fDelete)
5741{
5742 HRESULT rc;
5743
5744 try
5745 {
5746 ComPtr<IInternalSessionControl> directControl;
5747 {
5748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5749 if (mData->mSession.mLockType == LockType_VM)
5750 directControl = mData->mSession.mDirectControl;
5751 }
5752
5753 BSTR dummy = NULL; /* will not be changed (setter) */
5754 LONG64 dummy64;
5755 if (!directControl)
5756 rc = E_ACCESSDENIED;
5757 else
5758 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5759 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5760 fDelete? 2: 1 /* accessMode */,
5761 &dummy, &dummy64, &dummy);
5762 }
5763 catch (std::bad_alloc &)
5764 {
5765 rc = E_OUTOFMEMORY;
5766 }
5767
5768 return rc;
5769}
5770#endif // VBOX_WITH_GUEST_PROPS
5771
5772HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5773 const com::Utf8Str &aFlags)
5774{
5775#ifndef VBOX_WITH_GUEST_PROPS
5776 ReturnComNotImplemented();
5777#else // VBOX_WITH_GUEST_PROPS
5778 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5779 if (rc == E_ACCESSDENIED)
5780 /* The VM is not running or the service is not (yet) accessible */
5781 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5782 return rc;
5783#endif // VBOX_WITH_GUEST_PROPS
5784}
5785
5786HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5787{
5788 return setGuestProperty(aProperty, aValue, "");
5789}
5790
5791HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5792{
5793#ifndef VBOX_WITH_GUEST_PROPS
5794 ReturnComNotImplemented();
5795#else // VBOX_WITH_GUEST_PROPS
5796 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5797 if (rc == E_ACCESSDENIED)
5798 /* The VM is not running or the service is not (yet) accessible */
5799 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5800 return rc;
5801#endif // VBOX_WITH_GUEST_PROPS
5802}
5803
5804#ifdef VBOX_WITH_GUEST_PROPS
5805/**
5806 * Enumerate the guest properties in VBoxSVC's internal structures.
5807 */
5808HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5809 std::vector<com::Utf8Str> &aNames,
5810 std::vector<com::Utf8Str> &aValues,
5811 std::vector<LONG64> &aTimestamps,
5812 std::vector<com::Utf8Str> &aFlags)
5813{
5814 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5815 Utf8Str strPatterns(aPatterns);
5816
5817 /*
5818 * Look for matching patterns and build up a list.
5819 */
5820 HWData::GuestPropertyMap propMap;
5821 for (HWData::GuestPropertyMap::const_iterator
5822 it = mHWData->mGuestProperties.begin();
5823 it != mHWData->mGuestProperties.end();
5824 ++it)
5825 {
5826 if ( strPatterns.isEmpty()
5827 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5828 RTSTR_MAX,
5829 it->first.c_str(),
5830 RTSTR_MAX,
5831 NULL)
5832 )
5833 propMap.insert(*it);
5834 }
5835
5836 alock.release();
5837
5838 /*
5839 * And build up the arrays for returning the property information.
5840 */
5841 size_t cEntries = propMap.size();
5842
5843 aNames.resize(cEntries);
5844 aValues.resize(cEntries);
5845 aTimestamps.resize(cEntries);
5846 aFlags.resize(cEntries);
5847
5848 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5849 size_t i = 0;
5850 for (HWData::GuestPropertyMap::const_iterator
5851 it = propMap.begin();
5852 it != propMap.end();
5853 ++it, ++i)
5854 {
5855 aNames[i] = it->first;
5856 aValues[i] = it->second.strValue;
5857 aTimestamps[i] = it->second.mTimestamp;
5858 GuestPropWriteFlags(it->second.mFlags, szFlags);
5859 aFlags[i] = Utf8Str(szFlags);
5860 }
5861
5862 return S_OK;
5863}
5864
5865/**
5866 * Enumerate the properties managed by a VM.
5867 * @returns E_ACCESSDENIED if the VM process is not available or not
5868 * currently handling queries and the setting should then be done in
5869 * VBoxSVC.
5870 */
5871HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5872 std::vector<com::Utf8Str> &aNames,
5873 std::vector<com::Utf8Str> &aValues,
5874 std::vector<LONG64> &aTimestamps,
5875 std::vector<com::Utf8Str> &aFlags)
5876{
5877 HRESULT rc;
5878 ComPtr<IInternalSessionControl> directControl;
5879 {
5880 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5881 if (mData->mSession.mLockType == LockType_VM)
5882 directControl = mData->mSession.mDirectControl;
5883 }
5884
5885 com::SafeArray<BSTR> bNames;
5886 com::SafeArray<BSTR> bValues;
5887 com::SafeArray<LONG64> bTimestamps;
5888 com::SafeArray<BSTR> bFlags;
5889
5890 if (!directControl)
5891 rc = E_ACCESSDENIED;
5892 else
5893 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5894 ComSafeArrayAsOutParam(bNames),
5895 ComSafeArrayAsOutParam(bValues),
5896 ComSafeArrayAsOutParam(bTimestamps),
5897 ComSafeArrayAsOutParam(bFlags));
5898 size_t i;
5899 aNames.resize(bNames.size());
5900 for (i = 0; i < bNames.size(); ++i)
5901 aNames[i] = Utf8Str(bNames[i]);
5902 aValues.resize(bValues.size());
5903 for (i = 0; i < bValues.size(); ++i)
5904 aValues[i] = Utf8Str(bValues[i]);
5905 aTimestamps.resize(bTimestamps.size());
5906 for (i = 0; i < bTimestamps.size(); ++i)
5907 aTimestamps[i] = bTimestamps[i];
5908 aFlags.resize(bFlags.size());
5909 for (i = 0; i < bFlags.size(); ++i)
5910 aFlags[i] = Utf8Str(bFlags[i]);
5911
5912 return rc;
5913}
5914#endif // VBOX_WITH_GUEST_PROPS
5915HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5916 std::vector<com::Utf8Str> &aNames,
5917 std::vector<com::Utf8Str> &aValues,
5918 std::vector<LONG64> &aTimestamps,
5919 std::vector<com::Utf8Str> &aFlags)
5920{
5921#ifndef VBOX_WITH_GUEST_PROPS
5922 ReturnComNotImplemented();
5923#else // VBOX_WITH_GUEST_PROPS
5924
5925 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5926
5927 if (rc == E_ACCESSDENIED)
5928 /* The VM is not running or the service is not (yet) accessible */
5929 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5930 return rc;
5931#endif // VBOX_WITH_GUEST_PROPS
5932}
5933
5934HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5935 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5936{
5937 MediumAttachmentList atts;
5938
5939 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5940 if (FAILED(rc)) return rc;
5941
5942 aMediumAttachments.resize(atts.size());
5943 size_t i = 0;
5944 for (MediumAttachmentList::const_iterator
5945 it = atts.begin();
5946 it != atts.end();
5947 ++it, ++i)
5948 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5949
5950 return S_OK;
5951}
5952
5953HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5954 LONG aControllerPort,
5955 LONG aDevice,
5956 ComPtr<IMediumAttachment> &aAttachment)
5957{
5958 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5959 aName.c_str(), aControllerPort, aDevice));
5960
5961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5962
5963 aAttachment = NULL;
5964
5965 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5966 aName,
5967 aControllerPort,
5968 aDevice);
5969 if (pAttach.isNull())
5970 return setError(VBOX_E_OBJECT_NOT_FOUND,
5971 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5972 aDevice, aControllerPort, aName.c_str());
5973
5974 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5975
5976 return S_OK;
5977}
5978
5979
5980HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5981 StorageBus_T aConnectionType,
5982 ComPtr<IStorageController> &aController)
5983{
5984 if ( (aConnectionType <= StorageBus_Null)
5985 || (aConnectionType > StorageBus_PCIe))
5986 return setError(E_INVALIDARG,
5987 tr("Invalid connection type: %d"),
5988 aConnectionType);
5989
5990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5991
5992 HRESULT rc = i_checkStateDependency(MutableStateDep);
5993 if (FAILED(rc)) return rc;
5994
5995 /* try to find one with the name first. */
5996 ComObjPtr<StorageController> ctrl;
5997
5998 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5999 if (SUCCEEDED(rc))
6000 return setError(VBOX_E_OBJECT_IN_USE,
6001 tr("Storage controller named '%s' already exists"),
6002 aName.c_str());
6003
6004 ctrl.createObject();
6005
6006 /* get a new instance number for the storage controller */
6007 ULONG ulInstance = 0;
6008 bool fBootable = true;
6009 for (StorageControllerList::const_iterator
6010 it = mStorageControllers->begin();
6011 it != mStorageControllers->end();
6012 ++it)
6013 {
6014 if ((*it)->i_getStorageBus() == aConnectionType)
6015 {
6016 ULONG ulCurInst = (*it)->i_getInstance();
6017
6018 if (ulCurInst >= ulInstance)
6019 ulInstance = ulCurInst + 1;
6020
6021 /* Only one controller of each type can be marked as bootable. */
6022 if ((*it)->i_getBootable())
6023 fBootable = false;
6024 }
6025 }
6026
6027 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6028 if (FAILED(rc)) return rc;
6029
6030 i_setModified(IsModified_Storage);
6031 mStorageControllers.backup();
6032 mStorageControllers->push_back(ctrl);
6033
6034 ctrl.queryInterfaceTo(aController.asOutParam());
6035
6036 /* inform the direct session if any */
6037 alock.release();
6038 i_onStorageControllerChange();
6039
6040 return S_OK;
6041}
6042
6043HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6044 ComPtr<IStorageController> &aStorageController)
6045{
6046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6047
6048 ComObjPtr<StorageController> ctrl;
6049
6050 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6051 if (SUCCEEDED(rc))
6052 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6053
6054 return rc;
6055}
6056
6057HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6058 ULONG aInstance,
6059 ComPtr<IStorageController> &aStorageController)
6060{
6061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6062
6063 for (StorageControllerList::const_iterator
6064 it = mStorageControllers->begin();
6065 it != mStorageControllers->end();
6066 ++it)
6067 {
6068 if ( (*it)->i_getStorageBus() == aConnectionType
6069 && (*it)->i_getInstance() == aInstance)
6070 {
6071 (*it).queryInterfaceTo(aStorageController.asOutParam());
6072 return S_OK;
6073 }
6074 }
6075
6076 return setError(VBOX_E_OBJECT_NOT_FOUND,
6077 tr("Could not find a storage controller with instance number '%lu'"),
6078 aInstance);
6079}
6080
6081HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6082{
6083 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6084
6085 HRESULT rc = i_checkStateDependency(MutableStateDep);
6086 if (FAILED(rc)) return rc;
6087
6088 ComObjPtr<StorageController> ctrl;
6089
6090 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6091 if (SUCCEEDED(rc))
6092 {
6093 /* Ensure that only one controller of each type is marked as bootable. */
6094 if (aBootable == TRUE)
6095 {
6096 for (StorageControllerList::const_iterator
6097 it = mStorageControllers->begin();
6098 it != mStorageControllers->end();
6099 ++it)
6100 {
6101 ComObjPtr<StorageController> aCtrl = (*it);
6102
6103 if ( (aCtrl->i_getName() != aName)
6104 && aCtrl->i_getBootable() == TRUE
6105 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6106 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6107 {
6108 aCtrl->i_setBootable(FALSE);
6109 break;
6110 }
6111 }
6112 }
6113
6114 if (SUCCEEDED(rc))
6115 {
6116 ctrl->i_setBootable(aBootable);
6117 i_setModified(IsModified_Storage);
6118 }
6119 }
6120
6121 if (SUCCEEDED(rc))
6122 {
6123 /* inform the direct session if any */
6124 alock.release();
6125 i_onStorageControllerChange();
6126 }
6127
6128 return rc;
6129}
6130
6131HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6132{
6133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6134
6135 HRESULT rc = i_checkStateDependency(MutableStateDep);
6136 if (FAILED(rc)) return rc;
6137
6138 ComObjPtr<StorageController> ctrl;
6139 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6140 if (FAILED(rc)) return rc;
6141
6142 {
6143 /* find all attached devices to the appropriate storage controller and detach them all */
6144 // make a temporary list because detachDevice invalidates iterators into
6145 // mMediumAttachments
6146 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6147
6148 for (MediumAttachmentList::const_iterator
6149 it = llAttachments2.begin();
6150 it != llAttachments2.end();
6151 ++it)
6152 {
6153 MediumAttachment *pAttachTemp = *it;
6154
6155 AutoCaller localAutoCaller(pAttachTemp);
6156 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6157
6158 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6159
6160 if (pAttachTemp->i_getControllerName() == aName)
6161 {
6162 rc = i_detachDevice(pAttachTemp, alock, NULL);
6163 if (FAILED(rc)) return rc;
6164 }
6165 }
6166 }
6167
6168 /* We can remove it now. */
6169 i_setModified(IsModified_Storage);
6170 mStorageControllers.backup();
6171
6172 ctrl->i_unshare();
6173
6174 mStorageControllers->remove(ctrl);
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onStorageControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6184 ComPtr<IUSBController> &aController)
6185{
6186 if ( (aType <= USBControllerType_Null)
6187 || (aType >= USBControllerType_Last))
6188 return setError(E_INVALIDARG,
6189 tr("Invalid USB controller type: %d"),
6190 aType);
6191
6192 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6193
6194 HRESULT rc = i_checkStateDependency(MutableStateDep);
6195 if (FAILED(rc)) return rc;
6196
6197 /* try to find one with the same type first. */
6198 ComObjPtr<USBController> ctrl;
6199
6200 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6201 if (SUCCEEDED(rc))
6202 return setError(VBOX_E_OBJECT_IN_USE,
6203 tr("USB controller named '%s' already exists"),
6204 aName.c_str());
6205
6206 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6207 ULONG maxInstances;
6208 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6209 if (FAILED(rc))
6210 return rc;
6211
6212 ULONG cInstances = i_getUSBControllerCountByType(aType);
6213 if (cInstances >= maxInstances)
6214 return setError(E_INVALIDARG,
6215 tr("Too many USB controllers of this type"));
6216
6217 ctrl.createObject();
6218
6219 rc = ctrl->init(this, aName, aType);
6220 if (FAILED(rc)) return rc;
6221
6222 i_setModified(IsModified_USB);
6223 mUSBControllers.backup();
6224 mUSBControllers->push_back(ctrl);
6225
6226 ctrl.queryInterfaceTo(aController.asOutParam());
6227
6228 /* inform the direct session if any */
6229 alock.release();
6230 i_onUSBControllerChange();
6231
6232 return S_OK;
6233}
6234
6235HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6236{
6237 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6238
6239 ComObjPtr<USBController> ctrl;
6240
6241 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6242 if (SUCCEEDED(rc))
6243 ctrl.queryInterfaceTo(aController.asOutParam());
6244
6245 return rc;
6246}
6247
6248HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6249 ULONG *aControllers)
6250{
6251 if ( (aType <= USBControllerType_Null)
6252 || (aType >= USBControllerType_Last))
6253 return setError(E_INVALIDARG,
6254 tr("Invalid USB controller type: %d"),
6255 aType);
6256
6257 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6258
6259 ComObjPtr<USBController> ctrl;
6260
6261 *aControllers = i_getUSBControllerCountByType(aType);
6262
6263 return S_OK;
6264}
6265
6266HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6267{
6268
6269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6270
6271 HRESULT rc = i_checkStateDependency(MutableStateDep);
6272 if (FAILED(rc)) return rc;
6273
6274 ComObjPtr<USBController> ctrl;
6275 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6276 if (FAILED(rc)) return rc;
6277
6278 i_setModified(IsModified_USB);
6279 mUSBControllers.backup();
6280
6281 ctrl->i_unshare();
6282
6283 mUSBControllers->remove(ctrl);
6284
6285 /* inform the direct session if any */
6286 alock.release();
6287 i_onUSBControllerChange();
6288
6289 return S_OK;
6290}
6291
6292HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6293 ULONG *aOriginX,
6294 ULONG *aOriginY,
6295 ULONG *aWidth,
6296 ULONG *aHeight,
6297 BOOL *aEnabled)
6298{
6299 uint32_t u32OriginX= 0;
6300 uint32_t u32OriginY= 0;
6301 uint32_t u32Width = 0;
6302 uint32_t u32Height = 0;
6303 uint16_t u16Flags = 0;
6304
6305 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6306 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6307 if (RT_FAILURE(vrc))
6308 {
6309#ifdef RT_OS_WINDOWS
6310 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6311 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6312 * So just assign fEnable to TRUE again.
6313 * The right fix would be to change GUI API wrappers to make sure that parameters
6314 * are changed only if API succeeds.
6315 */
6316 *aEnabled = TRUE;
6317#endif
6318 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6319 tr("Saved guest size is not available (%Rrc)"),
6320 vrc);
6321 }
6322
6323 *aOriginX = u32OriginX;
6324 *aOriginY = u32OriginY;
6325 *aWidth = u32Width;
6326 *aHeight = u32Height;
6327 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6328
6329 return S_OK;
6330}
6331
6332HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6333 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6334{
6335 if (aScreenId != 0)
6336 return E_NOTIMPL;
6337
6338 if ( aBitmapFormat != BitmapFormat_BGR0
6339 && aBitmapFormat != BitmapFormat_BGRA
6340 && aBitmapFormat != BitmapFormat_RGBA
6341 && aBitmapFormat != BitmapFormat_PNG)
6342 return setError(E_NOTIMPL,
6343 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6344
6345 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6346
6347 uint8_t *pu8Data = NULL;
6348 uint32_t cbData = 0;
6349 uint32_t u32Width = 0;
6350 uint32_t u32Height = 0;
6351
6352 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6353
6354 if (RT_FAILURE(vrc))
6355 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6356 tr("Saved thumbnail data is not available (%Rrc)"),
6357 vrc);
6358
6359 HRESULT hr = S_OK;
6360
6361 *aWidth = u32Width;
6362 *aHeight = u32Height;
6363
6364 if (cbData > 0)
6365 {
6366 /* Convert pixels to the format expected by the API caller. */
6367 if (aBitmapFormat == BitmapFormat_BGR0)
6368 {
6369 /* [0] B, [1] G, [2] R, [3] 0. */
6370 aData.resize(cbData);
6371 memcpy(&aData.front(), pu8Data, cbData);
6372 }
6373 else if (aBitmapFormat == BitmapFormat_BGRA)
6374 {
6375 /* [0] B, [1] G, [2] R, [3] A. */
6376 aData.resize(cbData);
6377 for (uint32_t i = 0; i < cbData; i += 4)
6378 {
6379 aData[i] = pu8Data[i];
6380 aData[i + 1] = pu8Data[i + 1];
6381 aData[i + 2] = pu8Data[i + 2];
6382 aData[i + 3] = 0xff;
6383 }
6384 }
6385 else if (aBitmapFormat == BitmapFormat_RGBA)
6386 {
6387 /* [0] R, [1] G, [2] B, [3] A. */
6388 aData.resize(cbData);
6389 for (uint32_t i = 0; i < cbData; i += 4)
6390 {
6391 aData[i] = pu8Data[i + 2];
6392 aData[i + 1] = pu8Data[i + 1];
6393 aData[i + 2] = pu8Data[i];
6394 aData[i + 3] = 0xff;
6395 }
6396 }
6397 else if (aBitmapFormat == BitmapFormat_PNG)
6398 {
6399 uint8_t *pu8PNG = NULL;
6400 uint32_t cbPNG = 0;
6401 uint32_t cxPNG = 0;
6402 uint32_t cyPNG = 0;
6403
6404 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6405
6406 if (RT_SUCCESS(vrc))
6407 {
6408 aData.resize(cbPNG);
6409 if (cbPNG)
6410 memcpy(&aData.front(), pu8PNG, cbPNG);
6411 }
6412 else
6413 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6414 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6415 vrc);
6416
6417 RTMemFree(pu8PNG);
6418 }
6419 }
6420
6421 freeSavedDisplayScreenshot(pu8Data);
6422
6423 return hr;
6424}
6425
6426HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6427 ULONG *aWidth,
6428 ULONG *aHeight,
6429 std::vector<BitmapFormat_T> &aBitmapFormats)
6430{
6431 if (aScreenId != 0)
6432 return E_NOTIMPL;
6433
6434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6435
6436 uint8_t *pu8Data = NULL;
6437 uint32_t cbData = 0;
6438 uint32_t u32Width = 0;
6439 uint32_t u32Height = 0;
6440
6441 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6442
6443 if (RT_FAILURE(vrc))
6444 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6445 tr("Saved screenshot data is not available (%Rrc)"),
6446 vrc);
6447
6448 *aWidth = u32Width;
6449 *aHeight = u32Height;
6450 aBitmapFormats.resize(1);
6451 aBitmapFormats[0] = BitmapFormat_PNG;
6452
6453 freeSavedDisplayScreenshot(pu8Data);
6454
6455 return S_OK;
6456}
6457
6458HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6459 BitmapFormat_T aBitmapFormat,
6460 ULONG *aWidth,
6461 ULONG *aHeight,
6462 std::vector<BYTE> &aData)
6463{
6464 if (aScreenId != 0)
6465 return E_NOTIMPL;
6466
6467 if (aBitmapFormat != BitmapFormat_PNG)
6468 return E_NOTIMPL;
6469
6470 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6471
6472 uint8_t *pu8Data = NULL;
6473 uint32_t cbData = 0;
6474 uint32_t u32Width = 0;
6475 uint32_t u32Height = 0;
6476
6477 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6478
6479 if (RT_FAILURE(vrc))
6480 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6481 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6482 vrc);
6483
6484 *aWidth = u32Width;
6485 *aHeight = u32Height;
6486
6487 aData.resize(cbData);
6488 if (cbData)
6489 memcpy(&aData.front(), pu8Data, cbData);
6490
6491 freeSavedDisplayScreenshot(pu8Data);
6492
6493 return S_OK;
6494}
6495
6496HRESULT Machine::hotPlugCPU(ULONG aCpu)
6497{
6498 HRESULT rc = S_OK;
6499 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6500
6501 if (!mHWData->mCPUHotPlugEnabled)
6502 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6503
6504 if (aCpu >= mHWData->mCPUCount)
6505 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6506
6507 if (mHWData->mCPUAttached[aCpu])
6508 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6509
6510 alock.release();
6511 rc = i_onCPUChange(aCpu, false);
6512 alock.acquire();
6513 if (FAILED(rc)) return rc;
6514
6515 i_setModified(IsModified_MachineData);
6516 mHWData.backup();
6517 mHWData->mCPUAttached[aCpu] = true;
6518
6519 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6520 if (Global::IsOnline(mData->mMachineState))
6521 i_saveSettings(NULL);
6522
6523 return S_OK;
6524}
6525
6526HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6527{
6528 HRESULT rc = S_OK;
6529
6530 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6531
6532 if (!mHWData->mCPUHotPlugEnabled)
6533 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6534
6535 if (aCpu >= SchemaDefs::MaxCPUCount)
6536 return setError(E_INVALIDARG,
6537 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6538 SchemaDefs::MaxCPUCount);
6539
6540 if (!mHWData->mCPUAttached[aCpu])
6541 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6542
6543 /* CPU 0 can't be detached */
6544 if (aCpu == 0)
6545 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6546
6547 alock.release();
6548 rc = i_onCPUChange(aCpu, true);
6549 alock.acquire();
6550 if (FAILED(rc)) return rc;
6551
6552 i_setModified(IsModified_MachineData);
6553 mHWData.backup();
6554 mHWData->mCPUAttached[aCpu] = false;
6555
6556 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6557 if (Global::IsOnline(mData->mMachineState))
6558 i_saveSettings(NULL);
6559
6560 return S_OK;
6561}
6562
6563HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6564{
6565 *aAttached = false;
6566
6567 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6568
6569 /* If hotplug is enabled the CPU is always enabled. */
6570 if (!mHWData->mCPUHotPlugEnabled)
6571 {
6572 if (aCpu < mHWData->mCPUCount)
6573 *aAttached = true;
6574 }
6575 else
6576 {
6577 if (aCpu < SchemaDefs::MaxCPUCount)
6578 *aAttached = mHWData->mCPUAttached[aCpu];
6579 }
6580
6581 return S_OK;
6582}
6583
6584HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6585{
6586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6587
6588 Utf8Str log = i_getLogFilename(aIdx);
6589 if (!RTFileExists(log.c_str()))
6590 log.setNull();
6591 aFilename = log;
6592
6593 return S_OK;
6594}
6595
6596HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6597{
6598 if (aSize < 0)
6599 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6600
6601 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6602
6603 HRESULT rc = S_OK;
6604 Utf8Str log = i_getLogFilename(aIdx);
6605
6606 /* do not unnecessarily hold the lock while doing something which does
6607 * not need the lock and potentially takes a long time. */
6608 alock.release();
6609
6610 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6611 * keeps the SOAP reply size under 1M for the webservice (we're using
6612 * base64 encoded strings for binary data for years now, avoiding the
6613 * expansion of each byte array element to approx. 25 bytes of XML. */
6614 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6615 aData.resize(cbData);
6616
6617 RTFILE LogFile;
6618 int vrc = RTFileOpen(&LogFile, log.c_str(),
6619 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6620 if (RT_SUCCESS(vrc))
6621 {
6622 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6623 if (RT_SUCCESS(vrc))
6624 aData.resize(cbData);
6625 else
6626 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6627 tr("Could not read log file '%s' (%Rrc)"),
6628 log.c_str(), vrc);
6629 RTFileClose(LogFile);
6630 }
6631 else
6632 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6633 tr("Could not open log file '%s' (%Rrc)"),
6634 log.c_str(), vrc);
6635
6636 if (FAILED(rc))
6637 aData.resize(0);
6638
6639 return rc;
6640}
6641
6642
6643/**
6644 * Currently this method doesn't attach device to the running VM,
6645 * just makes sure it's plugged on next VM start.
6646 */
6647HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6648{
6649 // lock scope
6650 {
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 HRESULT rc = i_checkStateDependency(MutableStateDep);
6654 if (FAILED(rc)) return rc;
6655
6656 ChipsetType_T aChipset = ChipsetType_PIIX3;
6657 COMGETTER(ChipsetType)(&aChipset);
6658
6659 if (aChipset != ChipsetType_ICH9)
6660 {
6661 return setError(E_INVALIDARG,
6662 tr("Host PCI attachment only supported with ICH9 chipset"));
6663 }
6664
6665 // check if device with this host PCI address already attached
6666 for (HWData::PCIDeviceAssignmentList::const_iterator
6667 it = mHWData->mPCIDeviceAssignments.begin();
6668 it != mHWData->mPCIDeviceAssignments.end();
6669 ++it)
6670 {
6671 LONG iHostAddress = -1;
6672 ComPtr<PCIDeviceAttachment> pAttach;
6673 pAttach = *it;
6674 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6675 if (iHostAddress == aHostAddress)
6676 return setError(E_INVALIDARG,
6677 tr("Device with host PCI address already attached to this VM"));
6678 }
6679
6680 ComObjPtr<PCIDeviceAttachment> pda;
6681 char name[32];
6682
6683 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6684 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6685 pda.createObject();
6686 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6687 i_setModified(IsModified_MachineData);
6688 mHWData.backup();
6689 mHWData->mPCIDeviceAssignments.push_back(pda);
6690 }
6691
6692 return S_OK;
6693}
6694
6695/**
6696 * Currently this method doesn't detach device from the running VM,
6697 * just makes sure it's not plugged on next VM start.
6698 */
6699HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6700{
6701 ComObjPtr<PCIDeviceAttachment> pAttach;
6702 bool fRemoved = false;
6703 HRESULT rc;
6704
6705 // lock scope
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 rc = i_checkStateDependency(MutableStateDep);
6710 if (FAILED(rc)) return rc;
6711
6712 for (HWData::PCIDeviceAssignmentList::const_iterator
6713 it = mHWData->mPCIDeviceAssignments.begin();
6714 it != mHWData->mPCIDeviceAssignments.end();
6715 ++it)
6716 {
6717 LONG iHostAddress = -1;
6718 pAttach = *it;
6719 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6720 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6721 {
6722 i_setModified(IsModified_MachineData);
6723 mHWData.backup();
6724 mHWData->mPCIDeviceAssignments.remove(pAttach);
6725 fRemoved = true;
6726 break;
6727 }
6728 }
6729 }
6730
6731
6732 /* Fire event outside of the lock */
6733 if (fRemoved)
6734 {
6735 Assert(!pAttach.isNull());
6736 ComPtr<IEventSource> es;
6737 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6738 Assert(SUCCEEDED(rc));
6739 Bstr mid;
6740 rc = this->COMGETTER(Id)(mid.asOutParam());
6741 Assert(SUCCEEDED(rc));
6742 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6743 }
6744
6745 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6746 tr("No host PCI device %08x attached"),
6747 aHostAddress
6748 );
6749}
6750
6751HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6756 size_t i = 0;
6757 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6758 it = mHWData->mPCIDeviceAssignments.begin();
6759 it != mHWData->mPCIDeviceAssignments.end();
6760 ++it, ++i)
6761 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6767{
6768 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6778
6779 return S_OK;
6780}
6781
6782HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6783{
6784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6785 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6786 if (SUCCEEDED(hrc))
6787 {
6788 hrc = mHWData.backupEx();
6789 if (SUCCEEDED(hrc))
6790 {
6791 i_setModified(IsModified_MachineData);
6792 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6793 }
6794 }
6795 return hrc;
6796}
6797
6798HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6799{
6800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6802 return S_OK;
6803}
6804
6805HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6806{
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6815 if (SUCCEEDED(hrc))
6816 i_setModified(IsModified_MachineData);
6817 }
6818 }
6819 return hrc;
6820}
6821
6822HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6823{
6824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6825
6826 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6832{
6833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6834 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6835 if (SUCCEEDED(hrc))
6836 {
6837 hrc = mHWData.backupEx();
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6842 }
6843 }
6844 return hrc;
6845}
6846
6847HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6848{
6849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6850
6851 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6857{
6858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6861 if ( SUCCEEDED(hrc)
6862 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6863 {
6864 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6865 int vrc;
6866
6867 if (aAutostartEnabled)
6868 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6869 else
6870 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6879 }
6880 }
6881 else if (vrc == VERR_NOT_SUPPORTED)
6882 hrc = setError(VBOX_E_NOT_SUPPORTED,
6883 tr("The VM autostart feature is not supported on this platform"));
6884 else if (vrc == VERR_PATH_NOT_FOUND)
6885 hrc = setError(E_FAIL,
6886 tr("The path to the autostart database is not set"));
6887 else
6888 hrc = setError(E_UNEXPECTED,
6889 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6890 aAutostartEnabled ? "Adding" : "Removing",
6891 mUserData->s.strName.c_str(), vrc);
6892 }
6893 return hrc;
6894}
6895
6896HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6897{
6898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6899
6900 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6901
6902 return S_OK;
6903}
6904
6905HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6906{
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6909 if (SUCCEEDED(hrc))
6910 {
6911 hrc = mHWData.backupEx();
6912 if (SUCCEEDED(hrc))
6913 {
6914 i_setModified(IsModified_MachineData);
6915 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6916 }
6917 }
6918 return hrc;
6919}
6920
6921HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6922{
6923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6924
6925 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6926
6927 return S_OK;
6928}
6929
6930HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6931{
6932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6933 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6934 if ( SUCCEEDED(hrc)
6935 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6936 {
6937 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6938 int vrc;
6939
6940 if (aAutostopType != AutostopType_Disabled)
6941 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6942 else
6943 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6944
6945 if (RT_SUCCESS(vrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 i_setModified(IsModified_MachineData);
6951 mHWData->mAutostart.enmAutostopType = aAutostopType;
6952 }
6953 }
6954 else if (vrc == VERR_NOT_SUPPORTED)
6955 hrc = setError(VBOX_E_NOT_SUPPORTED,
6956 tr("The VM autostop feature is not supported on this platform"));
6957 else if (vrc == VERR_PATH_NOT_FOUND)
6958 hrc = setError(E_FAIL,
6959 tr("The path to the autostart database is not set"));
6960 else
6961 hrc = setError(E_UNEXPECTED,
6962 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6963 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6964 mUserData->s.strName.c_str(), vrc);
6965 }
6966 return hrc;
6967}
6968
6969HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6970{
6971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 aDefaultFrontend = mHWData->mDefaultFrontend;
6974
6975 return S_OK;
6976}
6977
6978HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6979{
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6982 if (SUCCEEDED(hrc))
6983 {
6984 hrc = mHWData.backupEx();
6985 if (SUCCEEDED(hrc))
6986 {
6987 i_setModified(IsModified_MachineData);
6988 mHWData->mDefaultFrontend = aDefaultFrontend;
6989 }
6990 }
6991 return hrc;
6992}
6993
6994HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6995{
6996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 size_t cbIcon = mUserData->s.ovIcon.size();
6998 aIcon.resize(cbIcon);
6999 if (cbIcon)
7000 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
7001 return S_OK;
7002}
7003
7004HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7005{
7006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7007 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7008 if (SUCCEEDED(hrc))
7009 {
7010 i_setModified(IsModified_MachineData);
7011 mUserData.backup();
7012 size_t cbIcon = aIcon.size();
7013 mUserData->s.ovIcon.resize(cbIcon);
7014 if (cbIcon)
7015 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
7016 }
7017 return hrc;
7018}
7019
7020HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7021{
7022#ifdef VBOX_WITH_USB
7023 *aUSBProxyAvailable = true;
7024#else
7025 *aUSBProxyAvailable = false;
7026#endif
7027 return S_OK;
7028}
7029
7030HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7031{
7032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7033
7034 aVMProcessPriority = mUserData->s.strVMPriority;
7035
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7040{
7041 RT_NOREF(aVMProcessPriority);
7042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7043 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7044 if (SUCCEEDED(hrc))
7045 {
7046 /** @todo r=klaus: currently this is marked as not implemented, as
7047 * the code for setting the priority of the process is not there
7048 * (neither when starting the VM nor at runtime). */
7049 ReturnComNotImplemented();
7050#if 0
7051 hrc = mUserData.backupEx();
7052 if (SUCCEEDED(hrc))
7053 {
7054 i_setModified(IsModified_MachineData);
7055 mUserData->s.strVMPriority = aVMProcessPriority;
7056 }
7057#endif
7058 }
7059 return hrc;
7060}
7061
7062HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7063 ComPtr<IProgress> &aProgress)
7064{
7065 ComObjPtr<Progress> pP;
7066 Progress *ppP = pP;
7067 IProgress *iP = static_cast<IProgress *>(ppP);
7068 IProgress **pProgress = &iP;
7069
7070 IMachine *pTarget = aTarget;
7071
7072 /* Convert the options. */
7073 RTCList<CloneOptions_T> optList;
7074 if (aOptions.size())
7075 for (size_t i = 0; i < aOptions.size(); ++i)
7076 optList.append(aOptions[i]);
7077
7078 if (optList.contains(CloneOptions_Link))
7079 {
7080 if (!i_isSnapshotMachine())
7081 return setError(E_INVALIDARG,
7082 tr("Linked clone can only be created from a snapshot"));
7083 if (aMode != CloneMode_MachineState)
7084 return setError(E_INVALIDARG,
7085 tr("Linked clone can only be created for a single machine state"));
7086 }
7087 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7088
7089 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7090
7091 HRESULT rc = pWorker->start(pProgress);
7092
7093 pP = static_cast<Progress *>(*pProgress);
7094 pP.queryInterfaceTo(aProgress.asOutParam());
7095
7096 return rc;
7097
7098}
7099
7100HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7101 const com::Utf8Str &aType,
7102 ComPtr<IProgress> &aProgress)
7103{
7104 LogFlowThisFuncEnter();
7105
7106 ComObjPtr<Progress> progress;
7107
7108 progress.createObject();
7109
7110 HRESULT rc = S_OK;
7111 Utf8Str targetPath = aTargetPath;
7112 Utf8Str type = aType;
7113
7114 /* Initialize our worker task */
7115 MachineMoveVM* task = NULL;
7116 try
7117 {
7118 task = new MachineMoveVM(this, targetPath, type, progress);
7119 }
7120 catch(...)
7121 {
7122 delete task;
7123 return rc;
7124 }
7125
7126 /*
7127 * task pointer will be owned by the ThreadTask class.
7128 * There is no need to call operator "delete" in the end.
7129 */
7130 rc = task->init();
7131 if (SUCCEEDED(rc))
7132 {
7133 rc = task->createThread();
7134 if (FAILED(rc))
7135 {
7136 setError(rc, tr("Could not run the thread for the task MachineMoveVM"));
7137 }
7138
7139 /* Return progress to the caller */
7140 progress.queryInterfaceTo(aProgress.asOutParam());
7141 }
7142
7143 LogFlowThisFuncLeave();
7144 return rc;
7145
7146}
7147
7148HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7149{
7150 NOREF(aProgress);
7151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7152
7153 // This check should always fail.
7154 HRESULT rc = i_checkStateDependency(MutableStateDep);
7155 if (FAILED(rc)) return rc;
7156
7157 AssertFailedReturn(E_NOTIMPL);
7158}
7159
7160HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7161{
7162 NOREF(aSavedStateFile);
7163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 // This check should always fail.
7166 HRESULT rc = i_checkStateDependency(MutableStateDep);
7167 if (FAILED(rc)) return rc;
7168
7169 AssertFailedReturn(E_NOTIMPL);
7170}
7171
7172HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7173{
7174 NOREF(aFRemoveFile);
7175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7176
7177 // This check should always fail.
7178 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7179 if (FAILED(rc)) return rc;
7180
7181 AssertFailedReturn(E_NOTIMPL);
7182}
7183
7184// public methods for internal purposes
7185/////////////////////////////////////////////////////////////////////////////
7186
7187/**
7188 * Adds the given IsModified_* flag to the dirty flags of the machine.
7189 * This must be called either during i_loadSettings or under the machine write lock.
7190 * @param fl Flag
7191 * @param fAllowStateModification If state modifications are allowed.
7192 */
7193void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7194{
7195 mData->flModifications |= fl;
7196 if (fAllowStateModification && i_isStateModificationAllowed())
7197 mData->mCurrentStateModified = true;
7198}
7199
7200/**
7201 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7202 * care of the write locking.
7203 *
7204 * @param fModification The flag to add.
7205 * @param fAllowStateModification If state modifications are allowed.
7206 */
7207void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7208{
7209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7210 i_setModified(fModification, fAllowStateModification);
7211}
7212
7213/**
7214 * Saves the registry entry of this machine to the given configuration node.
7215 *
7216 * @param data Machine registry data.
7217 *
7218 * @note locks this object for reading.
7219 */
7220HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7221{
7222 AutoLimitedCaller autoCaller(this);
7223 AssertComRCReturnRC(autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 data.uuid = mData->mUuid;
7228 data.strSettingsFile = mData->m_strConfigFile;
7229
7230 return S_OK;
7231}
7232
7233/**
7234 * Calculates the absolute path of the given path taking the directory of the
7235 * machine settings file as the current directory.
7236 *
7237 * @param strPath Path to calculate the absolute path for.
7238 * @param aResult Where to put the result (used only on success, can be the
7239 * same Utf8Str instance as passed in @a aPath).
7240 * @return IPRT result.
7241 *
7242 * @note Locks this object for reading.
7243 */
7244int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7245{
7246 AutoCaller autoCaller(this);
7247 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7248
7249 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7250
7251 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7252
7253 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7254
7255 strSettingsDir.stripFilename();
7256 char folder[RTPATH_MAX];
7257 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7258 if (RT_SUCCESS(vrc))
7259 aResult = folder;
7260
7261 return vrc;
7262}
7263
7264/**
7265 * Copies strSource to strTarget, making it relative to the machine folder
7266 * if it is a subdirectory thereof, or simply copying it otherwise.
7267 *
7268 * @param strSource Path to evaluate and copy.
7269 * @param strTarget Buffer to receive target path.
7270 *
7271 * @note Locks this object for reading.
7272 */
7273void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7274 Utf8Str &strTarget)
7275{
7276 AutoCaller autoCaller(this);
7277 AssertComRCReturn(autoCaller.rc(), (void)0);
7278
7279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7280
7281 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7282 // use strTarget as a temporary buffer to hold the machine settings dir
7283 strTarget = mData->m_strConfigFileFull;
7284 strTarget.stripFilename();
7285 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7286 {
7287 // is relative: then append what's left
7288 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7289 // for empty paths (only possible for subdirs) use "." to avoid
7290 // triggering default settings for not present config attributes.
7291 if (strTarget.isEmpty())
7292 strTarget = ".";
7293 }
7294 else
7295 // is not relative: then overwrite
7296 strTarget = strSource;
7297}
7298
7299/**
7300 * Returns the full path to the machine's log folder in the
7301 * \a aLogFolder argument.
7302 */
7303void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7304{
7305 AutoCaller autoCaller(this);
7306 AssertComRCReturnVoid(autoCaller.rc());
7307
7308 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7309
7310 char szTmp[RTPATH_MAX];
7311 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7312 if (RT_SUCCESS(vrc))
7313 {
7314 if (szTmp[0] && !mUserData.isNull())
7315 {
7316 char szTmp2[RTPATH_MAX];
7317 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7318 if (RT_SUCCESS(vrc))
7319 aLogFolder = Utf8StrFmt("%s%c%s",
7320 szTmp2,
7321 RTPATH_DELIMITER,
7322 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7323 }
7324 else
7325 vrc = VERR_PATH_IS_RELATIVE;
7326 }
7327
7328 if (RT_FAILURE(vrc))
7329 {
7330 // fallback if VBOX_USER_LOGHOME is not set or invalid
7331 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7332 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7333 aLogFolder.append(RTPATH_DELIMITER);
7334 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7335 }
7336}
7337
7338/**
7339 * Returns the full path to the machine's log file for an given index.
7340 */
7341Utf8Str Machine::i_getLogFilename(ULONG idx)
7342{
7343 Utf8Str logFolder;
7344 getLogFolder(logFolder);
7345 Assert(logFolder.length());
7346
7347 Utf8Str log;
7348 if (idx == 0)
7349 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7350#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7351 else if (idx == 1)
7352 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7353 else
7354 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7355#else
7356 else
7357 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7358#endif
7359 return log;
7360}
7361
7362/**
7363 * Returns the full path to the machine's hardened log file.
7364 */
7365Utf8Str Machine::i_getHardeningLogFilename(void)
7366{
7367 Utf8Str strFilename;
7368 getLogFolder(strFilename);
7369 Assert(strFilename.length());
7370 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7371 return strFilename;
7372}
7373
7374
7375/**
7376 * Composes a unique saved state filename based on the current system time. The filename is
7377 * granular to the second so this will work so long as no more than one snapshot is taken on
7378 * a machine per second.
7379 *
7380 * Before version 4.1, we used this formula for saved state files:
7381 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7382 * which no longer works because saved state files can now be shared between the saved state of the
7383 * "saved" machine and an online snapshot, and the following would cause problems:
7384 * 1) save machine
7385 * 2) create online snapshot from that machine state --> reusing saved state file
7386 * 3) save machine again --> filename would be reused, breaking the online snapshot
7387 *
7388 * So instead we now use a timestamp.
7389 *
7390 * @param strStateFilePath
7391 */
7392
7393void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7394{
7395 AutoCaller autoCaller(this);
7396 AssertComRCReturnVoid(autoCaller.rc());
7397
7398 {
7399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7400 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7401 }
7402
7403 RTTIMESPEC ts;
7404 RTTimeNow(&ts);
7405 RTTIME time;
7406 RTTimeExplode(&time, &ts);
7407
7408 strStateFilePath += RTPATH_DELIMITER;
7409 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7410 time.i32Year, time.u8Month, time.u8MonthDay,
7411 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7412}
7413
7414/**
7415 * Returns whether at least one USB controller is present for the VM.
7416 */
7417bool Machine::i_isUSBControllerPresent()
7418{
7419 AutoCaller autoCaller(this);
7420 AssertComRCReturn(autoCaller.rc(), false);
7421
7422 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7423
7424 return (mUSBControllers->size() > 0);
7425}
7426
7427/**
7428 * @note Locks this object for writing, calls the client process
7429 * (inside the lock).
7430 */
7431HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7432 const Utf8Str &strFrontend,
7433 const Utf8Str &strEnvironment,
7434 ProgressProxy *aProgress)
7435{
7436 LogFlowThisFuncEnter();
7437
7438 AssertReturn(aControl, E_FAIL);
7439 AssertReturn(aProgress, E_FAIL);
7440 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7441
7442 AutoCaller autoCaller(this);
7443 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7444
7445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7446
7447 if (!mData->mRegistered)
7448 return setError(E_UNEXPECTED,
7449 tr("The machine '%s' is not registered"),
7450 mUserData->s.strName.c_str());
7451
7452 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7453
7454 /* The process started when launching a VM with separate UI/VM processes is always
7455 * the UI process, i.e. needs special handling as it won't claim the session. */
7456 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7457
7458 if (fSeparate)
7459 {
7460 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7461 return setError(VBOX_E_INVALID_OBJECT_STATE,
7462 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7463 mUserData->s.strName.c_str());
7464 }
7465 else
7466 {
7467 if ( mData->mSession.mState == SessionState_Locked
7468 || mData->mSession.mState == SessionState_Spawning
7469 || mData->mSession.mState == SessionState_Unlocking)
7470 return setError(VBOX_E_INVALID_OBJECT_STATE,
7471 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7472 mUserData->s.strName.c_str());
7473
7474 /* may not be busy */
7475 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7476 }
7477
7478 /* get the path to the executable */
7479 char szPath[RTPATH_MAX];
7480 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7481 size_t cchBufLeft = strlen(szPath);
7482 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7483 szPath[cchBufLeft] = 0;
7484 char *pszNamePart = szPath + cchBufLeft;
7485 cchBufLeft = sizeof(szPath) - cchBufLeft;
7486
7487 int vrc = VINF_SUCCESS;
7488 RTPROCESS pid = NIL_RTPROCESS;
7489
7490 RTENV env = RTENV_DEFAULT;
7491
7492 if (!strEnvironment.isEmpty())
7493 {
7494 char *newEnvStr = NULL;
7495
7496 do
7497 {
7498 /* clone the current environment */
7499 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7500 AssertRCBreakStmt(vrc2, vrc = vrc2);
7501
7502 newEnvStr = RTStrDup(strEnvironment.c_str());
7503 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7504
7505 /* put new variables to the environment
7506 * (ignore empty variable names here since RTEnv API
7507 * intentionally doesn't do that) */
7508 char *var = newEnvStr;
7509 for (char *p = newEnvStr; *p; ++p)
7510 {
7511 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7512 {
7513 *p = '\0';
7514 if (*var)
7515 {
7516 char *val = strchr(var, '=');
7517 if (val)
7518 {
7519 *val++ = '\0';
7520 vrc2 = RTEnvSetEx(env, var, val);
7521 }
7522 else
7523 vrc2 = RTEnvUnsetEx(env, var);
7524 if (RT_FAILURE(vrc2))
7525 break;
7526 }
7527 var = p + 1;
7528 }
7529 }
7530 if (RT_SUCCESS(vrc2) && *var)
7531 vrc2 = RTEnvPutEx(env, var);
7532
7533 AssertRCBreakStmt(vrc2, vrc = vrc2);
7534 }
7535 while (0);
7536
7537 if (newEnvStr != NULL)
7538 RTStrFree(newEnvStr);
7539 }
7540
7541 /* Hardening logging */
7542#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7543 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7544 {
7545 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7546 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7547 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7548 {
7549 Utf8Str strStartupLogDir = strHardeningLogFile;
7550 strStartupLogDir.stripFilename();
7551 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7552 file without stripping the file. */
7553 }
7554 strSupHardeningLogArg.append(strHardeningLogFile);
7555
7556 /* Remove legacy log filename to avoid confusion. */
7557 Utf8Str strOldStartupLogFile;
7558 getLogFolder(strOldStartupLogFile);
7559 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7560 RTFileDelete(strOldStartupLogFile.c_str());
7561 }
7562 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7563#else
7564 const char *pszSupHardeningLogArg = NULL;
7565#endif
7566
7567 Utf8Str strCanonicalName;
7568
7569#ifdef VBOX_WITH_QTGUI
7570 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7571 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7572 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7573 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7574 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7575 {
7576 strCanonicalName = "GUI/Qt";
7577# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7578 /* Modify the base path so that we don't need to use ".." below. */
7579 RTPathStripTrailingSlash(szPath);
7580 RTPathStripFilename(szPath);
7581 cchBufLeft = strlen(szPath);
7582 pszNamePart = szPath + cchBufLeft;
7583 cchBufLeft = sizeof(szPath) - cchBufLeft;
7584
7585# define OSX_APP_NAME "VirtualBoxVM"
7586# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7587
7588 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7589 if ( strAppOverride.contains(".")
7590 || strAppOverride.contains("/")
7591 || strAppOverride.contains("\\")
7592 || strAppOverride.contains(":"))
7593 strAppOverride.setNull();
7594 Utf8Str strAppPath;
7595 if (!strAppOverride.isEmpty())
7596 {
7597 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7598 Utf8Str strFullPath(szPath);
7599 strFullPath.append(strAppPath);
7600 /* there is a race, but people using this deserve the failure */
7601 if (!RTFileExists(strFullPath.c_str()))
7602 strAppOverride.setNull();
7603 }
7604 if (strAppOverride.isEmpty())
7605 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7606 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7607 strcpy(pszNamePart, strAppPath.c_str());
7608# else
7609 static const char s_szVirtualBox_exe[] = "VirtualBoxVM" HOSTSUFF_EXE;
7610 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7611 strcpy(pszNamePart, s_szVirtualBox_exe);
7612# endif
7613
7614 Utf8Str idStr = mData->mUuid.toString();
7615 const char *apszArgs[] =
7616 {
7617 szPath,
7618 "--comment", mUserData->s.strName.c_str(),
7619 "--startvm", idStr.c_str(),
7620 "--no-startvm-errormsgbox",
7621 NULL, /* For "--separate". */
7622 NULL, /* For "--sup-startup-log". */
7623 NULL
7624 };
7625 unsigned iArg = 6;
7626 if (fSeparate)
7627 apszArgs[iArg++] = "--separate";
7628 apszArgs[iArg++] = pszSupHardeningLogArg;
7629
7630 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7631 }
7632#else /* !VBOX_WITH_QTGUI */
7633 if (0)
7634 ;
7635#endif /* VBOX_WITH_QTGUI */
7636
7637 else
7638
7639#ifdef VBOX_WITH_VBOXSDL
7640 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7641 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7642 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7643 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7644 {
7645 strCanonicalName = "GUI/SDL";
7646 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7647 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7648 strcpy(pszNamePart, s_szVBoxSDL_exe);
7649
7650 Utf8Str idStr = mData->mUuid.toString();
7651 const char *apszArgs[] =
7652 {
7653 szPath,
7654 "--comment", mUserData->s.strName.c_str(),
7655 "--startvm", idStr.c_str(),
7656 NULL, /* For "--separate". */
7657 NULL, /* For "--sup-startup-log". */
7658 NULL
7659 };
7660 unsigned iArg = 5;
7661 if (fSeparate)
7662 apszArgs[iArg++] = "--separate";
7663 apszArgs[iArg++] = pszSupHardeningLogArg;
7664
7665 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7666 }
7667#else /* !VBOX_WITH_VBOXSDL */
7668 if (0)
7669 ;
7670#endif /* !VBOX_WITH_VBOXSDL */
7671
7672 else
7673
7674#ifdef VBOX_WITH_HEADLESS
7675 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7676 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7677 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7678 )
7679 {
7680 strCanonicalName = "headless";
7681 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7682 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7683 * and a VM works even if the server has not been installed.
7684 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7685 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7686 * differently in 4.0 and 3.x.
7687 */
7688 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7689 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7690 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7691
7692 Utf8Str idStr = mData->mUuid.toString();
7693 const char *apszArgs[] =
7694 {
7695 szPath,
7696 "--comment", mUserData->s.strName.c_str(),
7697 "--startvm", idStr.c_str(),
7698 "--vrde", "config",
7699 NULL, /* For "--capture". */
7700 NULL, /* For "--sup-startup-log". */
7701 NULL
7702 };
7703 unsigned iArg = 7;
7704 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7705 apszArgs[iArg++] = "--capture";
7706 apszArgs[iArg++] = pszSupHardeningLogArg;
7707
7708# ifdef RT_OS_WINDOWS
7709 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7710# else
7711 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7712# endif
7713 }
7714#else /* !VBOX_WITH_HEADLESS */
7715 if (0)
7716 ;
7717#endif /* !VBOX_WITH_HEADLESS */
7718 else
7719 {
7720 RTEnvDestroy(env);
7721 return setError(E_INVALIDARG,
7722 tr("Invalid frontend name: '%s'"),
7723 strFrontend.c_str());
7724 }
7725
7726 RTEnvDestroy(env);
7727
7728 if (RT_FAILURE(vrc))
7729 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7730 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7731 mUserData->s.strName.c_str(), vrc);
7732
7733 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7734
7735 if (!fSeparate)
7736 {
7737 /*
7738 * Note that we don't release the lock here before calling the client,
7739 * because it doesn't need to call us back if called with a NULL argument.
7740 * Releasing the lock here is dangerous because we didn't prepare the
7741 * launch data yet, but the client we've just started may happen to be
7742 * too fast and call LockMachine() that will fail (because of PID, etc.),
7743 * so that the Machine will never get out of the Spawning session state.
7744 */
7745
7746 /* inform the session that it will be a remote one */
7747 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7748#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7749 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7750#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7751 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7752#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7753 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7754
7755 if (FAILED(rc))
7756 {
7757 /* restore the session state */
7758 mData->mSession.mState = SessionState_Unlocked;
7759 alock.release();
7760 mParent->i_addProcessToReap(pid);
7761 /* The failure may occur w/o any error info (from RPC), so provide one */
7762 return setError(VBOX_E_VM_ERROR,
7763 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7764 }
7765
7766 /* attach launch data to the machine */
7767 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7768 mData->mSession.mRemoteControls.push_back(aControl);
7769 mData->mSession.mProgress = aProgress;
7770 mData->mSession.mPID = pid;
7771 mData->mSession.mState = SessionState_Spawning;
7772 Assert(strCanonicalName.isNotEmpty());
7773 mData->mSession.mName = strCanonicalName;
7774 }
7775 else
7776 {
7777 /* For separate UI process we declare the launch as completed instantly, as the
7778 * actual headless VM start may or may not come. No point in remembering anything
7779 * yet, as what matters for us is when the headless VM gets started. */
7780 aProgress->i_notifyComplete(S_OK);
7781 }
7782
7783 alock.release();
7784 mParent->i_addProcessToReap(pid);
7785
7786 LogFlowThisFuncLeave();
7787 return S_OK;
7788}
7789
7790/**
7791 * Returns @c true if the given session machine instance has an open direct
7792 * session (and optionally also for direct sessions which are closing) and
7793 * returns the session control machine instance if so.
7794 *
7795 * Note that when the method returns @c false, the arguments remain unchanged.
7796 *
7797 * @param aMachine Session machine object.
7798 * @param aControl Direct session control object (optional).
7799 * @param aRequireVM If true then only allow VM sessions.
7800 * @param aAllowClosing If true then additionally a session which is currently
7801 * being closed will also be allowed.
7802 *
7803 * @note locks this object for reading.
7804 */
7805bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7806 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7807 bool aRequireVM /*= false*/,
7808 bool aAllowClosing /*= false*/)
7809{
7810 AutoLimitedCaller autoCaller(this);
7811 AssertComRCReturn(autoCaller.rc(), false);
7812
7813 /* just return false for inaccessible machines */
7814 if (getObjectState().getState() != ObjectState::Ready)
7815 return false;
7816
7817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7818
7819 if ( ( mData->mSession.mState == SessionState_Locked
7820 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7821 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7822 )
7823 {
7824 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7825
7826 aMachine = mData->mSession.mMachine;
7827
7828 if (aControl != NULL)
7829 *aControl = mData->mSession.mDirectControl;
7830
7831 return true;
7832 }
7833
7834 return false;
7835}
7836
7837/**
7838 * Returns @c true if the given machine has an spawning direct session.
7839 *
7840 * @note locks this object for reading.
7841 */
7842bool Machine::i_isSessionSpawning()
7843{
7844 AutoLimitedCaller autoCaller(this);
7845 AssertComRCReturn(autoCaller.rc(), false);
7846
7847 /* just return false for inaccessible machines */
7848 if (getObjectState().getState() != ObjectState::Ready)
7849 return false;
7850
7851 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7852
7853 if (mData->mSession.mState == SessionState_Spawning)
7854 return true;
7855
7856 return false;
7857}
7858
7859/**
7860 * Called from the client watcher thread to check for unexpected client process
7861 * death during Session_Spawning state (e.g. before it successfully opened a
7862 * direct session).
7863 *
7864 * On Win32 and on OS/2, this method is called only when we've got the
7865 * direct client's process termination notification, so it always returns @c
7866 * true.
7867 *
7868 * On other platforms, this method returns @c true if the client process is
7869 * terminated and @c false if it's still alive.
7870 *
7871 * @note Locks this object for writing.
7872 */
7873bool Machine::i_checkForSpawnFailure()
7874{
7875 AutoCaller autoCaller(this);
7876 if (!autoCaller.isOk())
7877 {
7878 /* nothing to do */
7879 LogFlowThisFunc(("Already uninitialized!\n"));
7880 return true;
7881 }
7882
7883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7884
7885 if (mData->mSession.mState != SessionState_Spawning)
7886 {
7887 /* nothing to do */
7888 LogFlowThisFunc(("Not spawning any more!\n"));
7889 return true;
7890 }
7891
7892 HRESULT rc = S_OK;
7893
7894 /* PID not yet initialized, skip check. */
7895 if (mData->mSession.mPID == NIL_RTPROCESS)
7896 return false;
7897
7898 RTPROCSTATUS status;
7899 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7900
7901 if (vrc != VERR_PROCESS_RUNNING)
7902 {
7903 Utf8Str strExtraInfo;
7904
7905#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7906 /* If the startup logfile exists and is of non-zero length, tell the
7907 user to look there for more details to encourage them to attach it
7908 when reporting startup issues. */
7909 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7910 uint64_t cbStartupLogFile = 0;
7911 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7912 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7913 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7914#endif
7915
7916 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7917 rc = setError(E_FAIL,
7918 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7919 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7920 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7921 rc = setError(E_FAIL,
7922 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7923 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7924 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7925 rc = setError(E_FAIL,
7926 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7927 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7928 else
7929 rc = setErrorBoth(E_FAIL, vrc,
7930 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7931 i_getName().c_str(), vrc, strExtraInfo.c_str());
7932 }
7933
7934 if (FAILED(rc))
7935 {
7936 /* Close the remote session, remove the remote control from the list
7937 * and reset session state to Closed (@note keep the code in sync with
7938 * the relevant part in LockMachine()). */
7939
7940 Assert(mData->mSession.mRemoteControls.size() == 1);
7941 if (mData->mSession.mRemoteControls.size() == 1)
7942 {
7943 ErrorInfoKeeper eik;
7944 mData->mSession.mRemoteControls.front()->Uninitialize();
7945 }
7946
7947 mData->mSession.mRemoteControls.clear();
7948 mData->mSession.mState = SessionState_Unlocked;
7949
7950 /* finalize the progress after setting the state */
7951 if (!mData->mSession.mProgress.isNull())
7952 {
7953 mData->mSession.mProgress->notifyComplete(rc);
7954 mData->mSession.mProgress.setNull();
7955 }
7956
7957 mData->mSession.mPID = NIL_RTPROCESS;
7958
7959 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7960 return true;
7961 }
7962
7963 return false;
7964}
7965
7966/**
7967 * Checks whether the machine can be registered. If so, commits and saves
7968 * all settings.
7969 *
7970 * @note Must be called from mParent's write lock. Locks this object and
7971 * children for writing.
7972 */
7973HRESULT Machine::i_prepareRegister()
7974{
7975 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7976
7977 AutoLimitedCaller autoCaller(this);
7978 AssertComRCReturnRC(autoCaller.rc());
7979
7980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7981
7982 /* wait for state dependents to drop to zero */
7983 i_ensureNoStateDependencies();
7984
7985 if (!mData->mAccessible)
7986 return setError(VBOX_E_INVALID_OBJECT_STATE,
7987 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7988 mUserData->s.strName.c_str(),
7989 mData->mUuid.toString().c_str());
7990
7991 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7992
7993 if (mData->mRegistered)
7994 return setError(VBOX_E_INVALID_OBJECT_STATE,
7995 tr("The machine '%s' with UUID {%s} is already registered"),
7996 mUserData->s.strName.c_str(),
7997 mData->mUuid.toString().c_str());
7998
7999 HRESULT rc = S_OK;
8000
8001 // Ensure the settings are saved. If we are going to be registered and
8002 // no config file exists yet, create it by calling i_saveSettings() too.
8003 if ( (mData->flModifications)
8004 || (!mData->pMachineConfigFile->fileExists())
8005 )
8006 {
8007 rc = i_saveSettings(NULL);
8008 // no need to check whether VirtualBox.xml needs saving too since
8009 // we can't have a machine XML file rename pending
8010 if (FAILED(rc)) return rc;
8011 }
8012
8013 /* more config checking goes here */
8014
8015 if (SUCCEEDED(rc))
8016 {
8017 /* we may have had implicit modifications we want to fix on success */
8018 i_commit();
8019
8020 mData->mRegistered = true;
8021 }
8022 else
8023 {
8024 /* we may have had implicit modifications we want to cancel on failure*/
8025 i_rollback(false /* aNotify */);
8026 }
8027
8028 return rc;
8029}
8030
8031/**
8032 * Increases the number of objects dependent on the machine state or on the
8033 * registered state. Guarantees that these two states will not change at least
8034 * until #i_releaseStateDependency() is called.
8035 *
8036 * Depending on the @a aDepType value, additional state checks may be made.
8037 * These checks will set extended error info on failure. See
8038 * #i_checkStateDependency() for more info.
8039 *
8040 * If this method returns a failure, the dependency is not added and the caller
8041 * is not allowed to rely on any particular machine state or registration state
8042 * value and may return the failed result code to the upper level.
8043 *
8044 * @param aDepType Dependency type to add.
8045 * @param aState Current machine state (NULL if not interested).
8046 * @param aRegistered Current registered state (NULL if not interested).
8047 *
8048 * @note Locks this object for writing.
8049 */
8050HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8051 MachineState_T *aState /* = NULL */,
8052 BOOL *aRegistered /* = NULL */)
8053{
8054 AutoCaller autoCaller(this);
8055 AssertComRCReturnRC(autoCaller.rc());
8056
8057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8058
8059 HRESULT rc = i_checkStateDependency(aDepType);
8060 if (FAILED(rc)) return rc;
8061
8062 {
8063 if (mData->mMachineStateChangePending != 0)
8064 {
8065 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8066 * drop to zero so don't add more. It may make sense to wait a bit
8067 * and retry before reporting an error (since the pending state
8068 * transition should be really quick) but let's just assert for
8069 * now to see if it ever happens on practice. */
8070
8071 AssertFailed();
8072
8073 return setError(E_ACCESSDENIED,
8074 tr("Machine state change is in progress. Please retry the operation later."));
8075 }
8076
8077 ++mData->mMachineStateDeps;
8078 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8079 }
8080
8081 if (aState)
8082 *aState = mData->mMachineState;
8083 if (aRegistered)
8084 *aRegistered = mData->mRegistered;
8085
8086 return S_OK;
8087}
8088
8089/**
8090 * Decreases the number of objects dependent on the machine state.
8091 * Must always complete the #i_addStateDependency() call after the state
8092 * dependency is no more necessary.
8093 */
8094void Machine::i_releaseStateDependency()
8095{
8096 AutoCaller autoCaller(this);
8097 AssertComRCReturnVoid(autoCaller.rc());
8098
8099 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8100
8101 /* releaseStateDependency() w/o addStateDependency()? */
8102 AssertReturnVoid(mData->mMachineStateDeps != 0);
8103 -- mData->mMachineStateDeps;
8104
8105 if (mData->mMachineStateDeps == 0)
8106 {
8107 /* inform i_ensureNoStateDependencies() that there are no more deps */
8108 if (mData->mMachineStateChangePending != 0)
8109 {
8110 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8111 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8112 }
8113 }
8114}
8115
8116Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8117{
8118 /* start with nothing found */
8119 Utf8Str strResult("");
8120
8121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8122
8123 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8124 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8125 // found:
8126 strResult = it->second; // source is a Utf8Str
8127
8128 return strResult;
8129}
8130
8131// protected methods
8132/////////////////////////////////////////////////////////////////////////////
8133
8134/**
8135 * Performs machine state checks based on the @a aDepType value. If a check
8136 * fails, this method will set extended error info, otherwise it will return
8137 * S_OK. It is supposed, that on failure, the caller will immediately return
8138 * the return value of this method to the upper level.
8139 *
8140 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8141 *
8142 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8143 * current state of this machine object allows to change settings of the
8144 * machine (i.e. the machine is not registered, or registered but not running
8145 * and not saved). It is useful to call this method from Machine setters
8146 * before performing any change.
8147 *
8148 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8149 * as for MutableStateDep except that if the machine is saved, S_OK is also
8150 * returned. This is useful in setters which allow changing machine
8151 * properties when it is in the saved state.
8152 *
8153 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8154 * if the current state of this machine object allows to change runtime
8155 * changeable settings of the machine (i.e. the machine is not registered, or
8156 * registered but either running or not running and not saved). It is useful
8157 * to call this method from Machine setters before performing any changes to
8158 * runtime changeable settings.
8159 *
8160 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8161 * the same as for MutableOrRunningStateDep except that if the machine is
8162 * saved, S_OK is also returned. This is useful in setters which allow
8163 * changing runtime and saved state changeable machine properties.
8164 *
8165 * @param aDepType Dependency type to check.
8166 *
8167 * @note Non Machine based classes should use #i_addStateDependency() and
8168 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8169 * template.
8170 *
8171 * @note This method must be called from under this object's read or write
8172 * lock.
8173 */
8174HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8175{
8176 switch (aDepType)
8177 {
8178 case AnyStateDep:
8179 {
8180 break;
8181 }
8182 case MutableStateDep:
8183 {
8184 if ( mData->mRegistered
8185 && ( !i_isSessionMachine()
8186 || ( mData->mMachineState != MachineState_Aborted
8187 && mData->mMachineState != MachineState_Teleported
8188 && mData->mMachineState != MachineState_PoweredOff
8189 )
8190 )
8191 )
8192 return setError(VBOX_E_INVALID_VM_STATE,
8193 tr("The machine is not mutable (state is %s)"),
8194 Global::stringifyMachineState(mData->mMachineState));
8195 break;
8196 }
8197 case MutableOrSavedStateDep:
8198 {
8199 if ( mData->mRegistered
8200 && ( !i_isSessionMachine()
8201 || ( mData->mMachineState != MachineState_Aborted
8202 && mData->mMachineState != MachineState_Teleported
8203 && mData->mMachineState != MachineState_Saved
8204 && mData->mMachineState != MachineState_PoweredOff
8205 )
8206 )
8207 )
8208 return setError(VBOX_E_INVALID_VM_STATE,
8209 tr("The machine is not mutable or saved (state is %s)"),
8210 Global::stringifyMachineState(mData->mMachineState));
8211 break;
8212 }
8213 case MutableOrRunningStateDep:
8214 {
8215 if ( mData->mRegistered
8216 && ( !i_isSessionMachine()
8217 || ( mData->mMachineState != MachineState_Aborted
8218 && mData->mMachineState != MachineState_Teleported
8219 && mData->mMachineState != MachineState_PoweredOff
8220 && !Global::IsOnline(mData->mMachineState)
8221 )
8222 )
8223 )
8224 return setError(VBOX_E_INVALID_VM_STATE,
8225 tr("The machine is not mutable or running (state is %s)"),
8226 Global::stringifyMachineState(mData->mMachineState));
8227 break;
8228 }
8229 case MutableOrSavedOrRunningStateDep:
8230 {
8231 if ( mData->mRegistered
8232 && ( !i_isSessionMachine()
8233 || ( mData->mMachineState != MachineState_Aborted
8234 && mData->mMachineState != MachineState_Teleported
8235 && mData->mMachineState != MachineState_Saved
8236 && mData->mMachineState != MachineState_PoweredOff
8237 && !Global::IsOnline(mData->mMachineState)
8238 )
8239 )
8240 )
8241 return setError(VBOX_E_INVALID_VM_STATE,
8242 tr("The machine is not mutable, saved or running (state is %s)"),
8243 Global::stringifyMachineState(mData->mMachineState));
8244 break;
8245 }
8246 }
8247
8248 return S_OK;
8249}
8250
8251/**
8252 * Helper to initialize all associated child objects and allocate data
8253 * structures.
8254 *
8255 * This method must be called as a part of the object's initialization procedure
8256 * (usually done in the #init() method).
8257 *
8258 * @note Must be called only from #init() or from #i_registeredInit().
8259 */
8260HRESULT Machine::initDataAndChildObjects()
8261{
8262 AutoCaller autoCaller(this);
8263 AssertComRCReturnRC(autoCaller.rc());
8264 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8265 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8266
8267 AssertReturn(!mData->mAccessible, E_FAIL);
8268
8269 /* allocate data structures */
8270 mSSData.allocate();
8271 mUserData.allocate();
8272 mHWData.allocate();
8273 mMediumAttachments.allocate();
8274 mStorageControllers.allocate();
8275 mUSBControllers.allocate();
8276
8277 /* initialize mOSTypeId */
8278 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8279
8280/** @todo r=bird: init() methods never fails, right? Why don't we make them
8281 * return void then! */
8282
8283 /* create associated BIOS settings object */
8284 unconst(mBIOSSettings).createObject();
8285 mBIOSSettings->init(this);
8286
8287 /* create associated record settings object */
8288 unconst(mRecordingSettings).createObject();
8289 mRecordingSettings->init(this);
8290
8291 /* create an associated VRDE object (default is disabled) */
8292 unconst(mVRDEServer).createObject();
8293 mVRDEServer->init(this);
8294
8295 /* create associated serial port objects */
8296 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8297 {
8298 unconst(mSerialPorts[slot]).createObject();
8299 mSerialPorts[slot]->init(this, slot);
8300 }
8301
8302 /* create associated parallel port objects */
8303 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8304 {
8305 unconst(mParallelPorts[slot]).createObject();
8306 mParallelPorts[slot]->init(this, slot);
8307 }
8308
8309 /* create the audio adapter object (always present, default is disabled) */
8310 unconst(mAudioAdapter).createObject();
8311 mAudioAdapter->init(this);
8312
8313 /* create the USB device filters object (always present) */
8314 unconst(mUSBDeviceFilters).createObject();
8315 mUSBDeviceFilters->init(this);
8316
8317 /* create associated network adapter objects */
8318 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8319 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8320 {
8321 unconst(mNetworkAdapters[slot]).createObject();
8322 mNetworkAdapters[slot]->init(this, slot);
8323 }
8324
8325 /* create the bandwidth control */
8326 unconst(mBandwidthControl).createObject();
8327 mBandwidthControl->init(this);
8328
8329 return S_OK;
8330}
8331
8332/**
8333 * Helper to uninitialize all associated child objects and to free all data
8334 * structures.
8335 *
8336 * This method must be called as a part of the object's uninitialization
8337 * procedure (usually done in the #uninit() method).
8338 *
8339 * @note Must be called only from #uninit() or from #i_registeredInit().
8340 */
8341void Machine::uninitDataAndChildObjects()
8342{
8343 AutoCaller autoCaller(this);
8344 AssertComRCReturnVoid(autoCaller.rc());
8345 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8346 || getObjectState().getState() == ObjectState::Limited);
8347
8348 /* tell all our other child objects we've been uninitialized */
8349 if (mBandwidthControl)
8350 {
8351 mBandwidthControl->uninit();
8352 unconst(mBandwidthControl).setNull();
8353 }
8354
8355 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8356 {
8357 if (mNetworkAdapters[slot])
8358 {
8359 mNetworkAdapters[slot]->uninit();
8360 unconst(mNetworkAdapters[slot]).setNull();
8361 }
8362 }
8363
8364 if (mUSBDeviceFilters)
8365 {
8366 mUSBDeviceFilters->uninit();
8367 unconst(mUSBDeviceFilters).setNull();
8368 }
8369
8370 if (mAudioAdapter)
8371 {
8372 mAudioAdapter->uninit();
8373 unconst(mAudioAdapter).setNull();
8374 }
8375
8376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8377 {
8378 if (mParallelPorts[slot])
8379 {
8380 mParallelPorts[slot]->uninit();
8381 unconst(mParallelPorts[slot]).setNull();
8382 }
8383 }
8384
8385 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8386 {
8387 if (mSerialPorts[slot])
8388 {
8389 mSerialPorts[slot]->uninit();
8390 unconst(mSerialPorts[slot]).setNull();
8391 }
8392 }
8393
8394 if (mVRDEServer)
8395 {
8396 mVRDEServer->uninit();
8397 unconst(mVRDEServer).setNull();
8398 }
8399
8400 if (mBIOSSettings)
8401 {
8402 mBIOSSettings->uninit();
8403 unconst(mBIOSSettings).setNull();
8404 }
8405
8406 if (mRecordingSettings)
8407 {
8408 mRecordingSettings->uninit();
8409 unconst(mRecordingSettings).setNull();
8410 }
8411
8412 /* Deassociate media (only when a real Machine or a SnapshotMachine
8413 * instance is uninitialized; SessionMachine instances refer to real
8414 * Machine media). This is necessary for a clean re-initialization of
8415 * the VM after successfully re-checking the accessibility state. Note
8416 * that in case of normal Machine or SnapshotMachine uninitialization (as
8417 * a result of unregistering or deleting the snapshot), outdated media
8418 * attachments will already be uninitialized and deleted, so this
8419 * code will not affect them. */
8420 if ( !mMediumAttachments.isNull()
8421 && !i_isSessionMachine()
8422 )
8423 {
8424 for (MediumAttachmentList::const_iterator
8425 it = mMediumAttachments->begin();
8426 it != mMediumAttachments->end();
8427 ++it)
8428 {
8429 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8430 if (pMedium.isNull())
8431 continue;
8432 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8433 AssertComRC(rc);
8434 }
8435 }
8436
8437 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8438 {
8439 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8440 if (mData->mFirstSnapshot)
8441 {
8442 // snapshots tree is protected by machine write lock; strictly
8443 // this isn't necessary here since we're deleting the entire
8444 // machine, but otherwise we assert in Snapshot::uninit()
8445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8446 mData->mFirstSnapshot->uninit();
8447 mData->mFirstSnapshot.setNull();
8448 }
8449
8450 mData->mCurrentSnapshot.setNull();
8451 }
8452
8453 /* free data structures (the essential mData structure is not freed here
8454 * since it may be still in use) */
8455 mMediumAttachments.free();
8456 mStorageControllers.free();
8457 mUSBControllers.free();
8458 mHWData.free();
8459 mUserData.free();
8460 mSSData.free();
8461}
8462
8463/**
8464 * Returns a pointer to the Machine object for this machine that acts like a
8465 * parent for complex machine data objects such as shared folders, etc.
8466 *
8467 * For primary Machine objects and for SnapshotMachine objects, returns this
8468 * object's pointer itself. For SessionMachine objects, returns the peer
8469 * (primary) machine pointer.
8470 */
8471Machine *Machine::i_getMachine()
8472{
8473 if (i_isSessionMachine())
8474 return (Machine*)mPeer;
8475 return this;
8476}
8477
8478/**
8479 * Makes sure that there are no machine state dependents. If necessary, waits
8480 * for the number of dependents to drop to zero.
8481 *
8482 * Make sure this method is called from under this object's write lock to
8483 * guarantee that no new dependents may be added when this method returns
8484 * control to the caller.
8485 *
8486 * @note Locks this object for writing. The lock will be released while waiting
8487 * (if necessary).
8488 *
8489 * @warning To be used only in methods that change the machine state!
8490 */
8491void Machine::i_ensureNoStateDependencies()
8492{
8493 AssertReturnVoid(isWriteLockOnCurrentThread());
8494
8495 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8496
8497 /* Wait for all state dependents if necessary */
8498 if (mData->mMachineStateDeps != 0)
8499 {
8500 /* lazy semaphore creation */
8501 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8502 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8503
8504 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8505 mData->mMachineStateDeps));
8506
8507 ++mData->mMachineStateChangePending;
8508
8509 /* reset the semaphore before waiting, the last dependent will signal
8510 * it */
8511 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8512
8513 alock.release();
8514
8515 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8516
8517 alock.acquire();
8518
8519 -- mData->mMachineStateChangePending;
8520 }
8521}
8522
8523/**
8524 * Changes the machine state and informs callbacks.
8525 *
8526 * This method is not intended to fail so it either returns S_OK or asserts (and
8527 * returns a failure).
8528 *
8529 * @note Locks this object for writing.
8530 */
8531HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8532{
8533 LogFlowThisFuncEnter();
8534 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8535 Assert(aMachineState != MachineState_Null);
8536
8537 AutoCaller autoCaller(this);
8538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8539
8540 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8541
8542 /* wait for state dependents to drop to zero */
8543 i_ensureNoStateDependencies();
8544
8545 MachineState_T const enmOldState = mData->mMachineState;
8546 if (enmOldState != aMachineState)
8547 {
8548 mData->mMachineState = aMachineState;
8549 RTTimeNow(&mData->mLastStateChange);
8550
8551#ifdef VBOX_WITH_DTRACE_R3_MAIN
8552 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8553#endif
8554 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8555 }
8556
8557 LogFlowThisFuncLeave();
8558 return S_OK;
8559}
8560
8561/**
8562 * Searches for a shared folder with the given logical name
8563 * in the collection of shared folders.
8564 *
8565 * @param aName logical name of the shared folder
8566 * @param aSharedFolder where to return the found object
8567 * @param aSetError whether to set the error info if the folder is
8568 * not found
8569 * @return
8570 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8571 *
8572 * @note
8573 * must be called from under the object's lock!
8574 */
8575HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8576 ComObjPtr<SharedFolder> &aSharedFolder,
8577 bool aSetError /* = false */)
8578{
8579 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8580 for (HWData::SharedFolderList::const_iterator
8581 it = mHWData->mSharedFolders.begin();
8582 it != mHWData->mSharedFolders.end();
8583 ++it)
8584 {
8585 SharedFolder *pSF = *it;
8586 AutoCaller autoCaller(pSF);
8587 if (pSF->i_getName() == aName)
8588 {
8589 aSharedFolder = pSF;
8590 rc = S_OK;
8591 break;
8592 }
8593 }
8594
8595 if (aSetError && FAILED(rc))
8596 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8597
8598 return rc;
8599}
8600
8601/**
8602 * Initializes all machine instance data from the given settings structures
8603 * from XML. The exception is the machine UUID which needs special handling
8604 * depending on the caller's use case, so the caller needs to set that herself.
8605 *
8606 * This gets called in several contexts during machine initialization:
8607 *
8608 * -- When machine XML exists on disk already and needs to be loaded into memory,
8609 * for example, from #i_registeredInit() to load all registered machines on
8610 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8611 * attached to the machine should be part of some media registry already.
8612 *
8613 * -- During OVF import, when a machine config has been constructed from an
8614 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8615 * ensure that the media listed as attachments in the config (which have
8616 * been imported from the OVF) receive the correct registry ID.
8617 *
8618 * -- During VM cloning.
8619 *
8620 * @param config Machine settings from XML.
8621 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8622 * for each attached medium in the config.
8623 * @return
8624 */
8625HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8626 const Guid *puuidRegistry)
8627{
8628 // copy name, description, OS type, teleporter, UTC etc.
8629 mUserData->s = config.machineUserData;
8630
8631 // look up the object by Id to check it is valid
8632 ComObjPtr<GuestOSType> pGuestOSType;
8633 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8634 if (!pGuestOSType.isNull())
8635 mUserData->s.strOsType = pGuestOSType->i_id();
8636
8637 // stateFile (optional)
8638 if (config.strStateFile.isEmpty())
8639 mSSData->strStateFilePath.setNull();
8640 else
8641 {
8642 Utf8Str stateFilePathFull(config.strStateFile);
8643 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8644 if (RT_FAILURE(vrc))
8645 return setErrorBoth(E_FAIL, vrc,
8646 tr("Invalid saved state file path '%s' (%Rrc)"),
8647 config.strStateFile.c_str(),
8648 vrc);
8649 mSSData->strStateFilePath = stateFilePathFull;
8650 }
8651
8652 // snapshot folder needs special processing so set it again
8653 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8654 if (FAILED(rc)) return rc;
8655
8656 /* Copy the extra data items (config may or may not be the same as
8657 * mData->pMachineConfigFile) if necessary. When loading the XML files
8658 * from disk they are the same, but not for OVF import. */
8659 if (mData->pMachineConfigFile != &config)
8660 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8661
8662 /* currentStateModified (optional, default is true) */
8663 mData->mCurrentStateModified = config.fCurrentStateModified;
8664
8665 mData->mLastStateChange = config.timeLastStateChange;
8666
8667 /*
8668 * note: all mUserData members must be assigned prior this point because
8669 * we need to commit changes in order to let mUserData be shared by all
8670 * snapshot machine instances.
8671 */
8672 mUserData.commitCopy();
8673
8674 // machine registry, if present (must be loaded before snapshots)
8675 if (config.canHaveOwnMediaRegistry())
8676 {
8677 // determine machine folder
8678 Utf8Str strMachineFolder = i_getSettingsFileFull();
8679 strMachineFolder.stripFilename();
8680 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8681 config.mediaRegistry,
8682 strMachineFolder);
8683 if (FAILED(rc)) return rc;
8684 }
8685
8686 /* Snapshot node (optional) */
8687 size_t cRootSnapshots;
8688 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8689 {
8690 // there must be only one root snapshot
8691 Assert(cRootSnapshots == 1);
8692
8693 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8694
8695 rc = i_loadSnapshot(snap,
8696 config.uuidCurrentSnapshot,
8697 NULL); // no parent == first snapshot
8698 if (FAILED(rc)) return rc;
8699 }
8700
8701 // hardware data
8702 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8703 if (FAILED(rc)) return rc;
8704
8705 /*
8706 * NOTE: the assignment below must be the last thing to do,
8707 * otherwise it will be not possible to change the settings
8708 * somewhere in the code above because all setters will be
8709 * blocked by i_checkStateDependency(MutableStateDep).
8710 */
8711
8712 /* set the machine state to Aborted or Saved when appropriate */
8713 if (config.fAborted)
8714 {
8715 mSSData->strStateFilePath.setNull();
8716
8717 /* no need to use i_setMachineState() during init() */
8718 mData->mMachineState = MachineState_Aborted;
8719 }
8720 else if (!mSSData->strStateFilePath.isEmpty())
8721 {
8722 /* no need to use i_setMachineState() during init() */
8723 mData->mMachineState = MachineState_Saved;
8724 }
8725
8726 // after loading settings, we are no longer different from the XML on disk
8727 mData->flModifications = 0;
8728
8729 return S_OK;
8730}
8731
8732/**
8733 * Recursively loads all snapshots starting from the given.
8734 *
8735 * @param data snapshot settings.
8736 * @param aCurSnapshotId Current snapshot ID from the settings file.
8737 * @param aParentSnapshot Parent snapshot.
8738 */
8739HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8740 const Guid &aCurSnapshotId,
8741 Snapshot *aParentSnapshot)
8742{
8743 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8744 AssertReturn(!i_isSessionMachine(), E_FAIL);
8745
8746 HRESULT rc = S_OK;
8747
8748 Utf8Str strStateFile;
8749 if (!data.strStateFile.isEmpty())
8750 {
8751 /* optional */
8752 strStateFile = data.strStateFile;
8753 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8754 if (RT_FAILURE(vrc))
8755 return setErrorBoth(E_FAIL, vrc,
8756 tr("Invalid saved state file path '%s' (%Rrc)"),
8757 strStateFile.c_str(),
8758 vrc);
8759 }
8760
8761 /* create a snapshot machine object */
8762 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8763 pSnapshotMachine.createObject();
8764 rc = pSnapshotMachine->initFromSettings(this,
8765 data.hardware,
8766 &data.debugging,
8767 &data.autostart,
8768 data.uuid.ref(),
8769 strStateFile);
8770 if (FAILED(rc)) return rc;
8771
8772 /* create a snapshot object */
8773 ComObjPtr<Snapshot> pSnapshot;
8774 pSnapshot.createObject();
8775 /* initialize the snapshot */
8776 rc = pSnapshot->init(mParent, // VirtualBox object
8777 data.uuid,
8778 data.strName,
8779 data.strDescription,
8780 data.timestamp,
8781 pSnapshotMachine,
8782 aParentSnapshot);
8783 if (FAILED(rc)) return rc;
8784
8785 /* memorize the first snapshot if necessary */
8786 if (!mData->mFirstSnapshot)
8787 mData->mFirstSnapshot = pSnapshot;
8788
8789 /* memorize the current snapshot when appropriate */
8790 if ( !mData->mCurrentSnapshot
8791 && pSnapshot->i_getId() == aCurSnapshotId
8792 )
8793 mData->mCurrentSnapshot = pSnapshot;
8794
8795 // now create the children
8796 for (settings::SnapshotsList::const_iterator
8797 it = data.llChildSnapshots.begin();
8798 it != data.llChildSnapshots.end();
8799 ++it)
8800 {
8801 const settings::Snapshot &childData = *it;
8802 // recurse
8803 rc = i_loadSnapshot(childData,
8804 aCurSnapshotId,
8805 pSnapshot); // parent = the one we created above
8806 if (FAILED(rc)) return rc;
8807 }
8808
8809 return rc;
8810}
8811
8812/**
8813 * Loads settings into mHWData.
8814 *
8815 * @param puuidRegistry Registry ID.
8816 * @param puuidSnapshot Snapshot ID
8817 * @param data Reference to the hardware settings.
8818 * @param pDbg Pointer to the debugging settings.
8819 * @param pAutostart Pointer to the autostart settings.
8820 */
8821HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8822 const Guid *puuidSnapshot,
8823 const settings::Hardware &data,
8824 const settings::Debugging *pDbg,
8825 const settings::Autostart *pAutostart)
8826{
8827 AssertReturn(!i_isSessionMachine(), E_FAIL);
8828
8829 HRESULT rc = S_OK;
8830
8831 try
8832 {
8833 ComObjPtr<GuestOSType> pGuestOSType;
8834 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8835
8836 /* The hardware version attribute (optional). */
8837 mHWData->mHWVersion = data.strVersion;
8838 mHWData->mHardwareUUID = data.uuid;
8839
8840 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8841 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8842 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8843 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8844 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8845 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8846 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8847 mHWData->mPAEEnabled = data.fPAE;
8848 mHWData->mLongMode = data.enmLongMode;
8849 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8850 mHWData->mAPIC = data.fAPIC;
8851 mHWData->mX2APIC = data.fX2APIC;
8852 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8853 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8854 mHWData->mSpecCtrl = data.fSpecCtrl;
8855 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8856 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8857 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8858 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8859 mHWData->mCPUCount = data.cCPUs;
8860 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8861 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8862 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8863 mHWData->mCpuProfile = data.strCpuProfile;
8864
8865 // cpu
8866 if (mHWData->mCPUHotPlugEnabled)
8867 {
8868 for (settings::CpuList::const_iterator
8869 it = data.llCpus.begin();
8870 it != data.llCpus.end();
8871 ++it)
8872 {
8873 const settings::Cpu &cpu = *it;
8874
8875 mHWData->mCPUAttached[cpu.ulId] = true;
8876 }
8877 }
8878
8879 // cpuid leafs
8880 for (settings::CpuIdLeafsList::const_iterator
8881 it = data.llCpuIdLeafs.begin();
8882 it != data.llCpuIdLeafs.end();
8883 ++it)
8884 {
8885 const settings::CpuIdLeaf &rLeaf= *it;
8886 if ( rLeaf.idx < UINT32_C(0x20)
8887 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8888 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8889 mHWData->mCpuIdLeafList.push_back(rLeaf);
8890 /* else: just ignore */
8891 }
8892
8893 mHWData->mMemorySize = data.ulMemorySizeMB;
8894 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8895
8896 // boot order
8897 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8898 {
8899 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8900 if (it == data.mapBootOrder.end())
8901 mHWData->mBootOrder[i] = DeviceType_Null;
8902 else
8903 mHWData->mBootOrder[i] = it->second;
8904 }
8905
8906 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8907 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8908 mHWData->mMonitorCount = data.cMonitors;
8909 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8910 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8911 mHWData->mFirmwareType = data.firmwareType;
8912 mHWData->mPointingHIDType = data.pointingHIDType;
8913 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8914 mHWData->mChipsetType = data.chipsetType;
8915 mHWData->mParavirtProvider = data.paravirtProvider;
8916 mHWData->mParavirtDebug = data.strParavirtDebug;
8917 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8918 mHWData->mHPETEnabled = data.fHPETEnabled;
8919
8920 /* VRDEServer */
8921 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8922 if (FAILED(rc)) return rc;
8923
8924 /* BIOS */
8925 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8926 if (FAILED(rc)) return rc;
8927
8928 /* Recording settings */
8929 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8930 if (FAILED(rc)) return rc;
8931
8932 // Bandwidth control (must come before network adapters)
8933 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8934 if (FAILED(rc)) return rc;
8935
8936 /* USB controllers */
8937 for (settings::USBControllerList::const_iterator
8938 it = data.usbSettings.llUSBControllers.begin();
8939 it != data.usbSettings.llUSBControllers.end();
8940 ++it)
8941 {
8942 const settings::USBController &settingsCtrl = *it;
8943 ComObjPtr<USBController> newCtrl;
8944
8945 newCtrl.createObject();
8946 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8947 mUSBControllers->push_back(newCtrl);
8948 }
8949
8950 /* USB device filters */
8951 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8952 if (FAILED(rc)) return rc;
8953
8954 // network adapters (establish array size first and apply defaults, to
8955 // ensure reading the same settings as we saved, since the list skips
8956 // adapters having defaults)
8957 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8958 size_t oldCount = mNetworkAdapters.size();
8959 if (newCount > oldCount)
8960 {
8961 mNetworkAdapters.resize(newCount);
8962 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8963 {
8964 unconst(mNetworkAdapters[slot]).createObject();
8965 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8966 }
8967 }
8968 else if (newCount < oldCount)
8969 mNetworkAdapters.resize(newCount);
8970 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8971 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8972 for (settings::NetworkAdaptersList::const_iterator
8973 it = data.llNetworkAdapters.begin();
8974 it != data.llNetworkAdapters.end();
8975 ++it)
8976 {
8977 const settings::NetworkAdapter &nic = *it;
8978
8979 /* slot uniqueness is guaranteed by XML Schema */
8980 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8981 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8982 if (FAILED(rc)) return rc;
8983 }
8984
8985 // serial ports (establish defaults first, to ensure reading the same
8986 // settings as we saved, since the list skips ports having defaults)
8987 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8988 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8989 for (settings::SerialPortsList::const_iterator
8990 it = data.llSerialPorts.begin();
8991 it != data.llSerialPorts.end();
8992 ++it)
8993 {
8994 const settings::SerialPort &s = *it;
8995
8996 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8997 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8998 if (FAILED(rc)) return rc;
8999 }
9000
9001 // parallel ports (establish defaults first, to ensure reading the same
9002 // settings as we saved, since the list skips ports having defaults)
9003 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
9004 mParallelPorts[i]->i_applyDefaults();
9005 for (settings::ParallelPortsList::const_iterator
9006 it = data.llParallelPorts.begin();
9007 it != data.llParallelPorts.end();
9008 ++it)
9009 {
9010 const settings::ParallelPort &p = *it;
9011
9012 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
9013 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
9014 if (FAILED(rc)) return rc;
9015 }
9016
9017 /* AudioAdapter */
9018 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
9019 if (FAILED(rc)) return rc;
9020
9021 /* storage controllers */
9022 rc = i_loadStorageControllers(data.storage,
9023 puuidRegistry,
9024 puuidSnapshot);
9025 if (FAILED(rc)) return rc;
9026
9027 /* Shared folders */
9028 for (settings::SharedFoldersList::const_iterator
9029 it = data.llSharedFolders.begin();
9030 it != data.llSharedFolders.end();
9031 ++it)
9032 {
9033 const settings::SharedFolder &sf = *it;
9034
9035 ComObjPtr<SharedFolder> sharedFolder;
9036 /* Check for double entries. Not allowed! */
9037 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9038 if (SUCCEEDED(rc))
9039 return setError(VBOX_E_OBJECT_IN_USE,
9040 tr("Shared folder named '%s' already exists"),
9041 sf.strName.c_str());
9042
9043 /* Create the new shared folder. Don't break on error. This will be
9044 * reported when the machine starts. */
9045 sharedFolder.createObject();
9046 rc = sharedFolder->init(i_getMachine(),
9047 sf.strName,
9048 sf.strHostPath,
9049 RT_BOOL(sf.fWritable),
9050 RT_BOOL(sf.fAutoMount),
9051 sf.strAutoMountPoint,
9052 false /* fFailOnError */);
9053 if (FAILED(rc)) return rc;
9054 mHWData->mSharedFolders.push_back(sharedFolder);
9055 }
9056
9057 // Clipboard
9058 mHWData->mClipboardMode = data.clipboardMode;
9059
9060 // drag'n'drop
9061 mHWData->mDnDMode = data.dndMode;
9062
9063 // guest settings
9064 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9065
9066 // IO settings
9067 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9068 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9069
9070 // Host PCI devices
9071 for (settings::HostPCIDeviceAttachmentList::const_iterator
9072 it = data.pciAttachments.begin();
9073 it != data.pciAttachments.end();
9074 ++it)
9075 {
9076 const settings::HostPCIDeviceAttachment &hpda = *it;
9077 ComObjPtr<PCIDeviceAttachment> pda;
9078
9079 pda.createObject();
9080 pda->i_loadSettings(this, hpda);
9081 mHWData->mPCIDeviceAssignments.push_back(pda);
9082 }
9083
9084 /*
9085 * (The following isn't really real hardware, but it lives in HWData
9086 * for reasons of convenience.)
9087 */
9088
9089#ifdef VBOX_WITH_GUEST_PROPS
9090 /* Guest properties (optional) */
9091
9092 /* Only load transient guest properties for configs which have saved
9093 * state, because there shouldn't be any for powered off VMs. The same
9094 * logic applies for snapshots, as offline snapshots shouldn't have
9095 * any such properties. They confuse the code in various places.
9096 * Note: can't rely on the machine state, as it isn't set yet. */
9097 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9098 /* apologies for the hacky unconst() usage, but this needs hacking
9099 * actually inconsistent settings into consistency, otherwise there
9100 * will be some corner cases where the inconsistency survives
9101 * surprisingly long without getting fixed, especially for snapshots
9102 * as there are no config changes. */
9103 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9104 for (settings::GuestPropertiesList::iterator
9105 it = llGuestProperties.begin();
9106 it != llGuestProperties.end();
9107 /*nothing*/)
9108 {
9109 const settings::GuestProperty &prop = *it;
9110 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9111 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9112 if ( fSkipTransientGuestProperties
9113 && ( fFlags & GUEST_PROP_F_TRANSIENT
9114 || fFlags & GUEST_PROP_F_TRANSRESET))
9115 {
9116 it = llGuestProperties.erase(it);
9117 continue;
9118 }
9119 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9120 mHWData->mGuestProperties[prop.strName] = property;
9121 ++it;
9122 }
9123#endif /* VBOX_WITH_GUEST_PROPS defined */
9124
9125 rc = i_loadDebugging(pDbg);
9126 if (FAILED(rc))
9127 return rc;
9128
9129 mHWData->mAutostart = *pAutostart;
9130
9131 /* default frontend */
9132 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9133 }
9134 catch (std::bad_alloc &)
9135 {
9136 return E_OUTOFMEMORY;
9137 }
9138
9139 AssertComRC(rc);
9140 return rc;
9141}
9142
9143/**
9144 * Called from i_loadHardware() to load the debugging settings of the
9145 * machine.
9146 *
9147 * @param pDbg Pointer to the settings.
9148 */
9149HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9150{
9151 mHWData->mDebugging = *pDbg;
9152 /* no more processing currently required, this will probably change. */
9153 return S_OK;
9154}
9155
9156/**
9157 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9158 *
9159 * @param data storage settings.
9160 * @param puuidRegistry media registry ID to set media to or NULL;
9161 * see Machine::i_loadMachineDataFromSettings()
9162 * @param puuidSnapshot snapshot ID
9163 * @return
9164 */
9165HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9166 const Guid *puuidRegistry,
9167 const Guid *puuidSnapshot)
9168{
9169 AssertReturn(!i_isSessionMachine(), E_FAIL);
9170
9171 HRESULT rc = S_OK;
9172
9173 for (settings::StorageControllersList::const_iterator
9174 it = data.llStorageControllers.begin();
9175 it != data.llStorageControllers.end();
9176 ++it)
9177 {
9178 const settings::StorageController &ctlData = *it;
9179
9180 ComObjPtr<StorageController> pCtl;
9181 /* Try to find one with the name first. */
9182 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9183 if (SUCCEEDED(rc))
9184 return setError(VBOX_E_OBJECT_IN_USE,
9185 tr("Storage controller named '%s' already exists"),
9186 ctlData.strName.c_str());
9187
9188 pCtl.createObject();
9189 rc = pCtl->init(this,
9190 ctlData.strName,
9191 ctlData.storageBus,
9192 ctlData.ulInstance,
9193 ctlData.fBootable);
9194 if (FAILED(rc)) return rc;
9195
9196 mStorageControllers->push_back(pCtl);
9197
9198 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9199 if (FAILED(rc)) return rc;
9200
9201 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9202 if (FAILED(rc)) return rc;
9203
9204 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9205 if (FAILED(rc)) return rc;
9206
9207 /* Load the attached devices now. */
9208 rc = i_loadStorageDevices(pCtl,
9209 ctlData,
9210 puuidRegistry,
9211 puuidSnapshot);
9212 if (FAILED(rc)) return rc;
9213 }
9214
9215 return S_OK;
9216}
9217
9218/**
9219 * Called from i_loadStorageControllers for a controller's devices.
9220 *
9221 * @param aStorageController
9222 * @param data
9223 * @param puuidRegistry media registry ID to set media to or NULL; see
9224 * Machine::i_loadMachineDataFromSettings()
9225 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9226 * @return
9227 */
9228HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9229 const settings::StorageController &data,
9230 const Guid *puuidRegistry,
9231 const Guid *puuidSnapshot)
9232{
9233 HRESULT rc = S_OK;
9234
9235 /* paranoia: detect duplicate attachments */
9236 for (settings::AttachedDevicesList::const_iterator
9237 it = data.llAttachedDevices.begin();
9238 it != data.llAttachedDevices.end();
9239 ++it)
9240 {
9241 const settings::AttachedDevice &ad = *it;
9242
9243 for (settings::AttachedDevicesList::const_iterator it2 = it;
9244 it2 != data.llAttachedDevices.end();
9245 ++it2)
9246 {
9247 if (it == it2)
9248 continue;
9249
9250 const settings::AttachedDevice &ad2 = *it2;
9251
9252 if ( ad.lPort == ad2.lPort
9253 && ad.lDevice == ad2.lDevice)
9254 {
9255 return setError(E_FAIL,
9256 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9257 aStorageController->i_getName().c_str(),
9258 ad.lPort,
9259 ad.lDevice,
9260 mUserData->s.strName.c_str());
9261 }
9262 }
9263 }
9264
9265 for (settings::AttachedDevicesList::const_iterator
9266 it = data.llAttachedDevices.begin();
9267 it != data.llAttachedDevices.end();
9268 ++it)
9269 {
9270 const settings::AttachedDevice &dev = *it;
9271 ComObjPtr<Medium> medium;
9272
9273 switch (dev.deviceType)
9274 {
9275 case DeviceType_Floppy:
9276 case DeviceType_DVD:
9277 if (dev.strHostDriveSrc.isNotEmpty())
9278 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9279 false /* fRefresh */, medium);
9280 else
9281 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9282 dev.uuid,
9283 false /* fRefresh */,
9284 false /* aSetError */,
9285 medium);
9286 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9287 // This is not an error. The host drive or UUID might have vanished, so just go
9288 // ahead without this removeable medium attachment
9289 rc = S_OK;
9290 break;
9291
9292 case DeviceType_HardDisk:
9293 {
9294 /* find a hard disk by UUID */
9295 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9296 if (FAILED(rc))
9297 {
9298 if (i_isSnapshotMachine())
9299 {
9300 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9301 // so the user knows that the bad disk is in a snapshot somewhere
9302 com::ErrorInfo info;
9303 return setError(E_FAIL,
9304 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9305 puuidSnapshot->raw(),
9306 info.getText().raw());
9307 }
9308 else
9309 return rc;
9310 }
9311
9312 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9313
9314 if (medium->i_getType() == MediumType_Immutable)
9315 {
9316 if (i_isSnapshotMachine())
9317 return setError(E_FAIL,
9318 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9319 "of the virtual machine '%s' ('%s')"),
9320 medium->i_getLocationFull().c_str(),
9321 dev.uuid.raw(),
9322 puuidSnapshot->raw(),
9323 mUserData->s.strName.c_str(),
9324 mData->m_strConfigFileFull.c_str());
9325
9326 return setError(E_FAIL,
9327 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9328 medium->i_getLocationFull().c_str(),
9329 dev.uuid.raw(),
9330 mUserData->s.strName.c_str(),
9331 mData->m_strConfigFileFull.c_str());
9332 }
9333
9334 if (medium->i_getType() == MediumType_MultiAttach)
9335 {
9336 if (i_isSnapshotMachine())
9337 return setError(E_FAIL,
9338 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9339 "of the virtual machine '%s' ('%s')"),
9340 medium->i_getLocationFull().c_str(),
9341 dev.uuid.raw(),
9342 puuidSnapshot->raw(),
9343 mUserData->s.strName.c_str(),
9344 mData->m_strConfigFileFull.c_str());
9345
9346 return setError(E_FAIL,
9347 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9348 medium->i_getLocationFull().c_str(),
9349 dev.uuid.raw(),
9350 mUserData->s.strName.c_str(),
9351 mData->m_strConfigFileFull.c_str());
9352 }
9353
9354 if ( !i_isSnapshotMachine()
9355 && medium->i_getChildren().size() != 0
9356 )
9357 return setError(E_FAIL,
9358 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9359 "because it has %d differencing child hard disks"),
9360 medium->i_getLocationFull().c_str(),
9361 dev.uuid.raw(),
9362 mUserData->s.strName.c_str(),
9363 mData->m_strConfigFileFull.c_str(),
9364 medium->i_getChildren().size());
9365
9366 if (i_findAttachment(*mMediumAttachments.data(),
9367 medium))
9368 return setError(E_FAIL,
9369 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9370 medium->i_getLocationFull().c_str(),
9371 dev.uuid.raw(),
9372 mUserData->s.strName.c_str(),
9373 mData->m_strConfigFileFull.c_str());
9374
9375 break;
9376 }
9377
9378 default:
9379 return setError(E_FAIL,
9380 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9381 medium->i_getLocationFull().c_str(),
9382 mUserData->s.strName.c_str(),
9383 mData->m_strConfigFileFull.c_str());
9384 }
9385
9386 if (FAILED(rc))
9387 break;
9388
9389 /* Bandwidth groups are loaded at this point. */
9390 ComObjPtr<BandwidthGroup> pBwGroup;
9391
9392 if (!dev.strBwGroup.isEmpty())
9393 {
9394 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9395 if (FAILED(rc))
9396 return setError(E_FAIL,
9397 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9398 medium->i_getLocationFull().c_str(),
9399 dev.strBwGroup.c_str(),
9400 mUserData->s.strName.c_str(),
9401 mData->m_strConfigFileFull.c_str());
9402 pBwGroup->i_reference();
9403 }
9404
9405 const Utf8Str controllerName = aStorageController->i_getName();
9406 ComObjPtr<MediumAttachment> pAttachment;
9407 pAttachment.createObject();
9408 rc = pAttachment->init(this,
9409 medium,
9410 controllerName,
9411 dev.lPort,
9412 dev.lDevice,
9413 dev.deviceType,
9414 false,
9415 dev.fPassThrough,
9416 dev.fTempEject,
9417 dev.fNonRotational,
9418 dev.fDiscard,
9419 dev.fHotPluggable,
9420 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9421 if (FAILED(rc)) break;
9422
9423 /* associate the medium with this machine and snapshot */
9424 if (!medium.isNull())
9425 {
9426 AutoCaller medCaller(medium);
9427 if (FAILED(medCaller.rc())) return medCaller.rc();
9428 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9429
9430 if (i_isSnapshotMachine())
9431 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9432 else
9433 rc = medium->i_addBackReference(mData->mUuid);
9434 /* If the medium->addBackReference fails it sets an appropriate
9435 * error message, so no need to do any guesswork here. */
9436
9437 if (puuidRegistry)
9438 // caller wants registry ID to be set on all attached media (OVF import case)
9439 medium->i_addRegistry(*puuidRegistry);
9440 }
9441
9442 if (FAILED(rc))
9443 break;
9444
9445 /* back up mMediumAttachments to let registeredInit() properly rollback
9446 * on failure (= limited accessibility) */
9447 i_setModified(IsModified_Storage);
9448 mMediumAttachments.backup();
9449 mMediumAttachments->push_back(pAttachment);
9450 }
9451
9452 return rc;
9453}
9454
9455/**
9456 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9457 *
9458 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9459 * @param aSnapshot where to return the found snapshot
9460 * @param aSetError true to set extended error info on failure
9461 */
9462HRESULT Machine::i_findSnapshotById(const Guid &aId,
9463 ComObjPtr<Snapshot> &aSnapshot,
9464 bool aSetError /* = false */)
9465{
9466 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9467
9468 if (!mData->mFirstSnapshot)
9469 {
9470 if (aSetError)
9471 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9472 return E_FAIL;
9473 }
9474
9475 if (aId.isZero())
9476 aSnapshot = mData->mFirstSnapshot;
9477 else
9478 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9479
9480 if (!aSnapshot)
9481 {
9482 if (aSetError)
9483 return setError(E_FAIL,
9484 tr("Could not find a snapshot with UUID {%s}"),
9485 aId.toString().c_str());
9486 return E_FAIL;
9487 }
9488
9489 return S_OK;
9490}
9491
9492/**
9493 * Returns the snapshot with the given name or fails of no such snapshot.
9494 *
9495 * @param strName snapshot name to find
9496 * @param aSnapshot where to return the found snapshot
9497 * @param aSetError true to set extended error info on failure
9498 */
9499HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9500 ComObjPtr<Snapshot> &aSnapshot,
9501 bool aSetError /* = false */)
9502{
9503 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9504
9505 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9506
9507 if (!mData->mFirstSnapshot)
9508 {
9509 if (aSetError)
9510 return setError(VBOX_E_OBJECT_NOT_FOUND,
9511 tr("This machine does not have any snapshots"));
9512 return VBOX_E_OBJECT_NOT_FOUND;
9513 }
9514
9515 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9516
9517 if (!aSnapshot)
9518 {
9519 if (aSetError)
9520 return setError(VBOX_E_OBJECT_NOT_FOUND,
9521 tr("Could not find a snapshot named '%s'"), strName.c_str());
9522 return VBOX_E_OBJECT_NOT_FOUND;
9523 }
9524
9525 return S_OK;
9526}
9527
9528/**
9529 * Returns a storage controller object with the given name.
9530 *
9531 * @param aName storage controller name to find
9532 * @param aStorageController where to return the found storage controller
9533 * @param aSetError true to set extended error info on failure
9534 */
9535HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9536 ComObjPtr<StorageController> &aStorageController,
9537 bool aSetError /* = false */)
9538{
9539 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9540
9541 for (StorageControllerList::const_iterator
9542 it = mStorageControllers->begin();
9543 it != mStorageControllers->end();
9544 ++it)
9545 {
9546 if ((*it)->i_getName() == aName)
9547 {
9548 aStorageController = (*it);
9549 return S_OK;
9550 }
9551 }
9552
9553 if (aSetError)
9554 return setError(VBOX_E_OBJECT_NOT_FOUND,
9555 tr("Could not find a storage controller named '%s'"),
9556 aName.c_str());
9557 return VBOX_E_OBJECT_NOT_FOUND;
9558}
9559
9560/**
9561 * Returns a USB controller object with the given name.
9562 *
9563 * @param aName USB controller name to find
9564 * @param aUSBController where to return the found USB controller
9565 * @param aSetError true to set extended error info on failure
9566 */
9567HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9568 ComObjPtr<USBController> &aUSBController,
9569 bool aSetError /* = false */)
9570{
9571 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9572
9573 for (USBControllerList::const_iterator
9574 it = mUSBControllers->begin();
9575 it != mUSBControllers->end();
9576 ++it)
9577 {
9578 if ((*it)->i_getName() == aName)
9579 {
9580 aUSBController = (*it);
9581 return S_OK;
9582 }
9583 }
9584
9585 if (aSetError)
9586 return setError(VBOX_E_OBJECT_NOT_FOUND,
9587 tr("Could not find a storage controller named '%s'"),
9588 aName.c_str());
9589 return VBOX_E_OBJECT_NOT_FOUND;
9590}
9591
9592/**
9593 * Returns the number of USB controller instance of the given type.
9594 *
9595 * @param enmType USB controller type.
9596 */
9597ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9598{
9599 ULONG cCtrls = 0;
9600
9601 for (USBControllerList::const_iterator
9602 it = mUSBControllers->begin();
9603 it != mUSBControllers->end();
9604 ++it)
9605 {
9606 if ((*it)->i_getControllerType() == enmType)
9607 cCtrls++;
9608 }
9609
9610 return cCtrls;
9611}
9612
9613HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9614 MediumAttachmentList &atts)
9615{
9616 AutoCaller autoCaller(this);
9617 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9618
9619 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9620
9621 for (MediumAttachmentList::const_iterator
9622 it = mMediumAttachments->begin();
9623 it != mMediumAttachments->end();
9624 ++it)
9625 {
9626 const ComObjPtr<MediumAttachment> &pAtt = *it;
9627 // should never happen, but deal with NULL pointers in the list.
9628 AssertContinue(!pAtt.isNull());
9629
9630 // getControllerName() needs caller+read lock
9631 AutoCaller autoAttCaller(pAtt);
9632 if (FAILED(autoAttCaller.rc()))
9633 {
9634 atts.clear();
9635 return autoAttCaller.rc();
9636 }
9637 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9638
9639 if (pAtt->i_getControllerName() == aName)
9640 atts.push_back(pAtt);
9641 }
9642
9643 return S_OK;
9644}
9645
9646
9647/**
9648 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9649 * file if the machine name was changed and about creating a new settings file
9650 * if this is a new machine.
9651 *
9652 * @note Must be never called directly but only from #saveSettings().
9653 */
9654HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9655{
9656 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9657
9658 HRESULT rc = S_OK;
9659
9660 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9661
9662 /// @todo need to handle primary group change, too
9663
9664 /* attempt to rename the settings file if machine name is changed */
9665 if ( mUserData->s.fNameSync
9666 && mUserData.isBackedUp()
9667 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9668 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9669 )
9670 {
9671 bool dirRenamed = false;
9672 bool fileRenamed = false;
9673
9674 Utf8Str configFile, newConfigFile;
9675 Utf8Str configFilePrev, newConfigFilePrev;
9676 Utf8Str configDir, newConfigDir;
9677
9678 do
9679 {
9680 int vrc = VINF_SUCCESS;
9681
9682 Utf8Str name = mUserData.backedUpData()->s.strName;
9683 Utf8Str newName = mUserData->s.strName;
9684 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9685 if (group == "/")
9686 group.setNull();
9687 Utf8Str newGroup = mUserData->s.llGroups.front();
9688 if (newGroup == "/")
9689 newGroup.setNull();
9690
9691 configFile = mData->m_strConfigFileFull;
9692
9693 /* first, rename the directory if it matches the group and machine name */
9694 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9695 group.c_str(), RTPATH_DELIMITER, name.c_str());
9696 /** @todo hack, make somehow use of ComposeMachineFilename */
9697 if (mUserData->s.fDirectoryIncludesUUID)
9698 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9699 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9700 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9701 /** @todo hack, make somehow use of ComposeMachineFilename */
9702 if (mUserData->s.fDirectoryIncludesUUID)
9703 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9704 configDir = configFile;
9705 configDir.stripFilename();
9706 newConfigDir = configDir;
9707 if ( configDir.length() >= groupPlusName.length()
9708 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9709 groupPlusName.c_str()))
9710 {
9711 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9712 Utf8Str newConfigBaseDir(newConfigDir);
9713 newConfigDir.append(newGroupPlusName);
9714 /* consistency: use \ if appropriate on the platform */
9715 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9716 /* new dir and old dir cannot be equal here because of 'if'
9717 * above and because name != newName */
9718 Assert(configDir != newConfigDir);
9719 if (!fSettingsFileIsNew)
9720 {
9721 /* perform real rename only if the machine is not new */
9722 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9723 if ( vrc == VERR_FILE_NOT_FOUND
9724 || vrc == VERR_PATH_NOT_FOUND)
9725 {
9726 /* create the parent directory, then retry renaming */
9727 Utf8Str parent(newConfigDir);
9728 parent.stripFilename();
9729 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9730 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9731 }
9732 if (RT_FAILURE(vrc))
9733 {
9734 rc = setErrorBoth(E_FAIL, vrc,
9735 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9736 configDir.c_str(),
9737 newConfigDir.c_str(),
9738 vrc);
9739 break;
9740 }
9741 /* delete subdirectories which are no longer needed */
9742 Utf8Str dir(configDir);
9743 dir.stripFilename();
9744 while (dir != newConfigBaseDir && dir != ".")
9745 {
9746 vrc = RTDirRemove(dir.c_str());
9747 if (RT_FAILURE(vrc))
9748 break;
9749 dir.stripFilename();
9750 }
9751 dirRenamed = true;
9752 }
9753 }
9754
9755 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9756 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9757
9758 /* then try to rename the settings file itself */
9759 if (newConfigFile != configFile)
9760 {
9761 /* get the path to old settings file in renamed directory */
9762 configFile = Utf8StrFmt("%s%c%s",
9763 newConfigDir.c_str(),
9764 RTPATH_DELIMITER,
9765 RTPathFilename(configFile.c_str()));
9766 if (!fSettingsFileIsNew)
9767 {
9768 /* perform real rename only if the machine is not new */
9769 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9770 if (RT_FAILURE(vrc))
9771 {
9772 rc = setErrorBoth(E_FAIL, vrc,
9773 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9774 configFile.c_str(),
9775 newConfigFile.c_str(),
9776 vrc);
9777 break;
9778 }
9779 fileRenamed = true;
9780 configFilePrev = configFile;
9781 configFilePrev += "-prev";
9782 newConfigFilePrev = newConfigFile;
9783 newConfigFilePrev += "-prev";
9784 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9785 }
9786 }
9787
9788 // update m_strConfigFileFull amd mConfigFile
9789 mData->m_strConfigFileFull = newConfigFile;
9790 // compute the relative path too
9791 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9792
9793 // store the old and new so that VirtualBox::i_saveSettings() can update
9794 // the media registry
9795 if ( mData->mRegistered
9796 && (configDir != newConfigDir || configFile != newConfigFile))
9797 {
9798 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9799
9800 if (pfNeedsGlobalSaveSettings)
9801 *pfNeedsGlobalSaveSettings = true;
9802 }
9803
9804 // in the saved state file path, replace the old directory with the new directory
9805 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9806 {
9807 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9808 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9809 }
9810
9811 // and do the same thing for the saved state file paths of all the online snapshots
9812 if (mData->mFirstSnapshot)
9813 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9814 newConfigDir.c_str());
9815 }
9816 while (0);
9817
9818 if (FAILED(rc))
9819 {
9820 /* silently try to rename everything back */
9821 if (fileRenamed)
9822 {
9823 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9824 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9825 }
9826 if (dirRenamed)
9827 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9828 }
9829
9830 if (FAILED(rc)) return rc;
9831 }
9832
9833 if (fSettingsFileIsNew)
9834 {
9835 /* create a virgin config file */
9836 int vrc = VINF_SUCCESS;
9837
9838 /* ensure the settings directory exists */
9839 Utf8Str path(mData->m_strConfigFileFull);
9840 path.stripFilename();
9841 if (!RTDirExists(path.c_str()))
9842 {
9843 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9844 if (RT_FAILURE(vrc))
9845 {
9846 return setErrorBoth(E_FAIL, vrc,
9847 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9848 path.c_str(),
9849 vrc);
9850 }
9851 }
9852
9853 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9854 path = Utf8Str(mData->m_strConfigFileFull);
9855 RTFILE f = NIL_RTFILE;
9856 vrc = RTFileOpen(&f, path.c_str(),
9857 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9858 if (RT_FAILURE(vrc))
9859 return setErrorBoth(E_FAIL, vrc,
9860 tr("Could not create the settings file '%s' (%Rrc)"),
9861 path.c_str(),
9862 vrc);
9863 RTFileClose(f);
9864 }
9865
9866 return rc;
9867}
9868
9869/**
9870 * Saves and commits machine data, user data and hardware data.
9871 *
9872 * Note that on failure, the data remains uncommitted.
9873 *
9874 * @a aFlags may combine the following flags:
9875 *
9876 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9877 * Used when saving settings after an operation that makes them 100%
9878 * correspond to the settings from the current snapshot.
9879 * - SaveS_Force: settings will be saved without doing a deep compare of the
9880 * settings structures. This is used when this is called because snapshots
9881 * have changed to avoid the overhead of the deep compare.
9882 *
9883 * @note Must be called from under this object's write lock. Locks children for
9884 * writing.
9885 *
9886 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9887 * initialized to false and that will be set to true by this function if
9888 * the caller must invoke VirtualBox::i_saveSettings() because the global
9889 * settings have changed. This will happen if a machine rename has been
9890 * saved and the global machine and media registries will therefore need
9891 * updating.
9892 * @param aFlags Flags.
9893 */
9894HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9895 int aFlags /*= 0*/)
9896{
9897 LogFlowThisFuncEnter();
9898
9899 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9900
9901 /* make sure child objects are unable to modify the settings while we are
9902 * saving them */
9903 i_ensureNoStateDependencies();
9904
9905 AssertReturn(!i_isSnapshotMachine(),
9906 E_FAIL);
9907
9908 HRESULT rc = S_OK;
9909 bool fNeedsWrite = false;
9910
9911 /* First, prepare to save settings. It will care about renaming the
9912 * settings directory and file if the machine name was changed and about
9913 * creating a new settings file if this is a new machine. */
9914 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9915 if (FAILED(rc)) return rc;
9916
9917 // keep a pointer to the current settings structures
9918 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9919 settings::MachineConfigFile *pNewConfig = NULL;
9920
9921 try
9922 {
9923 // make a fresh one to have everyone write stuff into
9924 pNewConfig = new settings::MachineConfigFile(NULL);
9925 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9926
9927 // now go and copy all the settings data from COM to the settings structures
9928 // (this calls i_saveSettings() on all the COM objects in the machine)
9929 i_copyMachineDataToSettings(*pNewConfig);
9930
9931 if (aFlags & SaveS_ResetCurStateModified)
9932 {
9933 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9934 mData->mCurrentStateModified = FALSE;
9935 fNeedsWrite = true; // always, no need to compare
9936 }
9937 else if (aFlags & SaveS_Force)
9938 {
9939 fNeedsWrite = true; // always, no need to compare
9940 }
9941 else
9942 {
9943 if (!mData->mCurrentStateModified)
9944 {
9945 // do a deep compare of the settings that we just saved with the settings
9946 // previously stored in the config file; this invokes MachineConfigFile::operator==
9947 // which does a deep compare of all the settings, which is expensive but less expensive
9948 // than writing out XML in vain
9949 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9950
9951 // could still be modified if any settings changed
9952 mData->mCurrentStateModified = fAnySettingsChanged;
9953
9954 fNeedsWrite = fAnySettingsChanged;
9955 }
9956 else
9957 fNeedsWrite = true;
9958 }
9959
9960 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9961
9962 if (fNeedsWrite)
9963 // now spit it all out!
9964 pNewConfig->write(mData->m_strConfigFileFull);
9965
9966 mData->pMachineConfigFile = pNewConfig;
9967 delete pOldConfig;
9968 i_commit();
9969
9970 // after saving settings, we are no longer different from the XML on disk
9971 mData->flModifications = 0;
9972 }
9973 catch (HRESULT err)
9974 {
9975 // we assume that error info is set by the thrower
9976 rc = err;
9977
9978 // restore old config
9979 delete pNewConfig;
9980 mData->pMachineConfigFile = pOldConfig;
9981 }
9982 catch (...)
9983 {
9984 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9985 }
9986
9987 if (fNeedsWrite)
9988 {
9989 /* Fire the data change event, even on failure (since we've already
9990 * committed all data). This is done only for SessionMachines because
9991 * mutable Machine instances are always not registered (i.e. private
9992 * to the client process that creates them) and thus don't need to
9993 * inform callbacks. */
9994 if (i_isSessionMachine())
9995 mParent->i_onMachineDataChange(mData->mUuid);
9996 }
9997
9998 LogFlowThisFunc(("rc=%08X\n", rc));
9999 LogFlowThisFuncLeave();
10000 return rc;
10001}
10002
10003/**
10004 * Implementation for saving the machine settings into the given
10005 * settings::MachineConfigFile instance. This copies machine extradata
10006 * from the previous machine config file in the instance data, if any.
10007 *
10008 * This gets called from two locations:
10009 *
10010 * -- Machine::i_saveSettings(), during the regular XML writing;
10011 *
10012 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
10013 * exported to OVF and we write the VirtualBox proprietary XML
10014 * into a <vbox:Machine> tag.
10015 *
10016 * This routine fills all the fields in there, including snapshots, *except*
10017 * for the following:
10018 *
10019 * -- fCurrentStateModified. There is some special logic associated with that.
10020 *
10021 * The caller can then call MachineConfigFile::write() or do something else
10022 * with it.
10023 *
10024 * Caller must hold the machine lock!
10025 *
10026 * This throws XML errors and HRESULT, so the caller must have a catch block!
10027 */
10028void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
10029{
10030 // deep copy extradata, being extra careful with self assignment (the STL
10031 // map assignment on Mac OS X clang based Xcode isn't checking)
10032 if (&config != mData->pMachineConfigFile)
10033 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
10034
10035 config.uuid = mData->mUuid;
10036
10037 // copy name, description, OS type, teleport, UTC etc.
10038 config.machineUserData = mUserData->s;
10039
10040 if ( mData->mMachineState == MachineState_Saved
10041 || mData->mMachineState == MachineState_Restoring
10042 // when doing certain snapshot operations we may or may not have
10043 // a saved state in the current state, so keep everything as is
10044 || ( ( mData->mMachineState == MachineState_Snapshotting
10045 || mData->mMachineState == MachineState_DeletingSnapshot
10046 || mData->mMachineState == MachineState_RestoringSnapshot)
10047 && (!mSSData->strStateFilePath.isEmpty())
10048 )
10049 )
10050 {
10051 Assert(!mSSData->strStateFilePath.isEmpty());
10052 /* try to make the file name relative to the settings file dir */
10053 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10054 }
10055 else
10056 {
10057 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10058 config.strStateFile.setNull();
10059 }
10060
10061 if (mData->mCurrentSnapshot)
10062 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10063 else
10064 config.uuidCurrentSnapshot.clear();
10065
10066 config.timeLastStateChange = mData->mLastStateChange;
10067 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10068 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10069
10070 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10071 if (FAILED(rc)) throw rc;
10072
10073 // save machine's media registry if this is VirtualBox 4.0 or later
10074 if (config.canHaveOwnMediaRegistry())
10075 {
10076 // determine machine folder
10077 Utf8Str strMachineFolder = i_getSettingsFileFull();
10078 strMachineFolder.stripFilename();
10079 mParent->i_saveMediaRegistry(config.mediaRegistry,
10080 i_getId(), // only media with registry ID == machine UUID
10081 strMachineFolder);
10082 // this throws HRESULT
10083 }
10084
10085 // save snapshots
10086 rc = i_saveAllSnapshots(config);
10087 if (FAILED(rc)) throw rc;
10088}
10089
10090/**
10091 * Saves all snapshots of the machine into the given machine config file. Called
10092 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10093 * @param config
10094 * @return
10095 */
10096HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10097{
10098 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10099
10100 HRESULT rc = S_OK;
10101
10102 try
10103 {
10104 config.llFirstSnapshot.clear();
10105
10106 if (mData->mFirstSnapshot)
10107 {
10108 // the settings use a list for "the first snapshot"
10109 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10110
10111 // get reference to the snapshot on the list and work on that
10112 // element straight in the list to avoid excessive copying later
10113 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10114 if (FAILED(rc)) throw rc;
10115 }
10116
10117// if (mType == IsSessionMachine)
10118// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10119
10120 }
10121 catch (HRESULT err)
10122 {
10123 /* we assume that error info is set by the thrower */
10124 rc = err;
10125 }
10126 catch (...)
10127 {
10128 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10129 }
10130
10131 return rc;
10132}
10133
10134/**
10135 * Saves the VM hardware configuration. It is assumed that the
10136 * given node is empty.
10137 *
10138 * @param data Reference to the settings object for the hardware config.
10139 * @param pDbg Pointer to the settings object for the debugging config
10140 * which happens to live in mHWData.
10141 * @param pAutostart Pointer to the settings object for the autostart config
10142 * which happens to live in mHWData.
10143 */
10144HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10145 settings::Autostart *pAutostart)
10146{
10147 HRESULT rc = S_OK;
10148
10149 try
10150 {
10151 /* The hardware version attribute (optional).
10152 Automatically upgrade from 1 to current default hardware version
10153 when there is no saved state. (ugly!) */
10154 if ( mHWData->mHWVersion == "1"
10155 && mSSData->strStateFilePath.isEmpty()
10156 )
10157 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10158
10159 data.strVersion = mHWData->mHWVersion;
10160 data.uuid = mHWData->mHardwareUUID;
10161
10162 // CPU
10163 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10164 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10165 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10166 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10167 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10168 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10169 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10170 data.fPAE = !!mHWData->mPAEEnabled;
10171 data.enmLongMode = mHWData->mLongMode;
10172 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10173 data.fAPIC = !!mHWData->mAPIC;
10174 data.fX2APIC = !!mHWData->mX2APIC;
10175 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10176 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10177 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10178 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10179 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10180 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10181 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10182 data.cCPUs = mHWData->mCPUCount;
10183 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10184 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10185 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10186 data.strCpuProfile = mHWData->mCpuProfile;
10187
10188 data.llCpus.clear();
10189 if (data.fCpuHotPlug)
10190 {
10191 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10192 {
10193 if (mHWData->mCPUAttached[idx])
10194 {
10195 settings::Cpu cpu;
10196 cpu.ulId = idx;
10197 data.llCpus.push_back(cpu);
10198 }
10199 }
10200 }
10201
10202 /* Standard and Extended CPUID leafs. */
10203 data.llCpuIdLeafs.clear();
10204 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10205
10206 // memory
10207 data.ulMemorySizeMB = mHWData->mMemorySize;
10208 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10209
10210 // firmware
10211 data.firmwareType = mHWData->mFirmwareType;
10212
10213 // HID
10214 data.pointingHIDType = mHWData->mPointingHIDType;
10215 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10216
10217 // chipset
10218 data.chipsetType = mHWData->mChipsetType;
10219
10220 // paravirt
10221 data.paravirtProvider = mHWData->mParavirtProvider;
10222 data.strParavirtDebug = mHWData->mParavirtDebug;
10223
10224 // emulated USB card reader
10225 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10226
10227 // HPET
10228 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10229
10230 // boot order
10231 data.mapBootOrder.clear();
10232 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10233 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10234
10235 // display
10236 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10237 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10238 data.cMonitors = mHWData->mMonitorCount;
10239 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10240 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10241
10242 /* VRDEServer settings (optional) */
10243 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10244 if (FAILED(rc)) throw rc;
10245
10246 /* BIOS settings (required) */
10247 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10248 if (FAILED(rc)) throw rc;
10249
10250 /* Recording settings (required) */
10251 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10252 if (FAILED(rc)) throw rc;
10253
10254 /* USB Controller (required) */
10255 data.usbSettings.llUSBControllers.clear();
10256 for (USBControllerList::const_iterator
10257 it = mUSBControllers->begin();
10258 it != mUSBControllers->end();
10259 ++it)
10260 {
10261 ComObjPtr<USBController> ctrl = *it;
10262 settings::USBController settingsCtrl;
10263
10264 settingsCtrl.strName = ctrl->i_getName();
10265 settingsCtrl.enmType = ctrl->i_getControllerType();
10266
10267 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10268 }
10269
10270 /* USB device filters (required) */
10271 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10272 if (FAILED(rc)) throw rc;
10273
10274 /* Network adapters (required) */
10275 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10276 data.llNetworkAdapters.clear();
10277 /* Write out only the nominal number of network adapters for this
10278 * chipset type. Since Machine::commit() hasn't been called there
10279 * may be extra NIC settings in the vector. */
10280 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10281 {
10282 settings::NetworkAdapter nic;
10283 nic.ulSlot = (uint32_t)slot;
10284 /* paranoia check... must not be NULL, but must not crash either. */
10285 if (mNetworkAdapters[slot])
10286 {
10287 if (mNetworkAdapters[slot]->i_hasDefaults())
10288 continue;
10289
10290 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10291 if (FAILED(rc)) throw rc;
10292
10293 data.llNetworkAdapters.push_back(nic);
10294 }
10295 }
10296
10297 /* Serial ports */
10298 data.llSerialPorts.clear();
10299 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10300 {
10301 if (mSerialPorts[slot]->i_hasDefaults())
10302 continue;
10303
10304 settings::SerialPort s;
10305 s.ulSlot = slot;
10306 rc = mSerialPorts[slot]->i_saveSettings(s);
10307 if (FAILED(rc)) return rc;
10308
10309 data.llSerialPorts.push_back(s);
10310 }
10311
10312 /* Parallel ports */
10313 data.llParallelPorts.clear();
10314 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10315 {
10316 if (mParallelPorts[slot]->i_hasDefaults())
10317 continue;
10318
10319 settings::ParallelPort p;
10320 p.ulSlot = slot;
10321 rc = mParallelPorts[slot]->i_saveSettings(p);
10322 if (FAILED(rc)) return rc;
10323
10324 data.llParallelPorts.push_back(p);
10325 }
10326
10327 /* Audio adapter */
10328 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10329 if (FAILED(rc)) return rc;
10330
10331 rc = i_saveStorageControllers(data.storage);
10332 if (FAILED(rc)) return rc;
10333
10334 /* Shared folders */
10335 data.llSharedFolders.clear();
10336 for (HWData::SharedFolderList::const_iterator
10337 it = mHWData->mSharedFolders.begin();
10338 it != mHWData->mSharedFolders.end();
10339 ++it)
10340 {
10341 SharedFolder *pSF = *it;
10342 AutoCaller sfCaller(pSF);
10343 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10344 settings::SharedFolder sf;
10345 sf.strName = pSF->i_getName();
10346 sf.strHostPath = pSF->i_getHostPath();
10347 sf.fWritable = !!pSF->i_isWritable();
10348 sf.fAutoMount = !!pSF->i_isAutoMounted();
10349 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10350
10351 data.llSharedFolders.push_back(sf);
10352 }
10353
10354 // clipboard
10355 data.clipboardMode = mHWData->mClipboardMode;
10356
10357 // drag'n'drop
10358 data.dndMode = mHWData->mDnDMode;
10359
10360 /* Guest */
10361 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10362
10363 // IO settings
10364 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10365 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10366
10367 /* BandwidthControl (required) */
10368 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10369 if (FAILED(rc)) throw rc;
10370
10371 /* Host PCI devices */
10372 data.pciAttachments.clear();
10373 for (HWData::PCIDeviceAssignmentList::const_iterator
10374 it = mHWData->mPCIDeviceAssignments.begin();
10375 it != mHWData->mPCIDeviceAssignments.end();
10376 ++it)
10377 {
10378 ComObjPtr<PCIDeviceAttachment> pda = *it;
10379 settings::HostPCIDeviceAttachment hpda;
10380
10381 rc = pda->i_saveSettings(hpda);
10382 if (FAILED(rc)) throw rc;
10383
10384 data.pciAttachments.push_back(hpda);
10385 }
10386
10387 // guest properties
10388 data.llGuestProperties.clear();
10389#ifdef VBOX_WITH_GUEST_PROPS
10390 for (HWData::GuestPropertyMap::const_iterator
10391 it = mHWData->mGuestProperties.begin();
10392 it != mHWData->mGuestProperties.end();
10393 ++it)
10394 {
10395 HWData::GuestProperty property = it->second;
10396
10397 /* Remove transient guest properties at shutdown unless we
10398 * are saving state. Note that restoring snapshot intentionally
10399 * keeps them, they will be removed if appropriate once the final
10400 * machine state is set (as crashes etc. need to work). */
10401 if ( ( mData->mMachineState == MachineState_PoweredOff
10402 || mData->mMachineState == MachineState_Aborted
10403 || mData->mMachineState == MachineState_Teleported)
10404 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10405 continue;
10406 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10407 prop.strName = it->first;
10408 prop.strValue = property.strValue;
10409 prop.timestamp = property.mTimestamp;
10410 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10411 GuestPropWriteFlags(property.mFlags, szFlags);
10412 prop.strFlags = szFlags;
10413
10414 data.llGuestProperties.push_back(prop);
10415 }
10416
10417 /* I presume this doesn't require a backup(). */
10418 mData->mGuestPropertiesModified = FALSE;
10419#endif /* VBOX_WITH_GUEST_PROPS defined */
10420
10421 *pDbg = mHWData->mDebugging;
10422 *pAutostart = mHWData->mAutostart;
10423
10424 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10425 }
10426 catch (std::bad_alloc &)
10427 {
10428 return E_OUTOFMEMORY;
10429 }
10430
10431 AssertComRC(rc);
10432 return rc;
10433}
10434
10435/**
10436 * Saves the storage controller configuration.
10437 *
10438 * @param data storage settings.
10439 */
10440HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10441{
10442 data.llStorageControllers.clear();
10443
10444 for (StorageControllerList::const_iterator
10445 it = mStorageControllers->begin();
10446 it != mStorageControllers->end();
10447 ++it)
10448 {
10449 HRESULT rc;
10450 ComObjPtr<StorageController> pCtl = *it;
10451
10452 settings::StorageController ctl;
10453 ctl.strName = pCtl->i_getName();
10454 ctl.controllerType = pCtl->i_getControllerType();
10455 ctl.storageBus = pCtl->i_getStorageBus();
10456 ctl.ulInstance = pCtl->i_getInstance();
10457 ctl.fBootable = pCtl->i_getBootable();
10458
10459 /* Save the port count. */
10460 ULONG portCount;
10461 rc = pCtl->COMGETTER(PortCount)(&portCount);
10462 ComAssertComRCRet(rc, rc);
10463 ctl.ulPortCount = portCount;
10464
10465 /* Save fUseHostIOCache */
10466 BOOL fUseHostIOCache;
10467 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10468 ComAssertComRCRet(rc, rc);
10469 ctl.fUseHostIOCache = !!fUseHostIOCache;
10470
10471 /* save the devices now. */
10472 rc = i_saveStorageDevices(pCtl, ctl);
10473 ComAssertComRCRet(rc, rc);
10474
10475 data.llStorageControllers.push_back(ctl);
10476 }
10477
10478 return S_OK;
10479}
10480
10481/**
10482 * Saves the hard disk configuration.
10483 */
10484HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10485 settings::StorageController &data)
10486{
10487 MediumAttachmentList atts;
10488
10489 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10490 if (FAILED(rc)) return rc;
10491
10492 data.llAttachedDevices.clear();
10493 for (MediumAttachmentList::const_iterator
10494 it = atts.begin();
10495 it != atts.end();
10496 ++it)
10497 {
10498 settings::AttachedDevice dev;
10499 IMediumAttachment *iA = *it;
10500 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10501 Medium *pMedium = pAttach->i_getMedium();
10502
10503 dev.deviceType = pAttach->i_getType();
10504 dev.lPort = pAttach->i_getPort();
10505 dev.lDevice = pAttach->i_getDevice();
10506 dev.fPassThrough = pAttach->i_getPassthrough();
10507 dev.fHotPluggable = pAttach->i_getHotPluggable();
10508 if (pMedium)
10509 {
10510 if (pMedium->i_isHostDrive())
10511 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10512 else
10513 dev.uuid = pMedium->i_getId();
10514 dev.fTempEject = pAttach->i_getTempEject();
10515 dev.fNonRotational = pAttach->i_getNonRotational();
10516 dev.fDiscard = pAttach->i_getDiscard();
10517 }
10518
10519 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10520
10521 data.llAttachedDevices.push_back(dev);
10522 }
10523
10524 return S_OK;
10525}
10526
10527/**
10528 * Saves machine state settings as defined by aFlags
10529 * (SaveSTS_* values).
10530 *
10531 * @param aFlags Combination of SaveSTS_* flags.
10532 *
10533 * @note Locks objects for writing.
10534 */
10535HRESULT Machine::i_saveStateSettings(int aFlags)
10536{
10537 if (aFlags == 0)
10538 return S_OK;
10539
10540 AutoCaller autoCaller(this);
10541 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10542
10543 /* This object's write lock is also necessary to serialize file access
10544 * (prevent concurrent reads and writes) */
10545 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10546
10547 HRESULT rc = S_OK;
10548
10549 Assert(mData->pMachineConfigFile);
10550
10551 try
10552 {
10553 if (aFlags & SaveSTS_CurStateModified)
10554 mData->pMachineConfigFile->fCurrentStateModified = true;
10555
10556 if (aFlags & SaveSTS_StateFilePath)
10557 {
10558 if (!mSSData->strStateFilePath.isEmpty())
10559 /* try to make the file name relative to the settings file dir */
10560 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10561 else
10562 mData->pMachineConfigFile->strStateFile.setNull();
10563 }
10564
10565 if (aFlags & SaveSTS_StateTimeStamp)
10566 {
10567 Assert( mData->mMachineState != MachineState_Aborted
10568 || mSSData->strStateFilePath.isEmpty());
10569
10570 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10571
10572 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10573/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10574 }
10575
10576 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10577 }
10578 catch (...)
10579 {
10580 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10581 }
10582
10583 return rc;
10584}
10585
10586/**
10587 * Ensures that the given medium is added to a media registry. If this machine
10588 * was created with 4.0 or later, then the machine registry is used. Otherwise
10589 * the global VirtualBox media registry is used.
10590 *
10591 * Caller must NOT hold machine lock, media tree or any medium locks!
10592 *
10593 * @param pMedium
10594 */
10595void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10596{
10597 /* Paranoia checks: do not hold machine or media tree locks. */
10598 AssertReturnVoid(!isWriteLockOnCurrentThread());
10599 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10600
10601 ComObjPtr<Medium> pBase;
10602 {
10603 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10604 pBase = pMedium->i_getBase();
10605 }
10606
10607 /* Paranoia checks: do not hold medium locks. */
10608 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10609 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10610
10611 // decide which medium registry to use now that the medium is attached:
10612 Guid uuid;
10613 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10614 if (fCanHaveOwnMediaRegistry)
10615 // machine XML is VirtualBox 4.0 or higher:
10616 uuid = i_getId(); // machine UUID
10617 else
10618 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10619
10620 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10621 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10622 if (pMedium->i_addRegistry(uuid))
10623 mParent->i_markRegistryModified(uuid);
10624
10625 /* For more complex hard disk structures it can happen that the base
10626 * medium isn't yet associated with any medium registry. Do that now. */
10627 if (pMedium != pBase)
10628 {
10629 /* Tree lock needed by Medium::addRegistry when recursing. */
10630 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10631 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10632 {
10633 treeLock.release();
10634 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10635 treeLock.acquire();
10636 }
10637 if (pBase->i_addRegistryRecursive(uuid))
10638 {
10639 treeLock.release();
10640 mParent->i_markRegistryModified(uuid);
10641 }
10642 }
10643}
10644
10645/**
10646 * Creates differencing hard disks for all normal hard disks attached to this
10647 * machine and a new set of attachments to refer to created disks.
10648 *
10649 * Used when taking a snapshot or when deleting the current state. Gets called
10650 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10651 *
10652 * This method assumes that mMediumAttachments contains the original hard disk
10653 * attachments it needs to create diffs for. On success, these attachments will
10654 * be replaced with the created diffs.
10655 *
10656 * Attachments with non-normal hard disks are left as is.
10657 *
10658 * If @a aOnline is @c false then the original hard disks that require implicit
10659 * diffs will be locked for reading. Otherwise it is assumed that they are
10660 * already locked for writing (when the VM was started). Note that in the latter
10661 * case it is responsibility of the caller to lock the newly created diffs for
10662 * writing if this method succeeds.
10663 *
10664 * @param aProgress Progress object to run (must contain at least as
10665 * many operations left as the number of hard disks
10666 * attached).
10667 * @param aWeight Weight of this operation.
10668 * @param aOnline Whether the VM was online prior to this operation.
10669 *
10670 * @note The progress object is not marked as completed, neither on success nor
10671 * on failure. This is a responsibility of the caller.
10672 *
10673 * @note Locks this object and the media tree for writing.
10674 */
10675HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10676 ULONG aWeight,
10677 bool aOnline)
10678{
10679 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10680
10681 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10682 AssertReturn(!!pProgressControl, E_INVALIDARG);
10683
10684 AutoCaller autoCaller(this);
10685 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10686
10687 AutoMultiWriteLock2 alock(this->lockHandle(),
10688 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10689
10690 /* must be in a protective state because we release the lock below */
10691 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10692 || mData->mMachineState == MachineState_OnlineSnapshotting
10693 || mData->mMachineState == MachineState_LiveSnapshotting
10694 || mData->mMachineState == MachineState_RestoringSnapshot
10695 || mData->mMachineState == MachineState_DeletingSnapshot
10696 , E_FAIL);
10697
10698 HRESULT rc = S_OK;
10699
10700 // use appropriate locked media map (online or offline)
10701 MediumLockListMap lockedMediaOffline;
10702 MediumLockListMap *lockedMediaMap;
10703 if (aOnline)
10704 lockedMediaMap = &mData->mSession.mLockedMedia;
10705 else
10706 lockedMediaMap = &lockedMediaOffline;
10707
10708 try
10709 {
10710 if (!aOnline)
10711 {
10712 /* lock all attached hard disks early to detect "in use"
10713 * situations before creating actual diffs */
10714 for (MediumAttachmentList::const_iterator
10715 it = mMediumAttachments->begin();
10716 it != mMediumAttachments->end();
10717 ++it)
10718 {
10719 MediumAttachment *pAtt = *it;
10720 if (pAtt->i_getType() == DeviceType_HardDisk)
10721 {
10722 Medium *pMedium = pAtt->i_getMedium();
10723 Assert(pMedium);
10724
10725 MediumLockList *pMediumLockList(new MediumLockList());
10726 alock.release();
10727 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10728 NULL /* pToLockWrite */,
10729 false /* fMediumLockWriteAll */,
10730 NULL,
10731 *pMediumLockList);
10732 alock.acquire();
10733 if (FAILED(rc))
10734 {
10735 delete pMediumLockList;
10736 throw rc;
10737 }
10738 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10739 if (FAILED(rc))
10740 {
10741 throw setError(rc,
10742 tr("Collecting locking information for all attached media failed"));
10743 }
10744 }
10745 }
10746
10747 /* Now lock all media. If this fails, nothing is locked. */
10748 alock.release();
10749 rc = lockedMediaMap->Lock();
10750 alock.acquire();
10751 if (FAILED(rc))
10752 {
10753 throw setError(rc,
10754 tr("Locking of attached media failed"));
10755 }
10756 }
10757
10758 /* remember the current list (note that we don't use backup() since
10759 * mMediumAttachments may be already backed up) */
10760 MediumAttachmentList atts = *mMediumAttachments.data();
10761
10762 /* start from scratch */
10763 mMediumAttachments->clear();
10764
10765 /* go through remembered attachments and create diffs for normal hard
10766 * disks and attach them */
10767 for (MediumAttachmentList::const_iterator
10768 it = atts.begin();
10769 it != atts.end();
10770 ++it)
10771 {
10772 MediumAttachment *pAtt = *it;
10773
10774 DeviceType_T devType = pAtt->i_getType();
10775 Medium *pMedium = pAtt->i_getMedium();
10776
10777 if ( devType != DeviceType_HardDisk
10778 || pMedium == NULL
10779 || pMedium->i_getType() != MediumType_Normal)
10780 {
10781 /* copy the attachment as is */
10782
10783 /** @todo the progress object created in SessionMachine::TakeSnaphot
10784 * only expects operations for hard disks. Later other
10785 * device types need to show up in the progress as well. */
10786 if (devType == DeviceType_HardDisk)
10787 {
10788 if (pMedium == NULL)
10789 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10790 aWeight); // weight
10791 else
10792 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10793 pMedium->i_getBase()->i_getName().c_str()).raw(),
10794 aWeight); // weight
10795 }
10796
10797 mMediumAttachments->push_back(pAtt);
10798 continue;
10799 }
10800
10801 /* need a diff */
10802 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10803 pMedium->i_getBase()->i_getName().c_str()).raw(),
10804 aWeight); // weight
10805
10806 Utf8Str strFullSnapshotFolder;
10807 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10808
10809 ComObjPtr<Medium> diff;
10810 diff.createObject();
10811 // store the diff in the same registry as the parent
10812 // (this cannot fail here because we can't create implicit diffs for
10813 // unregistered images)
10814 Guid uuidRegistryParent;
10815 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10816 Assert(fInRegistry); NOREF(fInRegistry);
10817 rc = diff->init(mParent,
10818 pMedium->i_getPreferredDiffFormat(),
10819 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10820 uuidRegistryParent,
10821 DeviceType_HardDisk);
10822 if (FAILED(rc)) throw rc;
10823
10824 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10825 * the push_back? Looks like we're going to release medium with the
10826 * wrong kind of lock (general issue with if we fail anywhere at all)
10827 * and an orphaned VDI in the snapshots folder. */
10828
10829 /* update the appropriate lock list */
10830 MediumLockList *pMediumLockList;
10831 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10832 AssertComRCThrowRC(rc);
10833 if (aOnline)
10834 {
10835 alock.release();
10836 /* The currently attached medium will be read-only, change
10837 * the lock type to read. */
10838 rc = pMediumLockList->Update(pMedium, false);
10839 alock.acquire();
10840 AssertComRCThrowRC(rc);
10841 }
10842
10843 /* release the locks before the potentially lengthy operation */
10844 alock.release();
10845 rc = pMedium->i_createDiffStorage(diff,
10846 pMedium->i_getPreferredDiffVariant(),
10847 pMediumLockList,
10848 NULL /* aProgress */,
10849 true /* aWait */,
10850 false /* aNotify */);
10851 alock.acquire();
10852 if (FAILED(rc)) throw rc;
10853
10854 /* actual lock list update is done in Machine::i_commitMedia */
10855
10856 rc = diff->i_addBackReference(mData->mUuid);
10857 AssertComRCThrowRC(rc);
10858
10859 /* add a new attachment */
10860 ComObjPtr<MediumAttachment> attachment;
10861 attachment.createObject();
10862 rc = attachment->init(this,
10863 diff,
10864 pAtt->i_getControllerName(),
10865 pAtt->i_getPort(),
10866 pAtt->i_getDevice(),
10867 DeviceType_HardDisk,
10868 true /* aImplicit */,
10869 false /* aPassthrough */,
10870 false /* aTempEject */,
10871 pAtt->i_getNonRotational(),
10872 pAtt->i_getDiscard(),
10873 pAtt->i_getHotPluggable(),
10874 pAtt->i_getBandwidthGroup());
10875 if (FAILED(rc)) throw rc;
10876
10877 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10878 AssertComRCThrowRC(rc);
10879 mMediumAttachments->push_back(attachment);
10880 }
10881 }
10882 catch (HRESULT aRC) { rc = aRC; }
10883
10884 /* unlock all hard disks we locked when there is no VM */
10885 if (!aOnline)
10886 {
10887 ErrorInfoKeeper eik;
10888
10889 HRESULT rc1 = lockedMediaMap->Clear();
10890 AssertComRC(rc1);
10891 }
10892
10893 return rc;
10894}
10895
10896/**
10897 * Deletes implicit differencing hard disks created either by
10898 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10899 * mMediumAttachments.
10900 *
10901 * Note that to delete hard disks created by #attachDevice() this method is
10902 * called from #i_rollbackMedia() when the changes are rolled back.
10903 *
10904 * @note Locks this object and the media tree for writing.
10905 */
10906HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10907{
10908 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10909
10910 AutoCaller autoCaller(this);
10911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10912
10913 AutoMultiWriteLock2 alock(this->lockHandle(),
10914 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10915
10916 /* We absolutely must have backed up state. */
10917 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10918
10919 /* Check if there are any implicitly created diff images. */
10920 bool fImplicitDiffs = false;
10921 for (MediumAttachmentList::const_iterator
10922 it = mMediumAttachments->begin();
10923 it != mMediumAttachments->end();
10924 ++it)
10925 {
10926 const ComObjPtr<MediumAttachment> &pAtt = *it;
10927 if (pAtt->i_isImplicit())
10928 {
10929 fImplicitDiffs = true;
10930 break;
10931 }
10932 }
10933 /* If there is nothing to do, leave early. This saves lots of image locking
10934 * effort. It also avoids a MachineStateChanged event without real reason.
10935 * This is important e.g. when loading a VM config, because there should be
10936 * no events. Otherwise API clients can become thoroughly confused for
10937 * inaccessible VMs (the code for loading VM configs uses this method for
10938 * cleanup if the config makes no sense), as they take such events as an
10939 * indication that the VM is alive, and they would force the VM config to
10940 * be reread, leading to an endless loop. */
10941 if (!fImplicitDiffs)
10942 return S_OK;
10943
10944 HRESULT rc = S_OK;
10945 MachineState_T oldState = mData->mMachineState;
10946
10947 /* will release the lock before the potentially lengthy operation,
10948 * so protect with the special state (unless already protected) */
10949 if ( oldState != MachineState_Snapshotting
10950 && oldState != MachineState_OnlineSnapshotting
10951 && oldState != MachineState_LiveSnapshotting
10952 && oldState != MachineState_RestoringSnapshot
10953 && oldState != MachineState_DeletingSnapshot
10954 && oldState != MachineState_DeletingSnapshotOnline
10955 && oldState != MachineState_DeletingSnapshotPaused
10956 )
10957 i_setMachineState(MachineState_SettingUp);
10958
10959 // use appropriate locked media map (online or offline)
10960 MediumLockListMap lockedMediaOffline;
10961 MediumLockListMap *lockedMediaMap;
10962 if (aOnline)
10963 lockedMediaMap = &mData->mSession.mLockedMedia;
10964 else
10965 lockedMediaMap = &lockedMediaOffline;
10966
10967 try
10968 {
10969 if (!aOnline)
10970 {
10971 /* lock all attached hard disks early to detect "in use"
10972 * situations before deleting actual diffs */
10973 for (MediumAttachmentList::const_iterator
10974 it = mMediumAttachments->begin();
10975 it != mMediumAttachments->end();
10976 ++it)
10977 {
10978 MediumAttachment *pAtt = *it;
10979 if (pAtt->i_getType() == DeviceType_HardDisk)
10980 {
10981 Medium *pMedium = pAtt->i_getMedium();
10982 Assert(pMedium);
10983
10984 MediumLockList *pMediumLockList(new MediumLockList());
10985 alock.release();
10986 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10987 NULL /* pToLockWrite */,
10988 false /* fMediumLockWriteAll */,
10989 NULL,
10990 *pMediumLockList);
10991 alock.acquire();
10992
10993 if (FAILED(rc))
10994 {
10995 delete pMediumLockList;
10996 throw rc;
10997 }
10998
10999 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
11000 if (FAILED(rc))
11001 throw rc;
11002 }
11003 }
11004
11005 if (FAILED(rc))
11006 throw rc;
11007 } // end of offline
11008
11009 /* Lock lists are now up to date and include implicitly created media */
11010
11011 /* Go through remembered attachments and delete all implicitly created
11012 * diffs and fix up the attachment information */
11013 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11014 MediumAttachmentList implicitAtts;
11015 for (MediumAttachmentList::const_iterator
11016 it = mMediumAttachments->begin();
11017 it != mMediumAttachments->end();
11018 ++it)
11019 {
11020 ComObjPtr<MediumAttachment> pAtt = *it;
11021 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11022 if (pMedium.isNull())
11023 continue;
11024
11025 // Implicit attachments go on the list for deletion and back references are removed.
11026 if (pAtt->i_isImplicit())
11027 {
11028 /* Deassociate and mark for deletion */
11029 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
11030 rc = pMedium->i_removeBackReference(mData->mUuid);
11031 if (FAILED(rc))
11032 throw rc;
11033 implicitAtts.push_back(pAtt);
11034 continue;
11035 }
11036
11037 /* Was this medium attached before? */
11038 if (!i_findAttachment(oldAtts, pMedium))
11039 {
11040 /* no: de-associate */
11041 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
11042 rc = pMedium->i_removeBackReference(mData->mUuid);
11043 if (FAILED(rc))
11044 throw rc;
11045 continue;
11046 }
11047 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
11048 }
11049
11050 /* If there are implicit attachments to delete, throw away the lock
11051 * map contents (which will unlock all media) since the medium
11052 * attachments will be rolled back. Below we need to completely
11053 * recreate the lock map anyway since it is infinitely complex to
11054 * do this incrementally (would need reconstructing each attachment
11055 * change, which would be extremely hairy). */
11056 if (implicitAtts.size() != 0)
11057 {
11058 ErrorInfoKeeper eik;
11059
11060 HRESULT rc1 = lockedMediaMap->Clear();
11061 AssertComRC(rc1);
11062 }
11063
11064 /* rollback hard disk changes */
11065 mMediumAttachments.rollback();
11066
11067 MultiResult mrc(S_OK);
11068
11069 // Delete unused implicit diffs.
11070 if (implicitAtts.size() != 0)
11071 {
11072 alock.release();
11073
11074 for (MediumAttachmentList::const_iterator
11075 it = implicitAtts.begin();
11076 it != implicitAtts.end();
11077 ++it)
11078 {
11079 // Remove medium associated with this attachment.
11080 ComObjPtr<MediumAttachment> pAtt = *it;
11081 Assert(pAtt);
11082 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11083 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11084 Assert(pMedium);
11085
11086 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11087 // continue on delete failure, just collect error messages
11088 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11089 pMedium->i_getLocationFull().c_str() ));
11090 mrc = rc;
11091 }
11092 // Clear the list of deleted implicit attachments now, while not
11093 // holding the lock, as it will ultimately trigger Medium::uninit()
11094 // calls which assume that the media tree lock isn't held.
11095 implicitAtts.clear();
11096
11097 alock.acquire();
11098
11099 /* if there is a VM recreate media lock map as mentioned above,
11100 * otherwise it is a waste of time and we leave things unlocked */
11101 if (aOnline)
11102 {
11103 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11104 /* must never be NULL, but better safe than sorry */
11105 if (!pMachine.isNull())
11106 {
11107 alock.release();
11108 rc = mData->mSession.mMachine->i_lockMedia();
11109 alock.acquire();
11110 if (FAILED(rc))
11111 throw rc;
11112 }
11113 }
11114 }
11115 }
11116 catch (HRESULT aRC) {rc = aRC;}
11117
11118 if (mData->mMachineState == MachineState_SettingUp)
11119 i_setMachineState(oldState);
11120
11121 /* unlock all hard disks we locked when there is no VM */
11122 if (!aOnline)
11123 {
11124 ErrorInfoKeeper eik;
11125
11126 HRESULT rc1 = lockedMediaMap->Clear();
11127 AssertComRC(rc1);
11128 }
11129
11130 return rc;
11131}
11132
11133
11134/**
11135 * Looks through the given list of media attachments for one with the given parameters
11136 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11137 * can be searched as well if needed.
11138 *
11139 * @param ll
11140 * @param aControllerName
11141 * @param aControllerPort
11142 * @param aDevice
11143 * @return
11144 */
11145MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11146 const Utf8Str &aControllerName,
11147 LONG aControllerPort,
11148 LONG aDevice)
11149{
11150 for (MediumAttachmentList::const_iterator
11151 it = ll.begin();
11152 it != ll.end();
11153 ++it)
11154 {
11155 MediumAttachment *pAttach = *it;
11156 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11157 return pAttach;
11158 }
11159
11160 return NULL;
11161}
11162
11163/**
11164 * Looks through the given list of media attachments for one with the given parameters
11165 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11166 * can be searched as well if needed.
11167 *
11168 * @param ll
11169 * @param pMedium
11170 * @return
11171 */
11172MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11173 ComObjPtr<Medium> pMedium)
11174{
11175 for (MediumAttachmentList::const_iterator
11176 it = ll.begin();
11177 it != ll.end();
11178 ++it)
11179 {
11180 MediumAttachment *pAttach = *it;
11181 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11182 if (pMediumThis == pMedium)
11183 return pAttach;
11184 }
11185
11186 return NULL;
11187}
11188
11189/**
11190 * Looks through the given list of media attachments for one with the given parameters
11191 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11192 * can be searched as well if needed.
11193 *
11194 * @param ll
11195 * @param id
11196 * @return
11197 */
11198MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11199 Guid &id)
11200{
11201 for (MediumAttachmentList::const_iterator
11202 it = ll.begin();
11203 it != ll.end();
11204 ++it)
11205 {
11206 MediumAttachment *pAttach = *it;
11207 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11208 if (pMediumThis->i_getId() == id)
11209 return pAttach;
11210 }
11211
11212 return NULL;
11213}
11214
11215/**
11216 * Main implementation for Machine::DetachDevice. This also gets called
11217 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11218 *
11219 * @param pAttach Medium attachment to detach.
11220 * @param writeLock Machine write lock which the caller must have locked once.
11221 * This may be released temporarily in here.
11222 * @param pSnapshot If NULL, then the detachment is for the current machine.
11223 * Otherwise this is for a SnapshotMachine, and this must be
11224 * its snapshot.
11225 * @return
11226 */
11227HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11228 AutoWriteLock &writeLock,
11229 Snapshot *pSnapshot)
11230{
11231 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11232 DeviceType_T mediumType = pAttach->i_getType();
11233
11234 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11235
11236 if (pAttach->i_isImplicit())
11237 {
11238 /* attempt to implicitly delete the implicitly created diff */
11239
11240 /// @todo move the implicit flag from MediumAttachment to Medium
11241 /// and forbid any hard disk operation when it is implicit. Or maybe
11242 /// a special media state for it to make it even more simple.
11243
11244 Assert(mMediumAttachments.isBackedUp());
11245
11246 /* will release the lock before the potentially lengthy operation, so
11247 * protect with the special state */
11248 MachineState_T oldState = mData->mMachineState;
11249 i_setMachineState(MachineState_SettingUp);
11250
11251 writeLock.release();
11252
11253 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11254 true /*aWait*/,
11255 false /*aNotify*/);
11256
11257 writeLock.acquire();
11258
11259 i_setMachineState(oldState);
11260
11261 if (FAILED(rc)) return rc;
11262 }
11263
11264 i_setModified(IsModified_Storage);
11265 mMediumAttachments.backup();
11266 mMediumAttachments->remove(pAttach);
11267
11268 if (!oldmedium.isNull())
11269 {
11270 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11271 if (pSnapshot)
11272 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11273 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11274 else if (mediumType != DeviceType_HardDisk)
11275 oldmedium->i_removeBackReference(mData->mUuid);
11276 }
11277
11278 return S_OK;
11279}
11280
11281/**
11282 * Goes thru all media of the given list and
11283 *
11284 * 1) calls i_detachDevice() on each of them for this machine and
11285 * 2) adds all Medium objects found in the process to the given list,
11286 * depending on cleanupMode.
11287 *
11288 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11289 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11290 * media to the list.
11291 *
11292 * This gets called from Machine::Unregister, both for the actual Machine and
11293 * the SnapshotMachine objects that might be found in the snapshots.
11294 *
11295 * Requires caller and locking. The machine lock must be passed in because it
11296 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11297 *
11298 * @param writeLock Machine lock from top-level caller; this gets passed to
11299 * i_detachDevice.
11300 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11301 * object if called for a SnapshotMachine.
11302 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11303 * added to llMedia; if Full, then all media get added;
11304 * otherwise no media get added.
11305 * @param llMedia Caller's list to receive Medium objects which got detached so
11306 * caller can close() them, depending on cleanupMode.
11307 * @return
11308 */
11309HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11310 Snapshot *pSnapshot,
11311 CleanupMode_T cleanupMode,
11312 MediaList &llMedia)
11313{
11314 Assert(isWriteLockOnCurrentThread());
11315
11316 HRESULT rc;
11317
11318 // make a temporary list because i_detachDevice invalidates iterators into
11319 // mMediumAttachments
11320 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11321
11322 for (MediumAttachmentList::iterator
11323 it = llAttachments2.begin();
11324 it != llAttachments2.end();
11325 ++it)
11326 {
11327 ComObjPtr<MediumAttachment> &pAttach = *it;
11328 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11329
11330 if (!pMedium.isNull())
11331 {
11332 AutoCaller mac(pMedium);
11333 if (FAILED(mac.rc())) return mac.rc();
11334 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11335 DeviceType_T devType = pMedium->i_getDeviceType();
11336 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11337 && devType == DeviceType_HardDisk)
11338 || (cleanupMode == CleanupMode_Full)
11339 )
11340 {
11341 llMedia.push_back(pMedium);
11342 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11343 /* Not allowed to keep this lock as below we need the parent
11344 * medium lock, and the lock order is parent to child. */
11345 lock.release();
11346 /*
11347 * Search for medias which are not attached to any machine, but
11348 * in the chain to an attached disk. Mediums are only consided
11349 * if they are:
11350 * - have only one child
11351 * - no references to any machines
11352 * - are of normal medium type
11353 */
11354 while (!pParent.isNull())
11355 {
11356 AutoCaller mac1(pParent);
11357 if (FAILED(mac1.rc())) return mac1.rc();
11358 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11359 if (pParent->i_getChildren().size() == 1)
11360 {
11361 if ( pParent->i_getMachineBackRefCount() == 0
11362 && pParent->i_getType() == MediumType_Normal
11363 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11364 llMedia.push_back(pParent);
11365 }
11366 else
11367 break;
11368 pParent = pParent->i_getParent();
11369 }
11370 }
11371 }
11372
11373 // real machine: then we need to use the proper method
11374 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11375
11376 if (FAILED(rc))
11377 return rc;
11378 }
11379
11380 return S_OK;
11381}
11382
11383/**
11384 * Perform deferred hard disk detachments.
11385 *
11386 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11387 * changed (not backed up).
11388 *
11389 * If @a aOnline is @c true then this method will also unlock the old hard
11390 * disks for which the new implicit diffs were created and will lock these new
11391 * diffs for writing.
11392 *
11393 * @param aOnline Whether the VM was online prior to this operation.
11394 *
11395 * @note Locks this object for writing!
11396 */
11397void Machine::i_commitMedia(bool aOnline /*= false*/)
11398{
11399 AutoCaller autoCaller(this);
11400 AssertComRCReturnVoid(autoCaller.rc());
11401
11402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11403
11404 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11405
11406 HRESULT rc = S_OK;
11407
11408 /* no attach/detach operations -- nothing to do */
11409 if (!mMediumAttachments.isBackedUp())
11410 return;
11411
11412 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11413 bool fMediaNeedsLocking = false;
11414
11415 /* enumerate new attachments */
11416 for (MediumAttachmentList::const_iterator
11417 it = mMediumAttachments->begin();
11418 it != mMediumAttachments->end();
11419 ++it)
11420 {
11421 MediumAttachment *pAttach = *it;
11422
11423 pAttach->i_commit();
11424
11425 Medium *pMedium = pAttach->i_getMedium();
11426 bool fImplicit = pAttach->i_isImplicit();
11427
11428 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11429 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11430 fImplicit));
11431
11432 /** @todo convert all this Machine-based voodoo to MediumAttachment
11433 * based commit logic. */
11434 if (fImplicit)
11435 {
11436 /* convert implicit attachment to normal */
11437 pAttach->i_setImplicit(false);
11438
11439 if ( aOnline
11440 && pMedium
11441 && pAttach->i_getType() == DeviceType_HardDisk
11442 )
11443 {
11444 /* update the appropriate lock list */
11445 MediumLockList *pMediumLockList;
11446 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11447 AssertComRC(rc);
11448 if (pMediumLockList)
11449 {
11450 /* unlock if there's a need to change the locking */
11451 if (!fMediaNeedsLocking)
11452 {
11453 rc = mData->mSession.mLockedMedia.Unlock();
11454 AssertComRC(rc);
11455 fMediaNeedsLocking = true;
11456 }
11457 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11458 AssertComRC(rc);
11459 rc = pMediumLockList->Append(pMedium, true);
11460 AssertComRC(rc);
11461 }
11462 }
11463
11464 continue;
11465 }
11466
11467 if (pMedium)
11468 {
11469 /* was this medium attached before? */
11470 for (MediumAttachmentList::iterator
11471 oldIt = oldAtts.begin();
11472 oldIt != oldAtts.end();
11473 ++oldIt)
11474 {
11475 MediumAttachment *pOldAttach = *oldIt;
11476 if (pOldAttach->i_getMedium() == pMedium)
11477 {
11478 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11479
11480 /* yes: remove from old to avoid de-association */
11481 oldAtts.erase(oldIt);
11482 break;
11483 }
11484 }
11485 }
11486 }
11487
11488 /* enumerate remaining old attachments and de-associate from the
11489 * current machine state */
11490 for (MediumAttachmentList::const_iterator
11491 it = oldAtts.begin();
11492 it != oldAtts.end();
11493 ++it)
11494 {
11495 MediumAttachment *pAttach = *it;
11496 Medium *pMedium = pAttach->i_getMedium();
11497
11498 /* Detach only hard disks, since DVD/floppy media is detached
11499 * instantly in MountMedium. */
11500 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11501 {
11502 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11503
11504 /* now de-associate from the current machine state */
11505 rc = pMedium->i_removeBackReference(mData->mUuid);
11506 AssertComRC(rc);
11507
11508 if (aOnline)
11509 {
11510 /* unlock since medium is not used anymore */
11511 MediumLockList *pMediumLockList;
11512 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11513 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11514 {
11515 /* this happens for online snapshots, there the attachment
11516 * is changing, but only to a diff image created under
11517 * the old one, so there is no separate lock list */
11518 Assert(!pMediumLockList);
11519 }
11520 else
11521 {
11522 AssertComRC(rc);
11523 if (pMediumLockList)
11524 {
11525 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11526 AssertComRC(rc);
11527 }
11528 }
11529 }
11530 }
11531 }
11532
11533 /* take media locks again so that the locking state is consistent */
11534 if (fMediaNeedsLocking)
11535 {
11536 Assert(aOnline);
11537 rc = mData->mSession.mLockedMedia.Lock();
11538 AssertComRC(rc);
11539 }
11540
11541 /* commit the hard disk changes */
11542 mMediumAttachments.commit();
11543
11544 if (i_isSessionMachine())
11545 {
11546 /*
11547 * Update the parent machine to point to the new owner.
11548 * This is necessary because the stored parent will point to the
11549 * session machine otherwise and cause crashes or errors later
11550 * when the session machine gets invalid.
11551 */
11552 /** @todo Change the MediumAttachment class to behave like any other
11553 * class in this regard by creating peer MediumAttachment
11554 * objects for session machines and share the data with the peer
11555 * machine.
11556 */
11557 for (MediumAttachmentList::const_iterator
11558 it = mMediumAttachments->begin();
11559 it != mMediumAttachments->end();
11560 ++it)
11561 (*it)->i_updateParentMachine(mPeer);
11562
11563 /* attach new data to the primary machine and reshare it */
11564 mPeer->mMediumAttachments.attach(mMediumAttachments);
11565 }
11566
11567 return;
11568}
11569
11570/**
11571 * Perform deferred deletion of implicitly created diffs.
11572 *
11573 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11574 * changed (not backed up).
11575 *
11576 * @note Locks this object for writing!
11577 */
11578void Machine::i_rollbackMedia()
11579{
11580 AutoCaller autoCaller(this);
11581 AssertComRCReturnVoid(autoCaller.rc());
11582
11583 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11584 LogFlowThisFunc(("Entering rollbackMedia\n"));
11585
11586 HRESULT rc = S_OK;
11587
11588 /* no attach/detach operations -- nothing to do */
11589 if (!mMediumAttachments.isBackedUp())
11590 return;
11591
11592 /* enumerate new attachments */
11593 for (MediumAttachmentList::const_iterator
11594 it = mMediumAttachments->begin();
11595 it != mMediumAttachments->end();
11596 ++it)
11597 {
11598 MediumAttachment *pAttach = *it;
11599 /* Fix up the backrefs for DVD/floppy media. */
11600 if (pAttach->i_getType() != DeviceType_HardDisk)
11601 {
11602 Medium *pMedium = pAttach->i_getMedium();
11603 if (pMedium)
11604 {
11605 rc = pMedium->i_removeBackReference(mData->mUuid);
11606 AssertComRC(rc);
11607 }
11608 }
11609
11610 (*it)->i_rollback();
11611
11612 pAttach = *it;
11613 /* Fix up the backrefs for DVD/floppy media. */
11614 if (pAttach->i_getType() != DeviceType_HardDisk)
11615 {
11616 Medium *pMedium = pAttach->i_getMedium();
11617 if (pMedium)
11618 {
11619 rc = pMedium->i_addBackReference(mData->mUuid);
11620 AssertComRC(rc);
11621 }
11622 }
11623 }
11624
11625 /** @todo convert all this Machine-based voodoo to MediumAttachment
11626 * based rollback logic. */
11627 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11628
11629 return;
11630}
11631
11632/**
11633 * Returns true if the settings file is located in the directory named exactly
11634 * as the machine; this means, among other things, that the machine directory
11635 * should be auto-renamed.
11636 *
11637 * @param aSettingsDir if not NULL, the full machine settings file directory
11638 * name will be assigned there.
11639 *
11640 * @note Doesn't lock anything.
11641 * @note Not thread safe (must be called from this object's lock).
11642 */
11643bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11644{
11645 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11646 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11647 if (aSettingsDir)
11648 *aSettingsDir = strMachineDirName;
11649 strMachineDirName.stripPath(); // vmname
11650 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11651 strConfigFileOnly.stripPath() // vmname.vbox
11652 .stripSuffix(); // vmname
11653 /** @todo hack, make somehow use of ComposeMachineFilename */
11654 if (mUserData->s.fDirectoryIncludesUUID)
11655 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11656
11657 AssertReturn(!strMachineDirName.isEmpty(), false);
11658 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11659
11660 return strMachineDirName == strConfigFileOnly;
11661}
11662
11663/**
11664 * Discards all changes to machine settings.
11665 *
11666 * @param aNotify Whether to notify the direct session about changes or not.
11667 *
11668 * @note Locks objects for writing!
11669 */
11670void Machine::i_rollback(bool aNotify)
11671{
11672 AutoCaller autoCaller(this);
11673 AssertComRCReturn(autoCaller.rc(), (void)0);
11674
11675 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11676
11677 if (!mStorageControllers.isNull())
11678 {
11679 if (mStorageControllers.isBackedUp())
11680 {
11681 /* unitialize all new devices (absent in the backed up list). */
11682 StorageControllerList *backedList = mStorageControllers.backedUpData();
11683 for (StorageControllerList::const_iterator
11684 it = mStorageControllers->begin();
11685 it != mStorageControllers->end();
11686 ++it)
11687 {
11688 if ( std::find(backedList->begin(), backedList->end(), *it)
11689 == backedList->end()
11690 )
11691 {
11692 (*it)->uninit();
11693 }
11694 }
11695
11696 /* restore the list */
11697 mStorageControllers.rollback();
11698 }
11699
11700 /* rollback any changes to devices after restoring the list */
11701 if (mData->flModifications & IsModified_Storage)
11702 {
11703 for (StorageControllerList::const_iterator
11704 it = mStorageControllers->begin();
11705 it != mStorageControllers->end();
11706 ++it)
11707 {
11708 (*it)->i_rollback();
11709 }
11710 }
11711 }
11712
11713 if (!mUSBControllers.isNull())
11714 {
11715 if (mUSBControllers.isBackedUp())
11716 {
11717 /* unitialize all new devices (absent in the backed up list). */
11718 USBControllerList *backedList = mUSBControllers.backedUpData();
11719 for (USBControllerList::const_iterator
11720 it = mUSBControllers->begin();
11721 it != mUSBControllers->end();
11722 ++it)
11723 {
11724 if ( std::find(backedList->begin(), backedList->end(), *it)
11725 == backedList->end()
11726 )
11727 {
11728 (*it)->uninit();
11729 }
11730 }
11731
11732 /* restore the list */
11733 mUSBControllers.rollback();
11734 }
11735
11736 /* rollback any changes to devices after restoring the list */
11737 if (mData->flModifications & IsModified_USB)
11738 {
11739 for (USBControllerList::const_iterator
11740 it = mUSBControllers->begin();
11741 it != mUSBControllers->end();
11742 ++it)
11743 {
11744 (*it)->i_rollback();
11745 }
11746 }
11747 }
11748
11749 mUserData.rollback();
11750
11751 mHWData.rollback();
11752
11753 if (mData->flModifications & IsModified_Storage)
11754 i_rollbackMedia();
11755
11756 if (mBIOSSettings)
11757 mBIOSSettings->i_rollback();
11758
11759 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11760 mRecordingSettings->i_rollback();
11761
11762 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11763 mVRDEServer->i_rollback();
11764
11765 if (mAudioAdapter)
11766 mAudioAdapter->i_rollback();
11767
11768 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11769 mUSBDeviceFilters->i_rollback();
11770
11771 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11772 mBandwidthControl->i_rollback();
11773
11774 if (!mHWData.isNull())
11775 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11776 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11777 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11778 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11779
11780 if (mData->flModifications & IsModified_NetworkAdapters)
11781 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11782 if ( mNetworkAdapters[slot]
11783 && mNetworkAdapters[slot]->i_isModified())
11784 {
11785 mNetworkAdapters[slot]->i_rollback();
11786 networkAdapters[slot] = mNetworkAdapters[slot];
11787 }
11788
11789 if (mData->flModifications & IsModified_SerialPorts)
11790 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11791 if ( mSerialPorts[slot]
11792 && mSerialPorts[slot]->i_isModified())
11793 {
11794 mSerialPorts[slot]->i_rollback();
11795 serialPorts[slot] = mSerialPorts[slot];
11796 }
11797
11798 if (mData->flModifications & IsModified_ParallelPorts)
11799 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11800 if ( mParallelPorts[slot]
11801 && mParallelPorts[slot]->i_isModified())
11802 {
11803 mParallelPorts[slot]->i_rollback();
11804 parallelPorts[slot] = mParallelPorts[slot];
11805 }
11806
11807 if (aNotify)
11808 {
11809 /* inform the direct session about changes */
11810
11811 ComObjPtr<Machine> that = this;
11812 uint32_t flModifications = mData->flModifications;
11813 alock.release();
11814
11815 if (flModifications & IsModified_SharedFolders)
11816 that->i_onSharedFolderChange();
11817
11818 if (flModifications & IsModified_VRDEServer)
11819 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11820 if (flModifications & IsModified_USB)
11821 that->i_onUSBControllerChange();
11822
11823 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11824 if (networkAdapters[slot])
11825 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11826 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11827 if (serialPorts[slot])
11828 that->i_onSerialPortChange(serialPorts[slot]);
11829 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11830 if (parallelPorts[slot])
11831 that->i_onParallelPortChange(parallelPorts[slot]);
11832
11833 if (flModifications & IsModified_Storage)
11834 that->i_onStorageControllerChange();
11835
11836#if 0
11837 if (flModifications & IsModified_BandwidthControl)
11838 that->onBandwidthControlChange();
11839#endif
11840 }
11841}
11842
11843/**
11844 * Commits all the changes to machine settings.
11845 *
11846 * Note that this operation is supposed to never fail.
11847 *
11848 * @note Locks this object and children for writing.
11849 */
11850void Machine::i_commit()
11851{
11852 AutoCaller autoCaller(this);
11853 AssertComRCReturnVoid(autoCaller.rc());
11854
11855 AutoCaller peerCaller(mPeer);
11856 AssertComRCReturnVoid(peerCaller.rc());
11857
11858 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11859
11860 /*
11861 * use safe commit to ensure Snapshot machines (that share mUserData)
11862 * will still refer to a valid memory location
11863 */
11864 mUserData.commitCopy();
11865
11866 mHWData.commit();
11867
11868 if (mMediumAttachments.isBackedUp())
11869 i_commitMedia(Global::IsOnline(mData->mMachineState));
11870
11871 mBIOSSettings->i_commit();
11872 mRecordingSettings->i_commit();
11873 mVRDEServer->i_commit();
11874 mAudioAdapter->i_commit();
11875 mUSBDeviceFilters->i_commit();
11876 mBandwidthControl->i_commit();
11877
11878 /* Since mNetworkAdapters is a list which might have been changed (resized)
11879 * without using the Backupable<> template we need to handle the copying
11880 * of the list entries manually, including the creation of peers for the
11881 * new objects. */
11882 bool commitNetworkAdapters = false;
11883 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11884 if (mPeer)
11885 {
11886 /* commit everything, even the ones which will go away */
11887 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11888 mNetworkAdapters[slot]->i_commit();
11889 /* copy over the new entries, creating a peer and uninit the original */
11890 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11891 for (size_t slot = 0; slot < newSize; slot++)
11892 {
11893 /* look if this adapter has a peer device */
11894 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11895 if (!peer)
11896 {
11897 /* no peer means the adapter is a newly created one;
11898 * create a peer owning data this data share it with */
11899 peer.createObject();
11900 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11901 }
11902 mPeer->mNetworkAdapters[slot] = peer;
11903 }
11904 /* uninit any no longer needed network adapters */
11905 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11906 mNetworkAdapters[slot]->uninit();
11907 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11908 {
11909 if (mPeer->mNetworkAdapters[slot])
11910 mPeer->mNetworkAdapters[slot]->uninit();
11911 }
11912 /* Keep the original network adapter count until this point, so that
11913 * discarding a chipset type change will not lose settings. */
11914 mNetworkAdapters.resize(newSize);
11915 mPeer->mNetworkAdapters.resize(newSize);
11916 }
11917 else
11918 {
11919 /* we have no peer (our parent is the newly created machine);
11920 * just commit changes to the network adapters */
11921 commitNetworkAdapters = true;
11922 }
11923 if (commitNetworkAdapters)
11924 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11925 mNetworkAdapters[slot]->i_commit();
11926
11927 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11928 mSerialPorts[slot]->i_commit();
11929 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11930 mParallelPorts[slot]->i_commit();
11931
11932 bool commitStorageControllers = false;
11933
11934 if (mStorageControllers.isBackedUp())
11935 {
11936 mStorageControllers.commit();
11937
11938 if (mPeer)
11939 {
11940 /* Commit all changes to new controllers (this will reshare data with
11941 * peers for those who have peers) */
11942 StorageControllerList *newList = new StorageControllerList();
11943 for (StorageControllerList::const_iterator
11944 it = mStorageControllers->begin();
11945 it != mStorageControllers->end();
11946 ++it)
11947 {
11948 (*it)->i_commit();
11949
11950 /* look if this controller has a peer device */
11951 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11952 if (!peer)
11953 {
11954 /* no peer means the device is a newly created one;
11955 * create a peer owning data this device share it with */
11956 peer.createObject();
11957 peer->init(mPeer, *it, true /* aReshare */);
11958 }
11959 else
11960 {
11961 /* remove peer from the old list */
11962 mPeer->mStorageControllers->remove(peer);
11963 }
11964 /* and add it to the new list */
11965 newList->push_back(peer);
11966 }
11967
11968 /* uninit old peer's controllers that are left */
11969 for (StorageControllerList::const_iterator
11970 it = mPeer->mStorageControllers->begin();
11971 it != mPeer->mStorageControllers->end();
11972 ++it)
11973 {
11974 (*it)->uninit();
11975 }
11976
11977 /* attach new list of controllers to our peer */
11978 mPeer->mStorageControllers.attach(newList);
11979 }
11980 else
11981 {
11982 /* we have no peer (our parent is the newly created machine);
11983 * just commit changes to devices */
11984 commitStorageControllers = true;
11985 }
11986 }
11987 else
11988 {
11989 /* the list of controllers itself is not changed,
11990 * just commit changes to controllers themselves */
11991 commitStorageControllers = true;
11992 }
11993
11994 if (commitStorageControllers)
11995 {
11996 for (StorageControllerList::const_iterator
11997 it = mStorageControllers->begin();
11998 it != mStorageControllers->end();
11999 ++it)
12000 {
12001 (*it)->i_commit();
12002 }
12003 }
12004
12005 bool commitUSBControllers = false;
12006
12007 if (mUSBControllers.isBackedUp())
12008 {
12009 mUSBControllers.commit();
12010
12011 if (mPeer)
12012 {
12013 /* Commit all changes to new controllers (this will reshare data with
12014 * peers for those who have peers) */
12015 USBControllerList *newList = new USBControllerList();
12016 for (USBControllerList::const_iterator
12017 it = mUSBControllers->begin();
12018 it != mUSBControllers->end();
12019 ++it)
12020 {
12021 (*it)->i_commit();
12022
12023 /* look if this controller has a peer device */
12024 ComObjPtr<USBController> peer = (*it)->i_getPeer();
12025 if (!peer)
12026 {
12027 /* no peer means the device is a newly created one;
12028 * create a peer owning data this device share it with */
12029 peer.createObject();
12030 peer->init(mPeer, *it, true /* aReshare */);
12031 }
12032 else
12033 {
12034 /* remove peer from the old list */
12035 mPeer->mUSBControllers->remove(peer);
12036 }
12037 /* and add it to the new list */
12038 newList->push_back(peer);
12039 }
12040
12041 /* uninit old peer's controllers that are left */
12042 for (USBControllerList::const_iterator
12043 it = mPeer->mUSBControllers->begin();
12044 it != mPeer->mUSBControllers->end();
12045 ++it)
12046 {
12047 (*it)->uninit();
12048 }
12049
12050 /* attach new list of controllers to our peer */
12051 mPeer->mUSBControllers.attach(newList);
12052 }
12053 else
12054 {
12055 /* we have no peer (our parent is the newly created machine);
12056 * just commit changes to devices */
12057 commitUSBControllers = true;
12058 }
12059 }
12060 else
12061 {
12062 /* the list of controllers itself is not changed,
12063 * just commit changes to controllers themselves */
12064 commitUSBControllers = true;
12065 }
12066
12067 if (commitUSBControllers)
12068 {
12069 for (USBControllerList::const_iterator
12070 it = mUSBControllers->begin();
12071 it != mUSBControllers->end();
12072 ++it)
12073 {
12074 (*it)->i_commit();
12075 }
12076 }
12077
12078 if (i_isSessionMachine())
12079 {
12080 /* attach new data to the primary machine and reshare it */
12081 mPeer->mUserData.attach(mUserData);
12082 mPeer->mHWData.attach(mHWData);
12083 /* mmMediumAttachments is reshared by fixupMedia */
12084 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12085 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12086 }
12087}
12088
12089/**
12090 * Copies all the hardware data from the given machine.
12091 *
12092 * Currently, only called when the VM is being restored from a snapshot. In
12093 * particular, this implies that the VM is not running during this method's
12094 * call.
12095 *
12096 * @note This method must be called from under this object's lock.
12097 *
12098 * @note This method doesn't call #i_commit(), so all data remains backed up and
12099 * unsaved.
12100 */
12101void Machine::i_copyFrom(Machine *aThat)
12102{
12103 AssertReturnVoid(!i_isSnapshotMachine());
12104 AssertReturnVoid(aThat->i_isSnapshotMachine());
12105
12106 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12107
12108 mHWData.assignCopy(aThat->mHWData);
12109
12110 // create copies of all shared folders (mHWData after attaching a copy
12111 // contains just references to original objects)
12112 for (HWData::SharedFolderList::iterator
12113 it = mHWData->mSharedFolders.begin();
12114 it != mHWData->mSharedFolders.end();
12115 ++it)
12116 {
12117 ComObjPtr<SharedFolder> folder;
12118 folder.createObject();
12119 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12120 AssertComRC(rc);
12121 *it = folder;
12122 }
12123
12124 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12125 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12126 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12127 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12128 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12129 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12130
12131 /* create private copies of all controllers */
12132 mStorageControllers.backup();
12133 mStorageControllers->clear();
12134 for (StorageControllerList::const_iterator
12135 it = aThat->mStorageControllers->begin();
12136 it != aThat->mStorageControllers->end();
12137 ++it)
12138 {
12139 ComObjPtr<StorageController> ctrl;
12140 ctrl.createObject();
12141 ctrl->initCopy(this, *it);
12142 mStorageControllers->push_back(ctrl);
12143 }
12144
12145 /* create private copies of all USB controllers */
12146 mUSBControllers.backup();
12147 mUSBControllers->clear();
12148 for (USBControllerList::const_iterator
12149 it = aThat->mUSBControllers->begin();
12150 it != aThat->mUSBControllers->end();
12151 ++it)
12152 {
12153 ComObjPtr<USBController> ctrl;
12154 ctrl.createObject();
12155 ctrl->initCopy(this, *it);
12156 mUSBControllers->push_back(ctrl);
12157 }
12158
12159 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12160 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12161 {
12162 if (mNetworkAdapters[slot].isNotNull())
12163 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12164 else
12165 {
12166 unconst(mNetworkAdapters[slot]).createObject();
12167 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12168 }
12169 }
12170 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12171 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12172 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12173 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12174}
12175
12176/**
12177 * Returns whether the given storage controller is hotplug capable.
12178 *
12179 * @returns true if the controller supports hotplugging
12180 * false otherwise.
12181 * @param enmCtrlType The controller type to check for.
12182 */
12183bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12184{
12185 ComPtr<ISystemProperties> systemProperties;
12186 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12187 if (FAILED(rc))
12188 return false;
12189
12190 BOOL aHotplugCapable = FALSE;
12191 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12192
12193 return RT_BOOL(aHotplugCapable);
12194}
12195
12196#ifdef VBOX_WITH_RESOURCE_USAGE_API
12197
12198void Machine::i_getDiskList(MediaList &list)
12199{
12200 for (MediumAttachmentList::const_iterator
12201 it = mMediumAttachments->begin();
12202 it != mMediumAttachments->end();
12203 ++it)
12204 {
12205 MediumAttachment *pAttach = *it;
12206 /* just in case */
12207 AssertContinue(pAttach);
12208
12209 AutoCaller localAutoCallerA(pAttach);
12210 if (FAILED(localAutoCallerA.rc())) continue;
12211
12212 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12213
12214 if (pAttach->i_getType() == DeviceType_HardDisk)
12215 list.push_back(pAttach->i_getMedium());
12216 }
12217}
12218
12219void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12220{
12221 AssertReturnVoid(isWriteLockOnCurrentThread());
12222 AssertPtrReturnVoid(aCollector);
12223
12224 pm::CollectorHAL *hal = aCollector->getHAL();
12225 /* Create sub metrics */
12226 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12227 "Percentage of processor time spent in user mode by the VM process.");
12228 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12229 "Percentage of processor time spent in kernel mode by the VM process.");
12230 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12231 "Size of resident portion of VM process in memory.");
12232 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12233 "Actual size of all VM disks combined.");
12234 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12235 "Network receive rate.");
12236 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12237 "Network transmit rate.");
12238 /* Create and register base metrics */
12239 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12240 cpuLoadUser, cpuLoadKernel);
12241 aCollector->registerBaseMetric(cpuLoad);
12242 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12243 ramUsageUsed);
12244 aCollector->registerBaseMetric(ramUsage);
12245 MediaList disks;
12246 i_getDiskList(disks);
12247 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12248 diskUsageUsed);
12249 aCollector->registerBaseMetric(diskUsage);
12250
12251 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12252 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12253 new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12255 new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12257 new pm::AggregateMax()));
12258 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12259 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12260 new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12262 new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12264 new pm::AggregateMax()));
12265
12266 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12267 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12268 new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12270 new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12272 new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12275 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12276 new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12278 new pm::AggregateMin()));
12279 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12280 new pm::AggregateMax()));
12281
12282
12283 /* Guest metrics collector */
12284 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12285 aCollector->registerGuest(mCollectorGuest);
12286 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12287
12288 /* Create sub metrics */
12289 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12290 "Percentage of processor time spent in user mode as seen by the guest.");
12291 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12292 "Percentage of processor time spent in kernel mode as seen by the guest.");
12293 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12294 "Percentage of processor time spent idling as seen by the guest.");
12295
12296 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12297 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12298 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12299 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12300 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12301 pm::SubMetric *guestMemCache = new pm::SubMetric(
12302 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12303
12304 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12305 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12306
12307 /* Create and register base metrics */
12308 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12309 machineNetRx, machineNetTx);
12310 aCollector->registerBaseMetric(machineNetRate);
12311
12312 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12313 guestLoadUser, guestLoadKernel, guestLoadIdle);
12314 aCollector->registerBaseMetric(guestCpuLoad);
12315
12316 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12317 guestMemTotal, guestMemFree,
12318 guestMemBalloon, guestMemShared,
12319 guestMemCache, guestPagedTotal);
12320 aCollector->registerBaseMetric(guestCpuMem);
12321
12322 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12323 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12324 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12325 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12326
12327 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12328 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12329 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12330 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12331
12332 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12333 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12334 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12335 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12336
12337 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12338 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12339 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12340 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12341
12342 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12343 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12344 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12345 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12346
12347 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12348 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12349 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12350 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12351
12352 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12353 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12354 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12355 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12356
12357 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12358 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12359 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12360 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12361
12362 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12363 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12364 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12365 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12366
12367 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12368 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12369 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12370 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12371
12372 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12373 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12374 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12375 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12376}
12377
12378void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12379{
12380 AssertReturnVoid(isWriteLockOnCurrentThread());
12381
12382 if (aCollector)
12383 {
12384 aCollector->unregisterMetricsFor(aMachine);
12385 aCollector->unregisterBaseMetricsFor(aMachine);
12386 }
12387}
12388
12389#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12390
12391
12392////////////////////////////////////////////////////////////////////////////////
12393
12394DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12395
12396HRESULT SessionMachine::FinalConstruct()
12397{
12398 LogFlowThisFunc(("\n"));
12399
12400 mClientToken = NULL;
12401
12402 return BaseFinalConstruct();
12403}
12404
12405void SessionMachine::FinalRelease()
12406{
12407 LogFlowThisFunc(("\n"));
12408
12409 Assert(!mClientToken);
12410 /* paranoia, should not hang around any more */
12411 if (mClientToken)
12412 {
12413 delete mClientToken;
12414 mClientToken = NULL;
12415 }
12416
12417 uninit(Uninit::Unexpected);
12418
12419 BaseFinalRelease();
12420}
12421
12422/**
12423 * @note Must be called only by Machine::LockMachine() from its own write lock.
12424 */
12425HRESULT SessionMachine::init(Machine *aMachine)
12426{
12427 LogFlowThisFuncEnter();
12428 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12429
12430 AssertReturn(aMachine, E_INVALIDARG);
12431
12432 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12433
12434 /* Enclose the state transition NotReady->InInit->Ready */
12435 AutoInitSpan autoInitSpan(this);
12436 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12437
12438 HRESULT rc = S_OK;
12439
12440 RT_ZERO(mAuthLibCtx);
12441
12442 /* create the machine client token */
12443 try
12444 {
12445 mClientToken = new ClientToken(aMachine, this);
12446 if (!mClientToken->isReady())
12447 {
12448 delete mClientToken;
12449 mClientToken = NULL;
12450 rc = E_FAIL;
12451 }
12452 }
12453 catch (std::bad_alloc &)
12454 {
12455 rc = E_OUTOFMEMORY;
12456 }
12457 if (FAILED(rc))
12458 return rc;
12459
12460 /* memorize the peer Machine */
12461 unconst(mPeer) = aMachine;
12462 /* share the parent pointer */
12463 unconst(mParent) = aMachine->mParent;
12464
12465 /* take the pointers to data to share */
12466 mData.share(aMachine->mData);
12467 mSSData.share(aMachine->mSSData);
12468
12469 mUserData.share(aMachine->mUserData);
12470 mHWData.share(aMachine->mHWData);
12471 mMediumAttachments.share(aMachine->mMediumAttachments);
12472
12473 mStorageControllers.allocate();
12474 for (StorageControllerList::const_iterator
12475 it = aMachine->mStorageControllers->begin();
12476 it != aMachine->mStorageControllers->end();
12477 ++it)
12478 {
12479 ComObjPtr<StorageController> ctl;
12480 ctl.createObject();
12481 ctl->init(this, *it);
12482 mStorageControllers->push_back(ctl);
12483 }
12484
12485 mUSBControllers.allocate();
12486 for (USBControllerList::const_iterator
12487 it = aMachine->mUSBControllers->begin();
12488 it != aMachine->mUSBControllers->end();
12489 ++it)
12490 {
12491 ComObjPtr<USBController> ctl;
12492 ctl.createObject();
12493 ctl->init(this, *it);
12494 mUSBControllers->push_back(ctl);
12495 }
12496
12497 unconst(mBIOSSettings).createObject();
12498 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12499 unconst(mRecordingSettings).createObject();
12500 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12501 /* create another VRDEServer object that will be mutable */
12502 unconst(mVRDEServer).createObject();
12503 mVRDEServer->init(this, aMachine->mVRDEServer);
12504 /* create another audio adapter object that will be mutable */
12505 unconst(mAudioAdapter).createObject();
12506 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12507 /* create a list of serial ports that will be mutable */
12508 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12509 {
12510 unconst(mSerialPorts[slot]).createObject();
12511 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12512 }
12513 /* create a list of parallel ports that will be mutable */
12514 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12515 {
12516 unconst(mParallelPorts[slot]).createObject();
12517 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12518 }
12519
12520 /* create another USB device filters object that will be mutable */
12521 unconst(mUSBDeviceFilters).createObject();
12522 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12523
12524 /* create a list of network adapters that will be mutable */
12525 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12526 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12527 {
12528 unconst(mNetworkAdapters[slot]).createObject();
12529 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12530 }
12531
12532 /* create another bandwidth control object that will be mutable */
12533 unconst(mBandwidthControl).createObject();
12534 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12535
12536 /* default is to delete saved state on Saved -> PoweredOff transition */
12537 mRemoveSavedState = true;
12538
12539 /* Confirm a successful initialization when it's the case */
12540 autoInitSpan.setSucceeded();
12541
12542 miNATNetworksStarted = 0;
12543
12544 LogFlowThisFuncLeave();
12545 return rc;
12546}
12547
12548/**
12549 * Uninitializes this session object. If the reason is other than
12550 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12551 * or the client watcher code.
12552 *
12553 * @param aReason uninitialization reason
12554 *
12555 * @note Locks mParent + this object for writing.
12556 */
12557void SessionMachine::uninit(Uninit::Reason aReason)
12558{
12559 LogFlowThisFuncEnter();
12560 LogFlowThisFunc(("reason=%d\n", aReason));
12561
12562 /*
12563 * Strongly reference ourselves to prevent this object deletion after
12564 * mData->mSession.mMachine.setNull() below (which can release the last
12565 * reference and call the destructor). Important: this must be done before
12566 * accessing any members (and before AutoUninitSpan that does it as well).
12567 * This self reference will be released as the very last step on return.
12568 */
12569 ComObjPtr<SessionMachine> selfRef;
12570 if (aReason != Uninit::Unexpected)
12571 selfRef = this;
12572
12573 /* Enclose the state transition Ready->InUninit->NotReady */
12574 AutoUninitSpan autoUninitSpan(this);
12575 if (autoUninitSpan.uninitDone())
12576 {
12577 LogFlowThisFunc(("Already uninitialized\n"));
12578 LogFlowThisFuncLeave();
12579 return;
12580 }
12581
12582 if (autoUninitSpan.initFailed())
12583 {
12584 /* We've been called by init() because it's failed. It's not really
12585 * necessary (nor it's safe) to perform the regular uninit sequence
12586 * below, the following is enough.
12587 */
12588 LogFlowThisFunc(("Initialization failed.\n"));
12589 /* destroy the machine client token */
12590 if (mClientToken)
12591 {
12592 delete mClientToken;
12593 mClientToken = NULL;
12594 }
12595 uninitDataAndChildObjects();
12596 mData.free();
12597 unconst(mParent) = NULL;
12598 unconst(mPeer) = NULL;
12599 LogFlowThisFuncLeave();
12600 return;
12601 }
12602
12603 MachineState_T lastState;
12604 {
12605 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12606 lastState = mData->mMachineState;
12607 }
12608 NOREF(lastState);
12609
12610#ifdef VBOX_WITH_USB
12611 // release all captured USB devices, but do this before requesting the locks below
12612 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12613 {
12614 /* Console::captureUSBDevices() is called in the VM process only after
12615 * setting the machine state to Starting or Restoring.
12616 * Console::detachAllUSBDevices() will be called upon successful
12617 * termination. So, we need to release USB devices only if there was
12618 * an abnormal termination of a running VM.
12619 *
12620 * This is identical to SessionMachine::DetachAllUSBDevices except
12621 * for the aAbnormal argument. */
12622 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12623 AssertComRC(rc);
12624 NOREF(rc);
12625
12626 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12627 if (service)
12628 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12629 }
12630#endif /* VBOX_WITH_USB */
12631
12632 // we need to lock this object in uninit() because the lock is shared
12633 // with mPeer (as well as data we modify below). mParent lock is needed
12634 // by several calls to it.
12635 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12636
12637#ifdef VBOX_WITH_RESOURCE_USAGE_API
12638 /*
12639 * It is safe to call Machine::i_unregisterMetrics() here because
12640 * PerformanceCollector::samplerCallback no longer accesses guest methods
12641 * holding the lock.
12642 */
12643 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12644 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12645 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12646 if (mCollectorGuest)
12647 {
12648 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12649 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12650 mCollectorGuest = NULL;
12651 }
12652#endif
12653
12654 if (aReason == Uninit::Abnormal)
12655 {
12656 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12657
12658 /* reset the state to Aborted */
12659 if (mData->mMachineState != MachineState_Aborted)
12660 i_setMachineState(MachineState_Aborted);
12661 }
12662
12663 // any machine settings modified?
12664 if (mData->flModifications)
12665 {
12666 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12667 i_rollback(false /* aNotify */);
12668 }
12669
12670 mData->mSession.mPID = NIL_RTPROCESS;
12671
12672 if (aReason == Uninit::Unexpected)
12673 {
12674 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12675 * client watcher thread to update the set of machines that have open
12676 * sessions. */
12677 mParent->i_updateClientWatcher();
12678 }
12679
12680 /* uninitialize all remote controls */
12681 if (mData->mSession.mRemoteControls.size())
12682 {
12683 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12684 mData->mSession.mRemoteControls.size()));
12685
12686 /* Always restart a the beginning, since the iterator is invalidated
12687 * by using erase(). */
12688 for (Data::Session::RemoteControlList::iterator
12689 it = mData->mSession.mRemoteControls.begin();
12690 it != mData->mSession.mRemoteControls.end();
12691 it = mData->mSession.mRemoteControls.begin())
12692 {
12693 ComPtr<IInternalSessionControl> pControl = *it;
12694 mData->mSession.mRemoteControls.erase(it);
12695 multilock.release();
12696 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12697 HRESULT rc = pControl->Uninitialize();
12698 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12699 if (FAILED(rc))
12700 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12701 multilock.acquire();
12702 }
12703 mData->mSession.mRemoteControls.clear();
12704 }
12705
12706 /* Remove all references to the NAT network service. The service will stop
12707 * if all references (also from other VMs) are removed. */
12708 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12709 {
12710 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12711 {
12712 BOOL enabled;
12713 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12714 if ( FAILED(hrc)
12715 || !enabled)
12716 continue;
12717
12718 NetworkAttachmentType_T type;
12719 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12720 if ( SUCCEEDED(hrc)
12721 && type == NetworkAttachmentType_NATNetwork)
12722 {
12723 Bstr name;
12724 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12725 if (SUCCEEDED(hrc))
12726 {
12727 multilock.release();
12728 Utf8Str strName(name);
12729 LogRel(("VM '%s' stops using NAT network '%s'\n",
12730 mUserData->s.strName.c_str(), strName.c_str()));
12731 mParent->i_natNetworkRefDec(strName);
12732 multilock.acquire();
12733 }
12734 }
12735 }
12736 }
12737
12738 /*
12739 * An expected uninitialization can come only from #i_checkForDeath().
12740 * Otherwise it means that something's gone really wrong (for example,
12741 * the Session implementation has released the VirtualBox reference
12742 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12743 * etc). However, it's also possible, that the client releases the IPC
12744 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12745 * but the VirtualBox release event comes first to the server process.
12746 * This case is practically possible, so we should not assert on an
12747 * unexpected uninit, just log a warning.
12748 */
12749
12750 if (aReason == Uninit::Unexpected)
12751 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12752
12753 if (aReason != Uninit::Normal)
12754 {
12755 mData->mSession.mDirectControl.setNull();
12756 }
12757 else
12758 {
12759 /* this must be null here (see #OnSessionEnd()) */
12760 Assert(mData->mSession.mDirectControl.isNull());
12761 Assert(mData->mSession.mState == SessionState_Unlocking);
12762 Assert(!mData->mSession.mProgress.isNull());
12763 }
12764 if (mData->mSession.mProgress)
12765 {
12766 if (aReason == Uninit::Normal)
12767 mData->mSession.mProgress->i_notifyComplete(S_OK);
12768 else
12769 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12770 COM_IIDOF(ISession),
12771 getComponentName(),
12772 tr("The VM session was aborted"));
12773 mData->mSession.mProgress.setNull();
12774 }
12775
12776 if (mConsoleTaskData.mProgress)
12777 {
12778 Assert(aReason == Uninit::Abnormal);
12779 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12780 COM_IIDOF(ISession),
12781 getComponentName(),
12782 tr("The VM session was aborted"));
12783 mConsoleTaskData.mProgress.setNull();
12784 }
12785
12786 /* remove the association between the peer machine and this session machine */
12787 Assert( (SessionMachine*)mData->mSession.mMachine == this
12788 || aReason == Uninit::Unexpected);
12789
12790 /* reset the rest of session data */
12791 mData->mSession.mLockType = LockType_Null;
12792 mData->mSession.mMachine.setNull();
12793 mData->mSession.mState = SessionState_Unlocked;
12794 mData->mSession.mName.setNull();
12795
12796 /* destroy the machine client token before leaving the exclusive lock */
12797 if (mClientToken)
12798 {
12799 delete mClientToken;
12800 mClientToken = NULL;
12801 }
12802
12803 /* fire an event */
12804 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12805
12806 uninitDataAndChildObjects();
12807
12808 /* free the essential data structure last */
12809 mData.free();
12810
12811 /* release the exclusive lock before setting the below two to NULL */
12812 multilock.release();
12813
12814 unconst(mParent) = NULL;
12815 unconst(mPeer) = NULL;
12816
12817 AuthLibUnload(&mAuthLibCtx);
12818
12819 LogFlowThisFuncLeave();
12820}
12821
12822// util::Lockable interface
12823////////////////////////////////////////////////////////////////////////////////
12824
12825/**
12826 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12827 * with the primary Machine instance (mPeer).
12828 */
12829RWLockHandle *SessionMachine::lockHandle() const
12830{
12831 AssertReturn(mPeer != NULL, NULL);
12832 return mPeer->lockHandle();
12833}
12834
12835// IInternalMachineControl methods
12836////////////////////////////////////////////////////////////////////////////////
12837
12838/**
12839 * Passes collected guest statistics to performance collector object
12840 */
12841HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12842 ULONG aCpuKernel, ULONG aCpuIdle,
12843 ULONG aMemTotal, ULONG aMemFree,
12844 ULONG aMemBalloon, ULONG aMemShared,
12845 ULONG aMemCache, ULONG aPageTotal,
12846 ULONG aAllocVMM, ULONG aFreeVMM,
12847 ULONG aBalloonedVMM, ULONG aSharedVMM,
12848 ULONG aVmNetRx, ULONG aVmNetTx)
12849{
12850#ifdef VBOX_WITH_RESOURCE_USAGE_API
12851 if (mCollectorGuest)
12852 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12853 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12854 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12855 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12856
12857 return S_OK;
12858#else
12859 NOREF(aValidStats);
12860 NOREF(aCpuUser);
12861 NOREF(aCpuKernel);
12862 NOREF(aCpuIdle);
12863 NOREF(aMemTotal);
12864 NOREF(aMemFree);
12865 NOREF(aMemBalloon);
12866 NOREF(aMemShared);
12867 NOREF(aMemCache);
12868 NOREF(aPageTotal);
12869 NOREF(aAllocVMM);
12870 NOREF(aFreeVMM);
12871 NOREF(aBalloonedVMM);
12872 NOREF(aSharedVMM);
12873 NOREF(aVmNetRx);
12874 NOREF(aVmNetTx);
12875 return E_NOTIMPL;
12876#endif
12877}
12878
12879////////////////////////////////////////////////////////////////////////////////
12880//
12881// SessionMachine task records
12882//
12883////////////////////////////////////////////////////////////////////////////////
12884
12885/**
12886 * Task record for saving the machine state.
12887 */
12888class SessionMachine::SaveStateTask
12889 : public Machine::Task
12890{
12891public:
12892 SaveStateTask(SessionMachine *m,
12893 Progress *p,
12894 const Utf8Str &t,
12895 Reason_T enmReason,
12896 const Utf8Str &strStateFilePath)
12897 : Task(m, p, t),
12898 m_enmReason(enmReason),
12899 m_strStateFilePath(strStateFilePath)
12900 {}
12901
12902private:
12903 void handler()
12904 {
12905 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12906 }
12907
12908 Reason_T m_enmReason;
12909 Utf8Str m_strStateFilePath;
12910
12911 friend class SessionMachine;
12912};
12913
12914/**
12915 * Task thread implementation for SessionMachine::SaveState(), called from
12916 * SessionMachine::taskHandler().
12917 *
12918 * @note Locks this object for writing.
12919 *
12920 * @param task
12921 * @return
12922 */
12923void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12924{
12925 LogFlowThisFuncEnter();
12926
12927 AutoCaller autoCaller(this);
12928 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12929 if (FAILED(autoCaller.rc()))
12930 {
12931 /* we might have been uninitialized because the session was accidentally
12932 * closed by the client, so don't assert */
12933 HRESULT rc = setError(E_FAIL,
12934 tr("The session has been accidentally closed"));
12935 task.m_pProgress->i_notifyComplete(rc);
12936 LogFlowThisFuncLeave();
12937 return;
12938 }
12939
12940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12941
12942 HRESULT rc = S_OK;
12943
12944 try
12945 {
12946 ComPtr<IInternalSessionControl> directControl;
12947 if (mData->mSession.mLockType == LockType_VM)
12948 directControl = mData->mSession.mDirectControl;
12949 if (directControl.isNull())
12950 throw setError(VBOX_E_INVALID_VM_STATE,
12951 tr("Trying to save state without a running VM"));
12952 alock.release();
12953 BOOL fSuspendedBySave;
12954 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12955 Assert(!fSuspendedBySave);
12956 alock.acquire();
12957
12958 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12959 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12960 throw E_FAIL);
12961
12962 if (SUCCEEDED(rc))
12963 {
12964 mSSData->strStateFilePath = task.m_strStateFilePath;
12965
12966 /* save all VM settings */
12967 rc = i_saveSettings(NULL);
12968 // no need to check whether VirtualBox.xml needs saving also since
12969 // we can't have a name change pending at this point
12970 }
12971 else
12972 {
12973 // On failure, set the state to the state we had at the beginning.
12974 i_setMachineState(task.m_machineStateBackup);
12975 i_updateMachineStateOnClient();
12976
12977 // Delete the saved state file (might have been already created).
12978 // No need to check whether this is shared with a snapshot here
12979 // because we certainly created a fresh saved state file here.
12980 RTFileDelete(task.m_strStateFilePath.c_str());
12981 }
12982 }
12983 catch (HRESULT aRC) { rc = aRC; }
12984
12985 task.m_pProgress->i_notifyComplete(rc);
12986
12987 LogFlowThisFuncLeave();
12988}
12989
12990/**
12991 * @note Locks this object for writing.
12992 */
12993HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12994{
12995 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12996}
12997
12998HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12999{
13000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13001
13002 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
13003 if (FAILED(rc)) return rc;
13004
13005 if ( mData->mMachineState != MachineState_Running
13006 && mData->mMachineState != MachineState_Paused
13007 )
13008 return setError(VBOX_E_INVALID_VM_STATE,
13009 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
13010 Global::stringifyMachineState(mData->mMachineState));
13011
13012 ComObjPtr<Progress> pProgress;
13013 pProgress.createObject();
13014 rc = pProgress->init(i_getVirtualBox(),
13015 static_cast<IMachine *>(this) /* aInitiator */,
13016 tr("Saving the execution state of the virtual machine"),
13017 FALSE /* aCancelable */);
13018 if (FAILED(rc))
13019 return rc;
13020
13021 Utf8Str strStateFilePath;
13022 i_composeSavedStateFilename(strStateFilePath);
13023
13024 /* create and start the task on a separate thread (note that it will not
13025 * start working until we release alock) */
13026 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
13027 rc = pTask->createThread();
13028 if (FAILED(rc))
13029 return rc;
13030
13031 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
13032 i_setMachineState(MachineState_Saving);
13033 i_updateMachineStateOnClient();
13034
13035 pProgress.queryInterfaceTo(aProgress.asOutParam());
13036
13037 return S_OK;
13038}
13039
13040/**
13041 * @note Locks this object for writing.
13042 */
13043HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
13044{
13045 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13046
13047 HRESULT rc = i_checkStateDependency(MutableStateDep);
13048 if (FAILED(rc)) return rc;
13049
13050 if ( mData->mMachineState != MachineState_PoweredOff
13051 && mData->mMachineState != MachineState_Teleported
13052 && mData->mMachineState != MachineState_Aborted
13053 )
13054 return setError(VBOX_E_INVALID_VM_STATE,
13055 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
13056 Global::stringifyMachineState(mData->mMachineState));
13057
13058 com::Utf8Str stateFilePathFull;
13059 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13060 if (RT_FAILURE(vrc))
13061 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13062 tr("Invalid saved state file path '%s' (%Rrc)"),
13063 aSavedStateFile.c_str(),
13064 vrc);
13065
13066 mSSData->strStateFilePath = stateFilePathFull;
13067
13068 /* The below i_setMachineState() will detect the state transition and will
13069 * update the settings file */
13070
13071 return i_setMachineState(MachineState_Saved);
13072}
13073
13074/**
13075 * @note Locks this object for writing.
13076 */
13077HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13078{
13079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13080
13081 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13082 if (FAILED(rc)) return rc;
13083
13084 if (mData->mMachineState != MachineState_Saved)
13085 return setError(VBOX_E_INVALID_VM_STATE,
13086 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13087 Global::stringifyMachineState(mData->mMachineState));
13088
13089 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13090
13091 /*
13092 * Saved -> PoweredOff transition will be detected in the SessionMachine
13093 * and properly handled.
13094 */
13095 rc = i_setMachineState(MachineState_PoweredOff);
13096 return rc;
13097}
13098
13099
13100/**
13101 * @note Locks the same as #i_setMachineState() does.
13102 */
13103HRESULT SessionMachine::updateState(MachineState_T aState)
13104{
13105 return i_setMachineState(aState);
13106}
13107
13108/**
13109 * @note Locks this object for writing.
13110 */
13111HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13112{
13113 IProgress *pProgress(aProgress);
13114
13115 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13116
13117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13118
13119 if (mData->mSession.mState != SessionState_Locked)
13120 return VBOX_E_INVALID_OBJECT_STATE;
13121
13122 if (!mData->mSession.mProgress.isNull())
13123 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13124
13125 /* If we didn't reference the NAT network service yet, add a reference to
13126 * force a start */
13127 if (miNATNetworksStarted < 1)
13128 {
13129 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13130 {
13131 BOOL enabled;
13132 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13133 if ( FAILED(hrc)
13134 || !enabled)
13135 continue;
13136
13137 NetworkAttachmentType_T type;
13138 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13139 if ( SUCCEEDED(hrc)
13140 && type == NetworkAttachmentType_NATNetwork)
13141 {
13142 Bstr name;
13143 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13144 if (SUCCEEDED(hrc))
13145 {
13146 Utf8Str strName(name);
13147 LogRel(("VM '%s' starts using NAT network '%s'\n",
13148 mUserData->s.strName.c_str(), strName.c_str()));
13149 mPeer->lockHandle()->unlockWrite();
13150 mParent->i_natNetworkRefInc(strName);
13151#ifdef RT_LOCK_STRICT
13152 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13153#else
13154 mPeer->lockHandle()->lockWrite();
13155#endif
13156 }
13157 }
13158 }
13159 miNATNetworksStarted++;
13160 }
13161
13162 LogFlowThisFunc(("returns S_OK.\n"));
13163 return S_OK;
13164}
13165
13166/**
13167 * @note Locks this object for writing.
13168 */
13169HRESULT SessionMachine::endPowerUp(LONG aResult)
13170{
13171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13172
13173 if (mData->mSession.mState != SessionState_Locked)
13174 return VBOX_E_INVALID_OBJECT_STATE;
13175
13176 /* Finalize the LaunchVMProcess progress object. */
13177 if (mData->mSession.mProgress)
13178 {
13179 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13180 mData->mSession.mProgress.setNull();
13181 }
13182
13183 if (SUCCEEDED((HRESULT)aResult))
13184 {
13185#ifdef VBOX_WITH_RESOURCE_USAGE_API
13186 /* The VM has been powered up successfully, so it makes sense
13187 * now to offer the performance metrics for a running machine
13188 * object. Doing it earlier wouldn't be safe. */
13189 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13190 mData->mSession.mPID);
13191#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13192 }
13193
13194 return S_OK;
13195}
13196
13197/**
13198 * @note Locks this object for writing.
13199 */
13200HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13201{
13202 LogFlowThisFuncEnter();
13203
13204 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13205
13206 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13207 E_FAIL);
13208
13209 /* create a progress object to track operation completion */
13210 ComObjPtr<Progress> pProgress;
13211 pProgress.createObject();
13212 pProgress->init(i_getVirtualBox(),
13213 static_cast<IMachine *>(this) /* aInitiator */,
13214 tr("Stopping the virtual machine"),
13215 FALSE /* aCancelable */);
13216
13217 /* fill in the console task data */
13218 mConsoleTaskData.mLastState = mData->mMachineState;
13219 mConsoleTaskData.mProgress = pProgress;
13220
13221 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13222 i_setMachineState(MachineState_Stopping);
13223
13224 pProgress.queryInterfaceTo(aProgress.asOutParam());
13225
13226 return S_OK;
13227}
13228
13229/**
13230 * @note Locks this object for writing.
13231 */
13232HRESULT SessionMachine::endPoweringDown(LONG aResult,
13233 const com::Utf8Str &aErrMsg)
13234{
13235 LogFlowThisFuncEnter();
13236
13237 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13238
13239 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13240 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13241 && mConsoleTaskData.mLastState != MachineState_Null,
13242 E_FAIL);
13243
13244 /*
13245 * On failure, set the state to the state we had when BeginPoweringDown()
13246 * was called (this is expected by Console::PowerDown() and the associated
13247 * task). On success the VM process already changed the state to
13248 * MachineState_PoweredOff, so no need to do anything.
13249 */
13250 if (FAILED(aResult))
13251 i_setMachineState(mConsoleTaskData.mLastState);
13252
13253 /* notify the progress object about operation completion */
13254 Assert(mConsoleTaskData.mProgress);
13255 if (SUCCEEDED(aResult))
13256 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13257 else
13258 {
13259 if (aErrMsg.length())
13260 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13261 COM_IIDOF(ISession),
13262 getComponentName(),
13263 aErrMsg.c_str());
13264 else
13265 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13266 }
13267
13268 /* clear out the temporary saved state data */
13269 mConsoleTaskData.mLastState = MachineState_Null;
13270 mConsoleTaskData.mProgress.setNull();
13271
13272 LogFlowThisFuncLeave();
13273 return S_OK;
13274}
13275
13276
13277/**
13278 * Goes through the USB filters of the given machine to see if the given
13279 * device matches any filter or not.
13280 *
13281 * @note Locks the same as USBController::hasMatchingFilter() does.
13282 */
13283HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13284 BOOL *aMatched,
13285 ULONG *aMaskedInterfaces)
13286{
13287 LogFlowThisFunc(("\n"));
13288
13289#ifdef VBOX_WITH_USB
13290 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13291#else
13292 NOREF(aDevice);
13293 NOREF(aMaskedInterfaces);
13294 *aMatched = FALSE;
13295#endif
13296
13297 return S_OK;
13298}
13299
13300/**
13301 * @note Locks the same as Host::captureUSBDevice() does.
13302 */
13303HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13304{
13305 LogFlowThisFunc(("\n"));
13306
13307#ifdef VBOX_WITH_USB
13308 /* if captureDeviceForVM() fails, it must have set extended error info */
13309 clearError();
13310 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13311 if (FAILED(rc)) return rc;
13312
13313 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13314 AssertReturn(service, E_FAIL);
13315 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13316#else
13317 NOREF(aId);
13318 return E_NOTIMPL;
13319#endif
13320}
13321
13322/**
13323 * @note Locks the same as Host::detachUSBDevice() does.
13324 */
13325HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13326 BOOL aDone)
13327{
13328 LogFlowThisFunc(("\n"));
13329
13330#ifdef VBOX_WITH_USB
13331 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13332 AssertReturn(service, E_FAIL);
13333 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13334#else
13335 NOREF(aId);
13336 NOREF(aDone);
13337 return E_NOTIMPL;
13338#endif
13339}
13340
13341/**
13342 * Inserts all machine filters to the USB proxy service and then calls
13343 * Host::autoCaptureUSBDevices().
13344 *
13345 * Called by Console from the VM process upon VM startup.
13346 *
13347 * @note Locks what called methods lock.
13348 */
13349HRESULT SessionMachine::autoCaptureUSBDevices()
13350{
13351 LogFlowThisFunc(("\n"));
13352
13353#ifdef VBOX_WITH_USB
13354 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13355 AssertComRC(rc);
13356 NOREF(rc);
13357
13358 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13359 AssertReturn(service, E_FAIL);
13360 return service->autoCaptureDevicesForVM(this);
13361#else
13362 return S_OK;
13363#endif
13364}
13365
13366/**
13367 * Removes all machine filters from the USB proxy service and then calls
13368 * Host::detachAllUSBDevices().
13369 *
13370 * Called by Console from the VM process upon normal VM termination or by
13371 * SessionMachine::uninit() upon abnormal VM termination (from under the
13372 * Machine/SessionMachine lock).
13373 *
13374 * @note Locks what called methods lock.
13375 */
13376HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13377{
13378 LogFlowThisFunc(("\n"));
13379
13380#ifdef VBOX_WITH_USB
13381 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13382 AssertComRC(rc);
13383 NOREF(rc);
13384
13385 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13386 AssertReturn(service, E_FAIL);
13387 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13388#else
13389 NOREF(aDone);
13390 return S_OK;
13391#endif
13392}
13393
13394/**
13395 * @note Locks this object for writing.
13396 */
13397HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13398 ComPtr<IProgress> &aProgress)
13399{
13400 LogFlowThisFuncEnter();
13401
13402 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13403 /*
13404 * We don't assert below because it might happen that a non-direct session
13405 * informs us it is closed right after we've been uninitialized -- it's ok.
13406 */
13407
13408 /* get IInternalSessionControl interface */
13409 ComPtr<IInternalSessionControl> control(aSession);
13410
13411 ComAssertRet(!control.isNull(), E_INVALIDARG);
13412
13413 /* Creating a Progress object requires the VirtualBox lock, and
13414 * thus locking it here is required by the lock order rules. */
13415 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13416
13417 if (control == mData->mSession.mDirectControl)
13418 {
13419 /* The direct session is being normally closed by the client process
13420 * ----------------------------------------------------------------- */
13421
13422 /* go to the closing state (essential for all open*Session() calls and
13423 * for #i_checkForDeath()) */
13424 Assert(mData->mSession.mState == SessionState_Locked);
13425 mData->mSession.mState = SessionState_Unlocking;
13426
13427 /* set direct control to NULL to release the remote instance */
13428 mData->mSession.mDirectControl.setNull();
13429 LogFlowThisFunc(("Direct control is set to NULL\n"));
13430
13431 if (mData->mSession.mProgress)
13432 {
13433 /* finalize the progress, someone might wait if a frontend
13434 * closes the session before powering on the VM. */
13435 mData->mSession.mProgress->notifyComplete(E_FAIL,
13436 COM_IIDOF(ISession),
13437 getComponentName(),
13438 tr("The VM session was closed before any attempt to power it on"));
13439 mData->mSession.mProgress.setNull();
13440 }
13441
13442 /* Create the progress object the client will use to wait until
13443 * #i_checkForDeath() is called to uninitialize this session object after
13444 * it releases the IPC semaphore.
13445 * Note! Because we're "reusing" mProgress here, this must be a proxy
13446 * object just like for LaunchVMProcess. */
13447 Assert(mData->mSession.mProgress.isNull());
13448 ComObjPtr<ProgressProxy> progress;
13449 progress.createObject();
13450 ComPtr<IUnknown> pPeer(mPeer);
13451 progress->init(mParent, pPeer,
13452 Bstr(tr("Closing session")).raw(),
13453 FALSE /* aCancelable */);
13454 progress.queryInterfaceTo(aProgress.asOutParam());
13455 mData->mSession.mProgress = progress;
13456 }
13457 else
13458 {
13459 /* the remote session is being normally closed */
13460 bool found = false;
13461 for (Data::Session::RemoteControlList::iterator
13462 it = mData->mSession.mRemoteControls.begin();
13463 it != mData->mSession.mRemoteControls.end();
13464 ++it)
13465 {
13466 if (control == *it)
13467 {
13468 found = true;
13469 // This MUST be erase(it), not remove(*it) as the latter
13470 // triggers a very nasty use after free due to the place where
13471 // the value "lives".
13472 mData->mSession.mRemoteControls.erase(it);
13473 break;
13474 }
13475 }
13476 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13477 E_INVALIDARG);
13478 }
13479
13480 /* signal the client watcher thread, because the client is going away */
13481 mParent->i_updateClientWatcher();
13482
13483 LogFlowThisFuncLeave();
13484 return S_OK;
13485}
13486
13487HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13488 std::vector<com::Utf8Str> &aValues,
13489 std::vector<LONG64> &aTimestamps,
13490 std::vector<com::Utf8Str> &aFlags)
13491{
13492 LogFlowThisFunc(("\n"));
13493
13494#ifdef VBOX_WITH_GUEST_PROPS
13495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13496
13497 size_t cEntries = mHWData->mGuestProperties.size();
13498 aNames.resize(cEntries);
13499 aValues.resize(cEntries);
13500 aTimestamps.resize(cEntries);
13501 aFlags.resize(cEntries);
13502
13503 size_t i = 0;
13504 for (HWData::GuestPropertyMap::const_iterator
13505 it = mHWData->mGuestProperties.begin();
13506 it != mHWData->mGuestProperties.end();
13507 ++it, ++i)
13508 {
13509 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13510 aNames[i] = it->first;
13511 aValues[i] = it->second.strValue;
13512 aTimestamps[i] = it->second.mTimestamp;
13513
13514 /* If it is NULL, keep it NULL. */
13515 if (it->second.mFlags)
13516 {
13517 GuestPropWriteFlags(it->second.mFlags, szFlags);
13518 aFlags[i] = szFlags;
13519 }
13520 else
13521 aFlags[i] = "";
13522 }
13523 return S_OK;
13524#else
13525 ReturnComNotImplemented();
13526#endif
13527}
13528
13529HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13530 const com::Utf8Str &aValue,
13531 LONG64 aTimestamp,
13532 const com::Utf8Str &aFlags)
13533{
13534 LogFlowThisFunc(("\n"));
13535
13536#ifdef VBOX_WITH_GUEST_PROPS
13537 try
13538 {
13539 /*
13540 * Convert input up front.
13541 */
13542 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13543 if (aFlags.length())
13544 {
13545 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13546 AssertRCReturn(vrc, E_INVALIDARG);
13547 }
13548
13549 /*
13550 * Now grab the object lock, validate the state and do the update.
13551 */
13552
13553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13554
13555 if (!Global::IsOnline(mData->mMachineState))
13556 {
13557 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13558 VBOX_E_INVALID_VM_STATE);
13559 }
13560
13561 i_setModified(IsModified_MachineData);
13562 mHWData.backup();
13563
13564 bool fDelete = !aValue.length();
13565 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13566 if (it != mHWData->mGuestProperties.end())
13567 {
13568 if (!fDelete)
13569 {
13570 it->second.strValue = aValue;
13571 it->second.mTimestamp = aTimestamp;
13572 it->second.mFlags = fFlags;
13573 }
13574 else
13575 mHWData->mGuestProperties.erase(it);
13576
13577 mData->mGuestPropertiesModified = TRUE;
13578 }
13579 else if (!fDelete)
13580 {
13581 HWData::GuestProperty prop;
13582 prop.strValue = aValue;
13583 prop.mTimestamp = aTimestamp;
13584 prop.mFlags = fFlags;
13585
13586 mHWData->mGuestProperties[aName] = prop;
13587 mData->mGuestPropertiesModified = TRUE;
13588 }
13589
13590 alock.release();
13591
13592 mParent->i_onGuestPropertyChange(mData->mUuid,
13593 Bstr(aName).raw(),
13594 Bstr(aValue).raw(),
13595 Bstr(aFlags).raw());
13596 }
13597 catch (...)
13598 {
13599 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13600 }
13601 return S_OK;
13602#else
13603 ReturnComNotImplemented();
13604#endif
13605}
13606
13607
13608HRESULT SessionMachine::lockMedia()
13609{
13610 AutoMultiWriteLock2 alock(this->lockHandle(),
13611 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13612
13613 AssertReturn( mData->mMachineState == MachineState_Starting
13614 || mData->mMachineState == MachineState_Restoring
13615 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13616
13617 clearError();
13618 alock.release();
13619 return i_lockMedia();
13620}
13621
13622HRESULT SessionMachine::unlockMedia()
13623{
13624 HRESULT hrc = i_unlockMedia();
13625 return hrc;
13626}
13627
13628HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13629 ComPtr<IMediumAttachment> &aNewAttachment)
13630{
13631 // request the host lock first, since might be calling Host methods for getting host drives;
13632 // next, protect the media tree all the while we're in here, as well as our member variables
13633 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13634 this->lockHandle(),
13635 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13636
13637 IMediumAttachment *iAttach = aAttachment;
13638 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13639
13640 Utf8Str ctrlName;
13641 LONG lPort;
13642 LONG lDevice;
13643 bool fTempEject;
13644 {
13645 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13646
13647 /* Need to query the details first, as the IMediumAttachment reference
13648 * might be to the original settings, which we are going to change. */
13649 ctrlName = pAttach->i_getControllerName();
13650 lPort = pAttach->i_getPort();
13651 lDevice = pAttach->i_getDevice();
13652 fTempEject = pAttach->i_getTempEject();
13653 }
13654
13655 if (!fTempEject)
13656 {
13657 /* Remember previously mounted medium. The medium before taking the
13658 * backup is not necessarily the same thing. */
13659 ComObjPtr<Medium> oldmedium;
13660 oldmedium = pAttach->i_getMedium();
13661
13662 i_setModified(IsModified_Storage);
13663 mMediumAttachments.backup();
13664
13665 // The backup operation makes the pAttach reference point to the
13666 // old settings. Re-get the correct reference.
13667 pAttach = i_findAttachment(*mMediumAttachments.data(),
13668 ctrlName,
13669 lPort,
13670 lDevice);
13671
13672 {
13673 AutoCaller autoAttachCaller(this);
13674 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13675
13676 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13677 if (!oldmedium.isNull())
13678 oldmedium->i_removeBackReference(mData->mUuid);
13679
13680 pAttach->i_updateMedium(NULL);
13681 pAttach->i_updateEjected();
13682 }
13683
13684 i_setModified(IsModified_Storage);
13685 }
13686 else
13687 {
13688 {
13689 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13690 pAttach->i_updateEjected();
13691 }
13692 }
13693
13694 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13695
13696 return S_OK;
13697}
13698
13699HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13700 com::Utf8Str &aResult)
13701{
13702 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13703
13704 HRESULT hr = S_OK;
13705
13706 if (!mAuthLibCtx.hAuthLibrary)
13707 {
13708 /* Load the external authentication library. */
13709 Bstr authLibrary;
13710 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13711
13712 Utf8Str filename = authLibrary;
13713
13714 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13715 if (RT_FAILURE(vrc))
13716 hr = setErrorBoth(E_FAIL, vrc,
13717 tr("Could not load the external authentication library '%s' (%Rrc)"),
13718 filename.c_str(), vrc);
13719 }
13720
13721 /* The auth library might need the machine lock. */
13722 alock.release();
13723
13724 if (FAILED(hr))
13725 return hr;
13726
13727 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13728 {
13729 enum VRDEAuthParams
13730 {
13731 parmUuid = 1,
13732 parmGuestJudgement,
13733 parmUser,
13734 parmPassword,
13735 parmDomain,
13736 parmClientId
13737 };
13738
13739 AuthResult result = AuthResultAccessDenied;
13740
13741 Guid uuid(aAuthParams[parmUuid]);
13742 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13743 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13744
13745 result = AuthLibAuthenticate(&mAuthLibCtx,
13746 uuid.raw(), guestJudgement,
13747 aAuthParams[parmUser].c_str(),
13748 aAuthParams[parmPassword].c_str(),
13749 aAuthParams[parmDomain].c_str(),
13750 u32ClientId);
13751
13752 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13753 size_t cbPassword = aAuthParams[parmPassword].length();
13754 if (cbPassword)
13755 {
13756 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13757 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13758 }
13759
13760 if (result == AuthResultAccessGranted)
13761 aResult = "granted";
13762 else
13763 aResult = "denied";
13764
13765 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13766 aAuthParams[parmUser].c_str(), aResult.c_str()));
13767 }
13768 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13769 {
13770 enum VRDEAuthDisconnectParams
13771 {
13772 parmUuid = 1,
13773 parmClientId
13774 };
13775
13776 Guid uuid(aAuthParams[parmUuid]);
13777 uint32_t u32ClientId = 0;
13778 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13779 }
13780 else
13781 {
13782 hr = E_INVALIDARG;
13783 }
13784
13785 return hr;
13786}
13787
13788// public methods only for internal purposes
13789/////////////////////////////////////////////////////////////////////////////
13790
13791#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13792/**
13793 * Called from the client watcher thread to check for expected or unexpected
13794 * death of the client process that has a direct session to this machine.
13795 *
13796 * On Win32 and on OS/2, this method is called only when we've got the
13797 * mutex (i.e. the client has either died or terminated normally) so it always
13798 * returns @c true (the client is terminated, the session machine is
13799 * uninitialized).
13800 *
13801 * On other platforms, the method returns @c true if the client process has
13802 * terminated normally or abnormally and the session machine was uninitialized,
13803 * and @c false if the client process is still alive.
13804 *
13805 * @note Locks this object for writing.
13806 */
13807bool SessionMachine::i_checkForDeath()
13808{
13809 Uninit::Reason reason;
13810 bool terminated = false;
13811
13812 /* Enclose autoCaller with a block because calling uninit() from under it
13813 * will deadlock. */
13814 {
13815 AutoCaller autoCaller(this);
13816 if (!autoCaller.isOk())
13817 {
13818 /* return true if not ready, to cause the client watcher to exclude
13819 * the corresponding session from watching */
13820 LogFlowThisFunc(("Already uninitialized!\n"));
13821 return true;
13822 }
13823
13824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13825
13826 /* Determine the reason of death: if the session state is Closing here,
13827 * everything is fine. Otherwise it means that the client did not call
13828 * OnSessionEnd() before it released the IPC semaphore. This may happen
13829 * either because the client process has abnormally terminated, or
13830 * because it simply forgot to call ISession::Close() before exiting. We
13831 * threat the latter also as an abnormal termination (see
13832 * Session::uninit() for details). */
13833 reason = mData->mSession.mState == SessionState_Unlocking ?
13834 Uninit::Normal :
13835 Uninit::Abnormal;
13836
13837 if (mClientToken)
13838 terminated = mClientToken->release();
13839 } /* AutoCaller block */
13840
13841 if (terminated)
13842 uninit(reason);
13843
13844 return terminated;
13845}
13846
13847void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13848{
13849 LogFlowThisFunc(("\n"));
13850
13851 strTokenId.setNull();
13852
13853 AutoCaller autoCaller(this);
13854 AssertComRCReturnVoid(autoCaller.rc());
13855
13856 Assert(mClientToken);
13857 if (mClientToken)
13858 mClientToken->getId(strTokenId);
13859}
13860#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13861IToken *SessionMachine::i_getToken()
13862{
13863 LogFlowThisFunc(("\n"));
13864
13865 AutoCaller autoCaller(this);
13866 AssertComRCReturn(autoCaller.rc(), NULL);
13867
13868 Assert(mClientToken);
13869 if (mClientToken)
13870 return mClientToken->getToken();
13871 else
13872 return NULL;
13873}
13874#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13875
13876Machine::ClientToken *SessionMachine::i_getClientToken()
13877{
13878 LogFlowThisFunc(("\n"));
13879
13880 AutoCaller autoCaller(this);
13881 AssertComRCReturn(autoCaller.rc(), NULL);
13882
13883 return mClientToken;
13884}
13885
13886
13887/**
13888 * @note Locks this object for reading.
13889 */
13890HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13891{
13892 LogFlowThisFunc(("\n"));
13893
13894 AutoCaller autoCaller(this);
13895 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13896
13897 ComPtr<IInternalSessionControl> directControl;
13898 {
13899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13900 if (mData->mSession.mLockType == LockType_VM)
13901 directControl = mData->mSession.mDirectControl;
13902 }
13903
13904 /* ignore notifications sent after #OnSessionEnd() is called */
13905 if (!directControl)
13906 return S_OK;
13907
13908 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13909}
13910
13911/**
13912 * @note Locks this object for reading.
13913 */
13914HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13915 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13916 IN_BSTR aGuestIp, LONG aGuestPort)
13917{
13918 LogFlowThisFunc(("\n"));
13919
13920 AutoCaller autoCaller(this);
13921 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13922
13923 ComPtr<IInternalSessionControl> directControl;
13924 {
13925 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13926 if (mData->mSession.mLockType == LockType_VM)
13927 directControl = mData->mSession.mDirectControl;
13928 }
13929
13930 /* ignore notifications sent after #OnSessionEnd() is called */
13931 if (!directControl)
13932 return S_OK;
13933 /*
13934 * instead acting like callback we ask IVirtualBox deliver corresponding event
13935 */
13936
13937 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13938 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13939 return S_OK;
13940}
13941
13942/**
13943 * @note Locks this object for reading.
13944 */
13945HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13946{
13947 LogFlowThisFunc(("\n"));
13948
13949 AutoCaller autoCaller(this);
13950 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13951
13952 ComPtr<IInternalSessionControl> directControl;
13953 {
13954 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13955 if (mData->mSession.mLockType == LockType_VM)
13956 directControl = mData->mSession.mDirectControl;
13957 }
13958
13959 /* ignore notifications sent after #OnSessionEnd() is called */
13960 if (!directControl)
13961 return S_OK;
13962
13963 return directControl->OnAudioAdapterChange(audioAdapter);
13964}
13965
13966/**
13967 * @note Locks this object for reading.
13968 */
13969HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13970{
13971 LogFlowThisFunc(("\n"));
13972
13973 AutoCaller autoCaller(this);
13974 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13975
13976 ComPtr<IInternalSessionControl> directControl;
13977 {
13978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13979 if (mData->mSession.mLockType == LockType_VM)
13980 directControl = mData->mSession.mDirectControl;
13981 }
13982
13983 /* ignore notifications sent after #OnSessionEnd() is called */
13984 if (!directControl)
13985 return S_OK;
13986
13987 return directControl->OnSerialPortChange(serialPort);
13988}
13989
13990/**
13991 * @note Locks this object for reading.
13992 */
13993HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13994{
13995 LogFlowThisFunc(("\n"));
13996
13997 AutoCaller autoCaller(this);
13998 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13999
14000 ComPtr<IInternalSessionControl> directControl;
14001 {
14002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14003 if (mData->mSession.mLockType == LockType_VM)
14004 directControl = mData->mSession.mDirectControl;
14005 }
14006
14007 /* ignore notifications sent after #OnSessionEnd() is called */
14008 if (!directControl)
14009 return S_OK;
14010
14011 return directControl->OnParallelPortChange(parallelPort);
14012}
14013
14014/**
14015 * @note Locks this object for reading.
14016 */
14017HRESULT SessionMachine::i_onStorageControllerChange()
14018{
14019 LogFlowThisFunc(("\n"));
14020
14021 AutoCaller autoCaller(this);
14022 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14023
14024 ComPtr<IInternalSessionControl> directControl;
14025 {
14026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14027 if (mData->mSession.mLockType == LockType_VM)
14028 directControl = mData->mSession.mDirectControl;
14029 }
14030
14031 /* ignore notifications sent after #OnSessionEnd() is called */
14032 if (!directControl)
14033 return S_OK;
14034
14035 return directControl->OnStorageControllerChange();
14036}
14037
14038/**
14039 * @note Locks this object for reading.
14040 */
14041HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14042{
14043 LogFlowThisFunc(("\n"));
14044
14045 AutoCaller autoCaller(this);
14046 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14047
14048 ComPtr<IInternalSessionControl> directControl;
14049 {
14050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14051 if (mData->mSession.mLockType == LockType_VM)
14052 directControl = mData->mSession.mDirectControl;
14053 }
14054
14055 mParent->i_onMediumChanged(aAttachment);
14056
14057 /* ignore notifications sent after #OnSessionEnd() is called */
14058 if (!directControl)
14059 return S_OK;
14060
14061 return directControl->OnMediumChange(aAttachment, aForce);
14062}
14063
14064/**
14065 * @note Locks this object for reading.
14066 */
14067HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14068{
14069 LogFlowThisFunc(("\n"));
14070
14071 AutoCaller autoCaller(this);
14072 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14073
14074 ComPtr<IInternalSessionControl> directControl;
14075 {
14076 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14077 if (mData->mSession.mLockType == LockType_VM)
14078 directControl = mData->mSession.mDirectControl;
14079 }
14080
14081 /* ignore notifications sent after #OnSessionEnd() is called */
14082 if (!directControl)
14083 return S_OK;
14084
14085 return directControl->OnCPUChange(aCPU, aRemove);
14086}
14087
14088HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14089{
14090 LogFlowThisFunc(("\n"));
14091
14092 AutoCaller autoCaller(this);
14093 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14094
14095 ComPtr<IInternalSessionControl> directControl;
14096 {
14097 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14098 if (mData->mSession.mLockType == LockType_VM)
14099 directControl = mData->mSession.mDirectControl;
14100 }
14101
14102 /* ignore notifications sent after #OnSessionEnd() is called */
14103 if (!directControl)
14104 return S_OK;
14105
14106 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14107}
14108
14109/**
14110 * @note Locks this object for reading.
14111 */
14112HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14113{
14114 LogFlowThisFunc(("\n"));
14115
14116 AutoCaller autoCaller(this);
14117 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14118
14119 ComPtr<IInternalSessionControl> directControl;
14120 {
14121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14122 if (mData->mSession.mLockType == LockType_VM)
14123 directControl = mData->mSession.mDirectControl;
14124 }
14125
14126 /* ignore notifications sent after #OnSessionEnd() is called */
14127 if (!directControl)
14128 return S_OK;
14129
14130 return directControl->OnVRDEServerChange(aRestart);
14131}
14132
14133/**
14134 * @note Locks this object for reading.
14135 */
14136HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14137{
14138 LogFlowThisFunc(("\n"));
14139
14140 AutoCaller autoCaller(this);
14141 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14142
14143 ComPtr<IInternalSessionControl> directControl;
14144 {
14145 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14146 if (mData->mSession.mLockType == LockType_VM)
14147 directControl = mData->mSession.mDirectControl;
14148 }
14149
14150 /* ignore notifications sent after #OnSessionEnd() is called */
14151 if (!directControl)
14152 return S_OK;
14153
14154 return directControl->OnRecordingChange(aEnable);
14155}
14156
14157/**
14158 * @note Locks this object for reading.
14159 */
14160HRESULT SessionMachine::i_onUSBControllerChange()
14161{
14162 LogFlowThisFunc(("\n"));
14163
14164 AutoCaller autoCaller(this);
14165 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14166
14167 ComPtr<IInternalSessionControl> directControl;
14168 {
14169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14170 if (mData->mSession.mLockType == LockType_VM)
14171 directControl = mData->mSession.mDirectControl;
14172 }
14173
14174 /* ignore notifications sent after #OnSessionEnd() is called */
14175 if (!directControl)
14176 return S_OK;
14177
14178 return directControl->OnUSBControllerChange();
14179}
14180
14181/**
14182 * @note Locks this object for reading.
14183 */
14184HRESULT SessionMachine::i_onSharedFolderChange()
14185{
14186 LogFlowThisFunc(("\n"));
14187
14188 AutoCaller autoCaller(this);
14189 AssertComRCReturnRC(autoCaller.rc());
14190
14191 ComPtr<IInternalSessionControl> directControl;
14192 {
14193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14194 if (mData->mSession.mLockType == LockType_VM)
14195 directControl = mData->mSession.mDirectControl;
14196 }
14197
14198 /* ignore notifications sent after #OnSessionEnd() is called */
14199 if (!directControl)
14200 return S_OK;
14201
14202 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14203}
14204
14205/**
14206 * @note Locks this object for reading.
14207 */
14208HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14209{
14210 LogFlowThisFunc(("\n"));
14211
14212 AutoCaller autoCaller(this);
14213 AssertComRCReturnRC(autoCaller.rc());
14214
14215 ComPtr<IInternalSessionControl> directControl;
14216 {
14217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14218 if (mData->mSession.mLockType == LockType_VM)
14219 directControl = mData->mSession.mDirectControl;
14220 }
14221
14222 /* ignore notifications sent after #OnSessionEnd() is called */
14223 if (!directControl)
14224 return S_OK;
14225
14226 return directControl->OnClipboardModeChange(aClipboardMode);
14227}
14228
14229/**
14230 * @note Locks this object for reading.
14231 */
14232HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14233{
14234 LogFlowThisFunc(("\n"));
14235
14236 AutoCaller autoCaller(this);
14237 AssertComRCReturnRC(autoCaller.rc());
14238
14239 ComPtr<IInternalSessionControl> directControl;
14240 {
14241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14242 if (mData->mSession.mLockType == LockType_VM)
14243 directControl = mData->mSession.mDirectControl;
14244 }
14245
14246 /* ignore notifications sent after #OnSessionEnd() is called */
14247 if (!directControl)
14248 return S_OK;
14249
14250 return directControl->OnDnDModeChange(aDnDMode);
14251}
14252
14253/**
14254 * @note Locks this object for reading.
14255 */
14256HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14257{
14258 LogFlowThisFunc(("\n"));
14259
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14262
14263 ComPtr<IInternalSessionControl> directControl;
14264 {
14265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14266 if (mData->mSession.mLockType == LockType_VM)
14267 directControl = mData->mSession.mDirectControl;
14268 }
14269
14270 /* ignore notifications sent after #OnSessionEnd() is called */
14271 if (!directControl)
14272 return S_OK;
14273
14274 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14275}
14276
14277/**
14278 * @note Locks this object for reading.
14279 */
14280HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14281{
14282 LogFlowThisFunc(("\n"));
14283
14284 AutoCaller autoCaller(this);
14285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14286
14287 ComPtr<IInternalSessionControl> directControl;
14288 {
14289 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14290 if (mData->mSession.mLockType == LockType_VM)
14291 directControl = mData->mSession.mDirectControl;
14292 }
14293
14294 mParent->i_onStorageDeviceChanged(aAttachment, aRemove, aSilent);
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14301}
14302
14303/**
14304 * Returns @c true if this machine's USB controller reports it has a matching
14305 * filter for the given USB device and @c false otherwise.
14306 *
14307 * @note locks this object for reading.
14308 */
14309bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14310{
14311 AutoCaller autoCaller(this);
14312 /* silently return if not ready -- this method may be called after the
14313 * direct machine session has been called */
14314 if (!autoCaller.isOk())
14315 return false;
14316
14317#ifdef VBOX_WITH_USB
14318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14319
14320 switch (mData->mMachineState)
14321 {
14322 case MachineState_Starting:
14323 case MachineState_Restoring:
14324 case MachineState_TeleportingIn:
14325 case MachineState_Paused:
14326 case MachineState_Running:
14327 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14328 * elsewhere... */
14329 alock.release();
14330 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14331 default: break;
14332 }
14333#else
14334 NOREF(aDevice);
14335 NOREF(aMaskedIfs);
14336#endif
14337 return false;
14338}
14339
14340/**
14341 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14342 */
14343HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14344 IVirtualBoxErrorInfo *aError,
14345 ULONG aMaskedIfs,
14346 const com::Utf8Str &aCaptureFilename)
14347{
14348 LogFlowThisFunc(("\n"));
14349
14350 AutoCaller autoCaller(this);
14351
14352 /* This notification may happen after the machine object has been
14353 * uninitialized (the session was closed), so don't assert. */
14354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* fail on notifications sent after #OnSessionEnd() is called, it is
14364 * expected by the caller */
14365 if (!directControl)
14366 return E_FAIL;
14367
14368 /* No locks should be held at this point. */
14369 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14370 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14371
14372 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14373}
14374
14375/**
14376 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14377 */
14378HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14379 IVirtualBoxErrorInfo *aError)
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384
14385 /* This notification may happen after the machine object has been
14386 * uninitialized (the session was closed), so don't assert. */
14387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14388
14389 ComPtr<IInternalSessionControl> directControl;
14390 {
14391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14392 if (mData->mSession.mLockType == LockType_VM)
14393 directControl = mData->mSession.mDirectControl;
14394 }
14395
14396 /* fail on notifications sent after #OnSessionEnd() is called, it is
14397 * expected by the caller */
14398 if (!directControl)
14399 return E_FAIL;
14400
14401 /* No locks should be held at this point. */
14402 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14403 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14404
14405 return directControl->OnUSBDeviceDetach(aId, aError);
14406}
14407
14408// protected methods
14409/////////////////////////////////////////////////////////////////////////////
14410
14411/**
14412 * Deletes the given file if it is no longer in use by either the current machine state
14413 * (if the machine is "saved") or any of the machine's snapshots.
14414 *
14415 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14416 * but is different for each SnapshotMachine. When calling this, the order of calling this
14417 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14418 * is therefore critical. I know, it's all rather messy.
14419 *
14420 * @param strStateFile
14421 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14422 * the test for whether the saved state file is in use.
14423 */
14424void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14425 Snapshot *pSnapshotToIgnore)
14426{
14427 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14428 if ( (strStateFile.isNotEmpty())
14429 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14430 )
14431 // ... and it must also not be shared with other snapshots
14432 if ( !mData->mFirstSnapshot
14433 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14434 // this checks the SnapshotMachine's state file paths
14435 )
14436 RTFileDelete(strStateFile.c_str());
14437}
14438
14439/**
14440 * Locks the attached media.
14441 *
14442 * All attached hard disks are locked for writing and DVD/floppy are locked for
14443 * reading. Parents of attached hard disks (if any) are locked for reading.
14444 *
14445 * This method also performs accessibility check of all media it locks: if some
14446 * media is inaccessible, the method will return a failure and a bunch of
14447 * extended error info objects per each inaccessible medium.
14448 *
14449 * Note that this method is atomic: if it returns a success, all media are
14450 * locked as described above; on failure no media is locked at all (all
14451 * succeeded individual locks will be undone).
14452 *
14453 * The caller is responsible for doing the necessary state sanity checks.
14454 *
14455 * The locks made by this method must be undone by calling #unlockMedia() when
14456 * no more needed.
14457 */
14458HRESULT SessionMachine::i_lockMedia()
14459{
14460 AutoCaller autoCaller(this);
14461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14462
14463 AutoMultiWriteLock2 alock(this->lockHandle(),
14464 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14465
14466 /* bail out if trying to lock things with already set up locking */
14467 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14468
14469 MultiResult mrc(S_OK);
14470
14471 /* Collect locking information for all medium objects attached to the VM. */
14472 for (MediumAttachmentList::const_iterator
14473 it = mMediumAttachments->begin();
14474 it != mMediumAttachments->end();
14475 ++it)
14476 {
14477 MediumAttachment *pAtt = *it;
14478 DeviceType_T devType = pAtt->i_getType();
14479 Medium *pMedium = pAtt->i_getMedium();
14480
14481 MediumLockList *pMediumLockList(new MediumLockList());
14482 // There can be attachments without a medium (floppy/dvd), and thus
14483 // it's impossible to create a medium lock list. It still makes sense
14484 // to have the empty medium lock list in the map in case a medium is
14485 // attached later.
14486 if (pMedium != NULL)
14487 {
14488 MediumType_T mediumType = pMedium->i_getType();
14489 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14490 || mediumType == MediumType_Shareable;
14491 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14492
14493 alock.release();
14494 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14495 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14496 false /* fMediumLockWriteAll */,
14497 NULL,
14498 *pMediumLockList);
14499 alock.acquire();
14500 if (FAILED(mrc))
14501 {
14502 delete pMediumLockList;
14503 mData->mSession.mLockedMedia.Clear();
14504 break;
14505 }
14506 }
14507
14508 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14509 if (FAILED(rc))
14510 {
14511 mData->mSession.mLockedMedia.Clear();
14512 mrc = setError(rc,
14513 tr("Collecting locking information for all attached media failed"));
14514 break;
14515 }
14516 }
14517
14518 if (SUCCEEDED(mrc))
14519 {
14520 /* Now lock all media. If this fails, nothing is locked. */
14521 alock.release();
14522 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14523 alock.acquire();
14524 if (FAILED(rc))
14525 {
14526 mrc = setError(rc,
14527 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14528 }
14529 }
14530
14531 return mrc;
14532}
14533
14534/**
14535 * Undoes the locks made by by #lockMedia().
14536 */
14537HRESULT SessionMachine::i_unlockMedia()
14538{
14539 AutoCaller autoCaller(this);
14540 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14541
14542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14543
14544 /* we may be holding important error info on the current thread;
14545 * preserve it */
14546 ErrorInfoKeeper eik;
14547
14548 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14549 AssertComRC(rc);
14550 return rc;
14551}
14552
14553/**
14554 * Helper to change the machine state (reimplementation).
14555 *
14556 * @note Locks this object for writing.
14557 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14558 * it can cause crashes in random places due to unexpectedly committing
14559 * the current settings. The caller is responsible for that. The call
14560 * to saveStateSettings is fine, because this method does not commit.
14561 */
14562HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14563{
14564 LogFlowThisFuncEnter();
14565 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14566
14567 AutoCaller autoCaller(this);
14568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14569
14570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14571
14572 MachineState_T oldMachineState = mData->mMachineState;
14573
14574 AssertMsgReturn(oldMachineState != aMachineState,
14575 ("oldMachineState=%s, aMachineState=%s\n",
14576 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14577 E_FAIL);
14578
14579 HRESULT rc = S_OK;
14580
14581 int stsFlags = 0;
14582 bool deleteSavedState = false;
14583
14584 /* detect some state transitions */
14585
14586 if ( ( oldMachineState == MachineState_Saved
14587 && aMachineState == MachineState_Restoring)
14588 || ( ( oldMachineState == MachineState_PoweredOff
14589 || oldMachineState == MachineState_Teleported
14590 || oldMachineState == MachineState_Aborted
14591 )
14592 && ( aMachineState == MachineState_TeleportingIn
14593 || aMachineState == MachineState_Starting
14594 )
14595 )
14596 )
14597 {
14598 /* The EMT thread is about to start */
14599
14600 /* Nothing to do here for now... */
14601
14602 /// @todo NEWMEDIA don't let mDVDDrive and other children
14603 /// change anything when in the Starting/Restoring state
14604 }
14605 else if ( ( oldMachineState == MachineState_Running
14606 || oldMachineState == MachineState_Paused
14607 || oldMachineState == MachineState_Teleporting
14608 || oldMachineState == MachineState_OnlineSnapshotting
14609 || oldMachineState == MachineState_LiveSnapshotting
14610 || oldMachineState == MachineState_Stuck
14611 || oldMachineState == MachineState_Starting
14612 || oldMachineState == MachineState_Stopping
14613 || oldMachineState == MachineState_Saving
14614 || oldMachineState == MachineState_Restoring
14615 || oldMachineState == MachineState_TeleportingPausedVM
14616 || oldMachineState == MachineState_TeleportingIn
14617 )
14618 && ( aMachineState == MachineState_PoweredOff
14619 || aMachineState == MachineState_Saved
14620 || aMachineState == MachineState_Teleported
14621 || aMachineState == MachineState_Aborted
14622 )
14623 )
14624 {
14625 /* The EMT thread has just stopped, unlock attached media. Note that as
14626 * opposed to locking that is done from Console, we do unlocking here
14627 * because the VM process may have aborted before having a chance to
14628 * properly unlock all media it locked. */
14629
14630 unlockMedia();
14631 }
14632
14633 if (oldMachineState == MachineState_Restoring)
14634 {
14635 if (aMachineState != MachineState_Saved)
14636 {
14637 /*
14638 * delete the saved state file once the machine has finished
14639 * restoring from it (note that Console sets the state from
14640 * Restoring to Saved if the VM couldn't restore successfully,
14641 * to give the user an ability to fix an error and retry --
14642 * we keep the saved state file in this case)
14643 */
14644 deleteSavedState = true;
14645 }
14646 }
14647 else if ( oldMachineState == MachineState_Saved
14648 && ( aMachineState == MachineState_PoweredOff
14649 || aMachineState == MachineState_Aborted
14650 || aMachineState == MachineState_Teleported
14651 )
14652 )
14653 {
14654 /*
14655 * delete the saved state after SessionMachine::ForgetSavedState() is called
14656 * or if the VM process (owning a direct VM session) crashed while the
14657 * VM was Saved
14658 */
14659
14660 /// @todo (dmik)
14661 // Not sure that deleting the saved state file just because of the
14662 // client death before it attempted to restore the VM is a good
14663 // thing. But when it crashes we need to go to the Aborted state
14664 // which cannot have the saved state file associated... The only
14665 // way to fix this is to make the Aborted condition not a VM state
14666 // but a bool flag: i.e., when a crash occurs, set it to true and
14667 // change the state to PoweredOff or Saved depending on the
14668 // saved state presence.
14669
14670 deleteSavedState = true;
14671 mData->mCurrentStateModified = TRUE;
14672 stsFlags |= SaveSTS_CurStateModified;
14673 }
14674
14675 if ( aMachineState == MachineState_Starting
14676 || aMachineState == MachineState_Restoring
14677 || aMachineState == MachineState_TeleportingIn
14678 )
14679 {
14680 /* set the current state modified flag to indicate that the current
14681 * state is no more identical to the state in the
14682 * current snapshot */
14683 if (!mData->mCurrentSnapshot.isNull())
14684 {
14685 mData->mCurrentStateModified = TRUE;
14686 stsFlags |= SaveSTS_CurStateModified;
14687 }
14688 }
14689
14690 if (deleteSavedState)
14691 {
14692 if (mRemoveSavedState)
14693 {
14694 Assert(!mSSData->strStateFilePath.isEmpty());
14695
14696 // it is safe to delete the saved state file if ...
14697 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14698 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14699 // ... none of the snapshots share the saved state file
14700 )
14701 RTFileDelete(mSSData->strStateFilePath.c_str());
14702 }
14703
14704 mSSData->strStateFilePath.setNull();
14705 stsFlags |= SaveSTS_StateFilePath;
14706 }
14707
14708 /* redirect to the underlying peer machine */
14709 mPeer->i_setMachineState(aMachineState);
14710
14711 if ( oldMachineState != MachineState_RestoringSnapshot
14712 && ( aMachineState == MachineState_PoweredOff
14713 || aMachineState == MachineState_Teleported
14714 || aMachineState == MachineState_Aborted
14715 || aMachineState == MachineState_Saved))
14716 {
14717 /* the machine has stopped execution
14718 * (or the saved state file was adopted) */
14719 stsFlags |= SaveSTS_StateTimeStamp;
14720 }
14721
14722 if ( ( oldMachineState == MachineState_PoweredOff
14723 || oldMachineState == MachineState_Aborted
14724 || oldMachineState == MachineState_Teleported
14725 )
14726 && aMachineState == MachineState_Saved)
14727 {
14728 /* the saved state file was adopted */
14729 Assert(!mSSData->strStateFilePath.isEmpty());
14730 stsFlags |= SaveSTS_StateFilePath;
14731 }
14732
14733#ifdef VBOX_WITH_GUEST_PROPS
14734 if ( aMachineState == MachineState_PoweredOff
14735 || aMachineState == MachineState_Aborted
14736 || aMachineState == MachineState_Teleported)
14737 {
14738 /* Make sure any transient guest properties get removed from the
14739 * property store on shutdown. */
14740 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14741
14742 /* remove it from the settings representation */
14743 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14744 for (settings::GuestPropertiesList::iterator
14745 it = llGuestProperties.begin();
14746 it != llGuestProperties.end();
14747 /*nothing*/)
14748 {
14749 const settings::GuestProperty &prop = *it;
14750 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14751 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14752 {
14753 it = llGuestProperties.erase(it);
14754 fNeedsSaving = true;
14755 }
14756 else
14757 {
14758 ++it;
14759 }
14760 }
14761
14762 /* Additionally remove it from the HWData representation. Required to
14763 * keep everything in sync, as this is what the API keeps using. */
14764 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14765 for (HWData::GuestPropertyMap::iterator
14766 it = llHWGuestProperties.begin();
14767 it != llHWGuestProperties.end();
14768 /*nothing*/)
14769 {
14770 uint32_t fFlags = it->second.mFlags;
14771 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14772 {
14773 /* iterator where we need to continue after the erase call
14774 * (C++03 is a fact still, and it doesn't return the iterator
14775 * which would allow continuing) */
14776 HWData::GuestPropertyMap::iterator it2 = it;
14777 ++it2;
14778 llHWGuestProperties.erase(it);
14779 it = it2;
14780 fNeedsSaving = true;
14781 }
14782 else
14783 {
14784 ++it;
14785 }
14786 }
14787
14788 if (fNeedsSaving)
14789 {
14790 mData->mCurrentStateModified = TRUE;
14791 stsFlags |= SaveSTS_CurStateModified;
14792 }
14793 }
14794#endif /* VBOX_WITH_GUEST_PROPS */
14795
14796 rc = i_saveStateSettings(stsFlags);
14797
14798 if ( ( oldMachineState != MachineState_PoweredOff
14799 && oldMachineState != MachineState_Aborted
14800 && oldMachineState != MachineState_Teleported
14801 )
14802 && ( aMachineState == MachineState_PoweredOff
14803 || aMachineState == MachineState_Aborted
14804 || aMachineState == MachineState_Teleported
14805 )
14806 )
14807 {
14808 /* we've been shut down for any reason */
14809 /* no special action so far */
14810 }
14811
14812 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14813 LogFlowThisFuncLeave();
14814 return rc;
14815}
14816
14817/**
14818 * Sends the current machine state value to the VM process.
14819 *
14820 * @note Locks this object for reading, then calls a client process.
14821 */
14822HRESULT SessionMachine::i_updateMachineStateOnClient()
14823{
14824 AutoCaller autoCaller(this);
14825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14826
14827 ComPtr<IInternalSessionControl> directControl;
14828 {
14829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14830 AssertReturn(!!mData, E_FAIL);
14831 if (mData->mSession.mLockType == LockType_VM)
14832 directControl = mData->mSession.mDirectControl;
14833
14834 /* directControl may be already set to NULL here in #OnSessionEnd()
14835 * called too early by the direct session process while there is still
14836 * some operation (like deleting the snapshot) in progress. The client
14837 * process in this case is waiting inside Session::close() for the
14838 * "end session" process object to complete, while #uninit() called by
14839 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14840 * operation to complete. For now, we accept this inconsistent behavior
14841 * and simply do nothing here. */
14842
14843 if (mData->mSession.mState == SessionState_Unlocking)
14844 return S_OK;
14845 }
14846
14847 /* ignore notifications sent after #OnSessionEnd() is called */
14848 if (!directControl)
14849 return S_OK;
14850
14851 return directControl->UpdateMachineState(mData->mMachineState);
14852}
14853
14854
14855/*static*/
14856HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14857{
14858 va_list args;
14859 va_start(args, pcszMsg);
14860 HRESULT rc = setErrorInternal(aResultCode,
14861 getStaticClassIID(),
14862 getStaticComponentName(),
14863 Utf8Str(pcszMsg, args),
14864 false /* aWarning */,
14865 true /* aLogIt */);
14866 va_end(args);
14867 return rc;
14868}
14869
14870
14871HRESULT Machine::updateState(MachineState_T aState)
14872{
14873 NOREF(aState);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14878{
14879 NOREF(aProgress);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::endPowerUp(LONG aResult)
14884{
14885 NOREF(aResult);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14890{
14891 NOREF(aProgress);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::endPoweringDown(LONG aResult,
14896 const com::Utf8Str &aErrMsg)
14897{
14898 NOREF(aResult);
14899 NOREF(aErrMsg);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14904 BOOL *aMatched,
14905 ULONG *aMaskedInterfaces)
14906{
14907 NOREF(aDevice);
14908 NOREF(aMatched);
14909 NOREF(aMaskedInterfaces);
14910 ReturnComNotImplemented();
14911
14912}
14913
14914HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14915{
14916 NOREF(aId); NOREF(aCaptureFilename);
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14921 BOOL aDone)
14922{
14923 NOREF(aId);
14924 NOREF(aDone);
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::autoCaptureUSBDevices()
14929{
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14934{
14935 NOREF(aDone);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14940 ComPtr<IProgress> &aProgress)
14941{
14942 NOREF(aSession);
14943 NOREF(aProgress);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::finishOnlineMergeMedium()
14948{
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14953 std::vector<com::Utf8Str> &aValues,
14954 std::vector<LONG64> &aTimestamps,
14955 std::vector<com::Utf8Str> &aFlags)
14956{
14957 NOREF(aNames);
14958 NOREF(aValues);
14959 NOREF(aTimestamps);
14960 NOREF(aFlags);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14965 const com::Utf8Str &aValue,
14966 LONG64 aTimestamp,
14967 const com::Utf8Str &aFlags)
14968{
14969 NOREF(aName);
14970 NOREF(aValue);
14971 NOREF(aTimestamp);
14972 NOREF(aFlags);
14973 ReturnComNotImplemented();
14974}
14975
14976HRESULT Machine::lockMedia()
14977{
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::unlockMedia()
14982{
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14987 ComPtr<IMediumAttachment> &aNewAttachment)
14988{
14989 NOREF(aAttachment);
14990 NOREF(aNewAttachment);
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14995 ULONG aCpuUser,
14996 ULONG aCpuKernel,
14997 ULONG aCpuIdle,
14998 ULONG aMemTotal,
14999 ULONG aMemFree,
15000 ULONG aMemBalloon,
15001 ULONG aMemShared,
15002 ULONG aMemCache,
15003 ULONG aPagedTotal,
15004 ULONG aMemAllocTotal,
15005 ULONG aMemFreeTotal,
15006 ULONG aMemBalloonTotal,
15007 ULONG aMemSharedTotal,
15008 ULONG aVmNetRx,
15009 ULONG aVmNetTx)
15010{
15011 NOREF(aValidStats);
15012 NOREF(aCpuUser);
15013 NOREF(aCpuKernel);
15014 NOREF(aCpuIdle);
15015 NOREF(aMemTotal);
15016 NOREF(aMemFree);
15017 NOREF(aMemBalloon);
15018 NOREF(aMemShared);
15019 NOREF(aMemCache);
15020 NOREF(aPagedTotal);
15021 NOREF(aMemAllocTotal);
15022 NOREF(aMemFreeTotal);
15023 NOREF(aMemBalloonTotal);
15024 NOREF(aMemSharedTotal);
15025 NOREF(aVmNetRx);
15026 NOREF(aVmNetTx);
15027 ReturnComNotImplemented();
15028}
15029
15030HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15031 com::Utf8Str &aResult)
15032{
15033 NOREF(aAuthParams);
15034 NOREF(aResult);
15035 ReturnComNotImplemented();
15036}
15037
15038HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15039{
15040 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15041
15042 AutoCaller autoCaller(this);
15043 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15044
15045 HRESULT rc = S_OK;
15046
15047 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15048 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15049 rc = getUSBDeviceFilters(usbDeviceFilters);
15050 if (FAILED(rc)) return rc;
15051
15052 NOREF(aFlags);
15053 com::Utf8Str osTypeId;
15054 ComObjPtr<GuestOSType> osType = NULL;
15055
15056 /* Get the guest os type as a string from the VB. */
15057 rc = getOSTypeId(osTypeId);
15058 if (FAILED(rc)) return rc;
15059
15060 /* Get the os type obj that coresponds, can be used to get
15061 * the defaults for this guest OS. */
15062 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15063 if (FAILED(rc)) return rc;
15064
15065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15066
15067 /* Let the OS type select 64-bit ness. */
15068 mHWData->mLongMode = osType->i_is64Bit()
15069 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15070
15071 /* Apply network adapters defaults */
15072 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15073 mNetworkAdapters[slot]->i_applyDefaults(osType);
15074
15075 /* Apply serial port defaults */
15076 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15077 mSerialPorts[slot]->i_applyDefaults(osType);
15078
15079 /* Apply parallel port defaults - not OS dependent*/
15080 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15081 mParallelPorts[slot]->i_applyDefaults();
15082
15083
15084 /* Let the OS type enable the X2APIC */
15085 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15086
15087 /* This one covers IOAPICEnabled. */
15088 mBIOSSettings->i_applyDefaults(osType);
15089
15090 /* Initialize default record settings. */
15091 mRecordingSettings->i_applyDefaults();
15092
15093 /* Initialize default BIOS settings here */
15094 mHWData->mAPIC = osType->i_recommendedIOAPIC();
15095 mHWData->mHWVirtExEnabled = osType->i_recommendedVirtEx();
15096
15097 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15098 if (FAILED(rc)) return rc;
15099
15100 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15101 if (FAILED(rc)) return rc;
15102
15103 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15104 if (FAILED(rc)) return rc;
15105
15106 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15107 if (FAILED(rc)) return rc;
15108
15109 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15110 if (FAILED(rc)) return rc;
15111
15112 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15113 if (FAILED(rc)) return rc;
15114
15115 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15116 if (FAILED(rc)) return rc;
15117
15118 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15119 if (FAILED(rc)) return rc;
15120
15121 BOOL mRTCUseUTC;
15122 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15123 if (FAILED(rc)) return rc;
15124
15125 setRTCUseUTC(mRTCUseUTC);
15126 if (FAILED(rc)) return rc;
15127
15128 rc = osType->COMGETTER(RecommendedChipset)(&mHWData->mChipsetType);
15129 if (FAILED(rc)) return rc;
15130
15131 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15132 if (FAILED(rc)) return rc;
15133
15134 /* Audio stuff. */
15135 AudioCodecType_T audioCodec;
15136 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15137 if (FAILED(rc)) return rc;
15138
15139 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15140 if (FAILED(rc)) return rc;
15141
15142 AudioControllerType_T audioController;
15143 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15144 if (FAILED(rc)) return rc;
15145
15146 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15147 if (FAILED(rc)) return rc;
15148
15149 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15150 if (FAILED(rc)) return rc;
15151
15152 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15153 if (FAILED(rc)) return rc;
15154
15155 /* Storage Controllers */
15156 StorageControllerType_T hdStorageControllerType;
15157 StorageBus_T hdStorageBusType;
15158 StorageControllerType_T dvdStorageControllerType;
15159 StorageBus_T dvdStorageBusType;
15160 BOOL recommendedFloppy;
15161 ComPtr<IStorageController> floppyController;
15162 ComPtr<IStorageController> hdController;
15163 ComPtr<IStorageController> dvdController;
15164 Utf8Str strFloppyName, strDVDName, strHDName;
15165
15166 /* GUI auto generates these - not accesible here - so hardware, at least for now. */
15167 strFloppyName = Bstr("Floppy 1").raw();
15168 strDVDName = Bstr("DVD 1").raw();
15169 strHDName = Bstr("HDD 1").raw();
15170
15171 /* Floppy recommended? add one. */
15172 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15173 if (FAILED(rc)) return rc;
15174 if (recommendedFloppy)
15175 {
15176 rc = addStorageController(strFloppyName,
15177 StorageBus_Floppy,
15178 floppyController);
15179 if (FAILED(rc)) return rc;
15180 }
15181
15182 /* Setup one DVD storage controller. */
15183 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15184 if (FAILED(rc)) return rc;
15185
15186 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15187 if (FAILED(rc)) return rc;
15188
15189 rc = addStorageController(strDVDName,
15190 dvdStorageBusType,
15191 dvdController);
15192 if (FAILED(rc)) return rc;
15193
15194 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15195 if (FAILED(rc)) return rc;
15196
15197 /* Setup one HDD storage controller. */
15198 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15199 if (FAILED(rc)) return rc;
15200
15201 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15202 if (FAILED(rc)) return rc;
15203
15204 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15205 {
15206 rc = addStorageController(strHDName,
15207 hdStorageBusType,
15208 hdController);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15212 if (FAILED(rc)) return rc;
15213 }
15214 else
15215 {
15216 /* The HD controller is the same as DVD: */
15217 hdController = dvdController;
15218 strHDName = Bstr("DVD 1").raw();
15219 }
15220
15221 /* Limit the AHCI port count if it's used because windows has trouble with
15222 * too many ports and other guest (OS X in particular) may take extra long
15223 * boot: */
15224
15225 // pParent = static_cast<Medium*>(aP)
15226 IStorageController *temp = hdController;
15227 ComObjPtr<StorageController> storageController;
15228 storageController = static_cast<StorageController *>(temp);
15229
15230 // tempHDController = aHDController;
15231 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15232 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15233 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15234 storageController->COMSETTER(PortCount)(1);
15235
15236 /* USB stuff */
15237
15238 bool ohciEnabled = false;
15239
15240 ComPtr<IUSBController> usbController;
15241 BOOL recommendedUSB3;
15242 BOOL recommendedUSB;
15243 BOOL usbProxyAvailable;
15244
15245 getUSBProxyAvailable(&usbProxyAvailable);
15246 if (FAILED(rc)) return rc;
15247
15248 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15249 if (FAILED(rc)) return rc;
15250 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15251 if (FAILED(rc)) return rc;
15252
15253 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15254 {
15255#ifdef VBOX_WITH_EXTPACK
15256 /* USB 3.0 is only available if the proper ExtPack is installed. */
15257 ExtPackManager *aManager = mParent->i_getExtPackManager();
15258 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15259 {
15260 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15261 if (FAILED(rc)) return rc;
15262
15263 /* xHci includes OHCI */
15264 ohciEnabled = true;
15265 }
15266#endif
15267 }
15268 if ( !ohciEnabled
15269 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15270 {
15271 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15272 if (FAILED(rc)) return rc;
15273 ohciEnabled = true;
15274
15275#ifdef VBOX_WITH_EXTPACK
15276 /* USB 2.0 is only available if the proper ExtPack is installed.
15277 * Note. Configuring EHCI here and providing messages about
15278 * the missing extpack isn't exactly clean, but it is a
15279 * necessary evil to patch over legacy compatability issues
15280 * introduced by the new distribution model. */
15281 ExtPackManager *manager = mParent->i_getExtPackManager();
15282 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15283 {
15284 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15285 if (FAILED(rc)) return rc;
15286 }
15287#endif
15288 }
15289
15290 /* Set recommended human interface device types: */
15291 BOOL recommendedUSBHID;
15292 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15293 if (FAILED(rc)) return rc;
15294
15295 if (recommendedUSBHID)
15296 {
15297 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15298 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15299 if (!ohciEnabled && !usbDeviceFilters.isNull())
15300 {
15301 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15302 if (FAILED(rc)) return rc;
15303 }
15304 }
15305
15306 BOOL recommendedUSBTablet;
15307 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15308 if (FAILED(rc)) return rc;
15309
15310 if (recommendedUSBTablet)
15311 {
15312 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15313 if (!ohciEnabled && !usbDeviceFilters.isNull())
15314 {
15315 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15316 if (FAILED(rc)) return rc;
15317 }
15318 }
15319 return S_OK;
15320}
15321
15322/* This isn't handled entirely by the wrapper generator yet. */
15323#ifdef VBOX_WITH_XPCOM
15324NS_DECL_CLASSINFO(SessionMachine)
15325NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15326
15327NS_DECL_CLASSINFO(SnapshotMachine)
15328NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15329#endif
Note: See TracBrowser for help on using the repository browser.

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