VirtualBox

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

Last change on this file since 87241 was 87241, checked in by vboxsync, 4 years ago

AMD IOMMU: bugref:9654 Main/API: AMD IOMMU support.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.1 KB
Line 
1/* $Id: MachineImpl.cpp 87241 2021-01-13 15:56:05Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#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#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
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 mHWVirtExVirtVmsaveVmload = true;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mClipboardFileTransfersEnabled = FALSE;
218
219 mDnDMode = DnDMode_Disabled;
220
221 mFirmwareType = FirmwareType_BIOS;
222 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
223 mPointingHIDType = PointingHIDType_PS2Mouse;
224 mChipsetType = ChipsetType_PIIX3;
225 mIommuType = IommuType_None;
226 mParavirtProvider = ParavirtProvider_Default;
227 mEmulatedUSBCardReaderEnabled = FALSE;
228
229 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
230 mCPUAttached[i] = false;
231
232 mIOCacheEnabled = true;
233 mIOCacheSize = 5; /* 5MB */
234}
235
236Machine::HWData::~HWData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param strOsType OS Type string (stored as is if aOsType is NULL).
284 * @param aOsType OS Type of this machine or NULL.
285 * @param aId UUID for the new machine.
286 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
287 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
288 * scheme (includes the UUID).
289 *
290 * @return Success indicator. if not S_OK, the machine object is invalid
291 */
292HRESULT Machine::init(VirtualBox *aParent,
293 const Utf8Str &strConfigFile,
294 const Utf8Str &strName,
295 const StringsList &llGroups,
296 const Utf8Str &strOsType,
297 GuestOSType *aOsType,
298 const Guid &aId,
299 bool fForceOverwrite,
300 bool fDirectoryIncludesUUID)
301{
302 LogFlowThisFuncEnter();
303 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
304
305 /* Enclose the state transition NotReady->InInit->Ready */
306 AutoInitSpan autoInitSpan(this);
307 AssertReturn(autoInitSpan.isOk(), E_FAIL);
308
309 HRESULT rc = initImpl(aParent, strConfigFile);
310 if (FAILED(rc)) return rc;
311
312 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
313 if (FAILED(rc)) return rc;
314
315 if (SUCCEEDED(rc))
316 {
317 // create an empty machine config
318 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
319
320 rc = initDataAndChildObjects();
321 }
322
323 if (SUCCEEDED(rc))
324 {
325 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
326 mData->mAccessible = TRUE;
327
328 unconst(mData->mUuid) = aId;
329
330 mUserData->s.strName = strName;
331
332 if (llGroups.size())
333 mUserData->s.llGroups = llGroups;
334
335 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
336 // the "name sync" flag determines whether the machine directory gets renamed along
337 // with the machine file; say so if the settings file name is the same as the
338 // settings file parent directory (machine directory)
339 mUserData->s.fNameSync = i_isInOwnDir();
340
341 // initialize the default snapshots folder
342 rc = COMSETTER(SnapshotFolder)(NULL);
343 AssertComRC(rc);
344
345 if (aOsType)
346 {
347 /* Store OS type */
348 mUserData->s.strOsType = aOsType->i_id();
349
350 /* Let the OS type select 64-bit ness. */
351 mHWData->mLongMode = aOsType->i_is64Bit()
352 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
353
354 /* Let the OS type enable the X2APIC */
355 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
356 }
357 else if (!strOsType.isEmpty())
358 {
359 /* Store OS type */
360 mUserData->s.strOsType = strOsType;
361
362 /* No guest OS type object. Pick some plausible defaults which the
363 * host can handle. There's no way to know or validate anything. */
364 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
365 mHWData->mX2APIC = false;
366 }
367
368 /* Apply BIOS defaults. */
369 mBIOSSettings->i_applyDefaults(aOsType);
370
371 /* Apply record defaults. */
372 mRecordingSettings->i_applyDefaults();
373
374 /* Apply network adapters defaults */
375 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
376 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
377
378 /* Apply serial port defaults */
379 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
380 mSerialPorts[slot]->i_applyDefaults(aOsType);
381
382 /* Apply parallel port defaults */
383 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
384 mParallelPorts[slot]->i_applyDefaults();
385
386 /* At this point the changing of the current state modification
387 * flag is allowed. */
388 i_allowStateModification();
389
390 /* commit all changes made during the initialization */
391 i_commit();
392 }
393
394 /* Confirm a successful initialization when it's the case */
395 if (SUCCEEDED(rc))
396 {
397 if (mData->mAccessible)
398 autoInitSpan.setSucceeded();
399 else
400 autoInitSpan.setLimited();
401 }
402
403 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
404 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
405 mData->mRegistered,
406 mData->mAccessible,
407 rc));
408
409 LogFlowThisFuncLeave();
410
411 return rc;
412}
413
414/**
415 * Initializes a new instance with data from machine XML (formerly Init_Registered).
416 * Gets called in two modes:
417 *
418 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
419 * UUID is specified and we mark the machine as "registered";
420 *
421 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
422 * and the machine remains unregistered until RegisterMachine() is called.
423 *
424 * @param aParent Associated parent object
425 * @param strConfigFile Local file system path to the VM settings file (can
426 * be relative to the VirtualBox config directory).
427 * @param aId UUID of the machine or NULL (see above).
428 *
429 * @return Success indicator. if not S_OK, the machine object is invalid
430 */
431HRESULT Machine::initFromSettings(VirtualBox *aParent,
432 const Utf8Str &strConfigFile,
433 const Guid *aId)
434{
435 LogFlowThisFuncEnter();
436 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
437
438 /* Enclose the state transition NotReady->InInit->Ready */
439 AutoInitSpan autoInitSpan(this);
440 AssertReturn(autoInitSpan.isOk(), E_FAIL);
441
442 HRESULT rc = initImpl(aParent, strConfigFile);
443 if (FAILED(rc)) return rc;
444
445 if (aId)
446 {
447 // loading a registered VM:
448 unconst(mData->mUuid) = *aId;
449 mData->mRegistered = TRUE;
450 // now load the settings from XML:
451 rc = i_registeredInit();
452 // this calls initDataAndChildObjects() and loadSettings()
453 }
454 else
455 {
456 // opening an unregistered VM (VirtualBox::OpenMachine()):
457 rc = initDataAndChildObjects();
458
459 if (SUCCEEDED(rc))
460 {
461 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
462 mData->mAccessible = TRUE;
463
464 try
465 {
466 // load and parse machine XML; this will throw on XML or logic errors
467 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
468
469 // reject VM UUID duplicates, they can happen if someone
470 // tries to register an already known VM config again
471 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
472 true /* fPermitInaccessible */,
473 false /* aDoSetError */,
474 NULL) != VBOX_E_OBJECT_NOT_FOUND)
475 {
476 throw setError(E_FAIL,
477 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
478 mData->m_strConfigFile.c_str());
479 }
480
481 // use UUID from machine config
482 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
483
484 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
485 NULL /* puuidRegistry */);
486 if (FAILED(rc)) throw rc;
487
488 /* At this point the changing of the current state modification
489 * flag is allowed. */
490 i_allowStateModification();
491
492 i_commit();
493 }
494 catch (HRESULT err)
495 {
496 /* we assume that error info is set by the thrower */
497 rc = err;
498 }
499 catch (...)
500 {
501 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
502 }
503 }
504 }
505
506 /* Confirm a successful initialization when it's the case */
507 if (SUCCEEDED(rc))
508 {
509 if (mData->mAccessible)
510 autoInitSpan.setSucceeded();
511 else
512 {
513 autoInitSpan.setLimited();
514
515 // uninit media from this machine's media registry, or else
516 // reloading the settings will fail
517 mParent->i_unregisterMachineMedia(i_getId());
518 }
519 }
520
521 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
522 "rc=%08X\n",
523 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
524 mData->mRegistered, mData->mAccessible, rc));
525
526 LogFlowThisFuncLeave();
527
528 return rc;
529}
530
531/**
532 * Initializes a new instance from a machine config that is already in memory
533 * (import OVF case). Since we are importing, the UUID in the machine
534 * config is ignored and we always generate a fresh one.
535 *
536 * @param aParent Associated parent object.
537 * @param strName Name for the new machine; this overrides what is specified in config.
538 * @param strSettingsFilename File name of .vbox file.
539 * @param config Machine configuration loaded and parsed from XML.
540 *
541 * @return Success indicator. if not S_OK, the machine object is invalid
542 */
543HRESULT Machine::init(VirtualBox *aParent,
544 const Utf8Str &strName,
545 const Utf8Str &strSettingsFilename,
546 const settings::MachineConfigFile &config)
547{
548 LogFlowThisFuncEnter();
549
550 /* Enclose the state transition NotReady->InInit->Ready */
551 AutoInitSpan autoInitSpan(this);
552 AssertReturn(autoInitSpan.isOk(), E_FAIL);
553
554 HRESULT rc = initImpl(aParent, strSettingsFilename);
555 if (FAILED(rc)) return rc;
556
557 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
558 if (FAILED(rc)) return rc;
559
560 rc = initDataAndChildObjects();
561
562 if (SUCCEEDED(rc))
563 {
564 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
565 mData->mAccessible = TRUE;
566
567 // create empty machine config for instance data
568 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
569
570 // generate fresh UUID, ignore machine config
571 unconst(mData->mUuid).create();
572
573 rc = i_loadMachineDataFromSettings(config,
574 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
575
576 // override VM name as well, it may be different
577 mUserData->s.strName = strName;
578
579 if (SUCCEEDED(rc))
580 {
581 /* At this point the changing of the current state modification
582 * flag is allowed. */
583 i_allowStateModification();
584
585 /* commit all changes made during the initialization */
586 i_commit();
587 }
588 }
589
590 /* Confirm a successful initialization when it's the case */
591 if (SUCCEEDED(rc))
592 {
593 if (mData->mAccessible)
594 autoInitSpan.setSucceeded();
595 else
596 {
597 /* Ignore all errors from unregistering, they would destroy
598- * the more interesting error information we already have,
599- * pinpointing the issue with the VM config. */
600 ErrorInfoKeeper eik;
601
602 autoInitSpan.setLimited();
603
604 // uninit media from this machine's media registry, or else
605 // reloading the settings will fail
606 mParent->i_unregisterMachineMedia(i_getId());
607 }
608 }
609
610 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
611 "rc=%08X\n",
612 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
613 mData->mRegistered, mData->mAccessible, rc));
614
615 LogFlowThisFuncLeave();
616
617 return rc;
618}
619
620/**
621 * Shared code between the various init() implementations.
622 * @param aParent The VirtualBox object.
623 * @param strConfigFile Settings file.
624 * @return
625 */
626HRESULT Machine::initImpl(VirtualBox *aParent,
627 const Utf8Str &strConfigFile)
628{
629 LogFlowThisFuncEnter();
630
631 AssertReturn(aParent, E_INVALIDARG);
632 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
633
634 HRESULT rc = S_OK;
635
636 /* share the parent weakly */
637 unconst(mParent) = aParent;
638
639 /* allocate the essential machine data structure (the rest will be
640 * allocated later by initDataAndChildObjects() */
641 mData.allocate();
642
643 /* memorize the config file name (as provided) */
644 mData->m_strConfigFile = strConfigFile;
645
646 /* get the full file name */
647 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
648 if (RT_FAILURE(vrc1))
649 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
650 tr("Invalid machine settings file name '%s' (%Rrc)"),
651 strConfigFile.c_str(),
652 vrc1);
653
654 LogFlowThisFuncLeave();
655
656 return rc;
657}
658
659/**
660 * Tries to create a machine settings file in the path stored in the machine
661 * instance data. Used when a new machine is created to fail gracefully if
662 * the settings file could not be written (e.g. because machine dir is read-only).
663 * @return
664 */
665HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
666{
667 HRESULT rc = S_OK;
668
669 // when we create a new machine, we must be able to create the settings file
670 RTFILE f = NIL_RTFILE;
671 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
672 if ( RT_SUCCESS(vrc)
673 || vrc == VERR_SHARING_VIOLATION
674 )
675 {
676 if (RT_SUCCESS(vrc))
677 RTFileClose(f);
678 if (!fForceOverwrite)
679 rc = setError(VBOX_E_FILE_ERROR,
680 tr("Machine settings file '%s' already exists"),
681 mData->m_strConfigFileFull.c_str());
682 else
683 {
684 /* try to delete the config file, as otherwise the creation
685 * of a new settings file will fail. */
686 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
687 if (RT_FAILURE(vrc2))
688 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
689 tr("Could not delete the existing settings file '%s' (%Rrc)"),
690 mData->m_strConfigFileFull.c_str(), vrc2);
691 }
692 }
693 else if ( vrc != VERR_FILE_NOT_FOUND
694 && vrc != VERR_PATH_NOT_FOUND
695 )
696 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
697 tr("Invalid machine settings file name '%s' (%Rrc)"),
698 mData->m_strConfigFileFull.c_str(),
699 vrc);
700 return rc;
701}
702
703/**
704 * Initializes the registered machine by loading the settings file.
705 * This method is separated from #init() in order to make it possible to
706 * retry the operation after VirtualBox startup instead of refusing to
707 * startup the whole VirtualBox server in case if the settings file of some
708 * registered VM is invalid or inaccessible.
709 *
710 * @note Must be always called from this object's write lock
711 * (unless called from #init() that doesn't need any locking).
712 * @note Locks the mUSBController method for writing.
713 * @note Subclasses must not call this method.
714 */
715HRESULT Machine::i_registeredInit()
716{
717 AssertReturn(!i_isSessionMachine(), E_FAIL);
718 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
719 AssertReturn(mData->mUuid.isValid(), E_FAIL);
720 AssertReturn(!mData->mAccessible, E_FAIL);
721
722 HRESULT rc = initDataAndChildObjects();
723
724 if (SUCCEEDED(rc))
725 {
726 /* Temporarily reset the registered flag in order to let setters
727 * potentially called from loadSettings() succeed (isMutable() used in
728 * all setters will return FALSE for a Machine instance if mRegistered
729 * is TRUE). */
730 mData->mRegistered = FALSE;
731
732 try
733 {
734 // load and parse machine XML; this will throw on XML or logic errors
735 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
736
737 if (mData->mUuid != mData->pMachineConfigFile->uuid)
738 throw setError(E_FAIL,
739 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
740 mData->pMachineConfigFile->uuid.raw(),
741 mData->m_strConfigFileFull.c_str(),
742 mData->mUuid.toString().c_str(),
743 mParent->i_settingsFilePath().c_str());
744
745 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
746 NULL /* const Guid *puuidRegistry */);
747 if (FAILED(rc)) throw rc;
748 }
749 catch (HRESULT err)
750 {
751 /* we assume that error info is set by the thrower */
752 rc = err;
753 }
754 catch (...)
755 {
756 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
757 }
758
759 /* Restore the registered flag (even on failure) */
760 mData->mRegistered = TRUE;
761 }
762
763 if (SUCCEEDED(rc))
764 {
765 /* Set mAccessible to TRUE only if we successfully locked and loaded
766 * the settings file */
767 mData->mAccessible = TRUE;
768
769 /* commit all changes made during loading the settings file */
770 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
771 /// @todo r=klaus for some reason the settings loading logic backs up
772 // the settings, and therefore a commit is needed. Should probably be changed.
773 }
774 else
775 {
776 /* If the machine is registered, then, instead of returning a
777 * failure, we mark it as inaccessible and set the result to
778 * success to give it a try later */
779
780 /* fetch the current error info */
781 mData->mAccessError = com::ErrorInfo();
782 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
783
784 /* rollback all changes */
785 i_rollback(false /* aNotify */);
786
787 // uninit media from this machine's media registry, or else
788 // reloading the settings will fail
789 mParent->i_unregisterMachineMedia(i_getId());
790
791 /* uninitialize the common part to make sure all data is reset to
792 * default (null) values */
793 uninitDataAndChildObjects();
794
795 rc = S_OK;
796 }
797
798 return rc;
799}
800
801/**
802 * Uninitializes the instance.
803 * Called either from FinalRelease() or by the parent when it gets destroyed.
804 *
805 * @note The caller of this method must make sure that this object
806 * a) doesn't have active callers on the current thread and b) is not locked
807 * by the current thread; otherwise uninit() will hang either a) due to
808 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
809 * a dead-lock caused by this thread waiting for all callers on the other
810 * threads are done but preventing them from doing so by holding a lock.
811 */
812void Machine::uninit()
813{
814 LogFlowThisFuncEnter();
815
816 Assert(!isWriteLockOnCurrentThread());
817
818 Assert(!uRegistryNeedsSaving);
819 if (uRegistryNeedsSaving)
820 {
821 AutoCaller autoCaller(this);
822 if (SUCCEEDED(autoCaller.rc()))
823 {
824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
825 i_saveSettings(NULL, Machine::SaveS_Force);
826 }
827 }
828
829 /* Enclose the state transition Ready->InUninit->NotReady */
830 AutoUninitSpan autoUninitSpan(this);
831 if (autoUninitSpan.uninitDone())
832 return;
833
834 Assert(!i_isSnapshotMachine());
835 Assert(!i_isSessionMachine());
836 Assert(!!mData);
837
838 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
839 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
840
841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
842
843 if (!mData->mSession.mMachine.isNull())
844 {
845 /* Theoretically, this can only happen if the VirtualBox server has been
846 * terminated while there were clients running that owned open direct
847 * sessions. Since in this case we are definitely called by
848 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
849 * won't happen on the client watcher thread (because it has a
850 * VirtualBox caller for the duration of the
851 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
852 * cannot happen until the VirtualBox caller is released). This is
853 * important, because SessionMachine::uninit() cannot correctly operate
854 * after we return from this method (it expects the Machine instance is
855 * still valid). We'll call it ourselves below.
856 */
857 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
858 (SessionMachine*)mData->mSession.mMachine));
859
860 if (Global::IsOnlineOrTransient(mData->mMachineState))
861 {
862 Log1WarningThisFunc(("Setting state to Aborted!\n"));
863 /* set machine state using SessionMachine reimplementation */
864 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
865 }
866
867 /*
868 * Uninitialize SessionMachine using public uninit() to indicate
869 * an unexpected uninitialization.
870 */
871 mData->mSession.mMachine->uninit();
872 /* SessionMachine::uninit() must set mSession.mMachine to null */
873 Assert(mData->mSession.mMachine.isNull());
874 }
875
876 // uninit media from this machine's media registry, if they're still there
877 Guid uuidMachine(i_getId());
878
879 /* the lock is no more necessary (SessionMachine is uninitialized) */
880 alock.release();
881
882 /* XXX This will fail with
883 * "cannot be closed because it is still attached to 1 virtual machines"
884 * because at this point we did not call uninitDataAndChildObjects() yet
885 * and therefore also removeBackReference() for all these mediums was not called! */
886
887 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
888 mParent->i_unregisterMachineMedia(uuidMachine);
889
890 // has machine been modified?
891 if (mData->flModifications)
892 {
893 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
894 i_rollback(false /* aNotify */);
895 }
896
897 if (mData->mAccessible)
898 uninitDataAndChildObjects();
899
900 /* free the essential data structure last */
901 mData.free();
902
903 LogFlowThisFuncLeave();
904}
905
906// Wrapped IMachine properties
907/////////////////////////////////////////////////////////////////////////////
908HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
909{
910 /* mParent is constant during life time, no need to lock */
911 ComObjPtr<VirtualBox> pVirtualBox(mParent);
912 aParent = pVirtualBox;
913
914 return S_OK;
915}
916
917
918HRESULT Machine::getAccessible(BOOL *aAccessible)
919{
920 /* In some cases (medium registry related), it is necessary to be able to
921 * go through the list of all machines. Happens when an inaccessible VM
922 * has a sensible medium registry. */
923 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
924 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
925
926 HRESULT rc = S_OK;
927
928 if (!mData->mAccessible)
929 {
930 /* try to initialize the VM once more if not accessible */
931
932 AutoReinitSpan autoReinitSpan(this);
933 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
934
935#ifdef DEBUG
936 LogFlowThisFunc(("Dumping media backreferences\n"));
937 mParent->i_dumpAllBackRefs();
938#endif
939
940 if (mData->pMachineConfigFile)
941 {
942 // reset the XML file to force loadSettings() (called from i_registeredInit())
943 // to parse it again; the file might have changed
944 delete mData->pMachineConfigFile;
945 mData->pMachineConfigFile = NULL;
946 }
947
948 rc = i_registeredInit();
949
950 if (SUCCEEDED(rc) && mData->mAccessible)
951 {
952 autoReinitSpan.setSucceeded();
953
954 /* make sure interesting parties will notice the accessibility
955 * state change */
956 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
957 mParent->i_onMachineDataChanged(mData->mUuid);
958 }
959 }
960
961 if (SUCCEEDED(rc))
962 *aAccessible = mData->mAccessible;
963
964 LogFlowThisFuncLeave();
965
966 return rc;
967}
968
969HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
970{
971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
972
973 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
974 {
975 /* return shortly */
976 aAccessError = NULL;
977 return S_OK;
978 }
979
980 HRESULT rc = S_OK;
981
982 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
983 rc = errorInfo.createObject();
984 if (SUCCEEDED(rc))
985 {
986 errorInfo->init(mData->mAccessError.getResultCode(),
987 mData->mAccessError.getInterfaceID().ref(),
988 Utf8Str(mData->mAccessError.getComponent()).c_str(),
989 Utf8Str(mData->mAccessError.getText()));
990 aAccessError = errorInfo;
991 }
992
993 return rc;
994}
995
996HRESULT Machine::getName(com::Utf8Str &aName)
997{
998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
999
1000 aName = mUserData->s.strName;
1001
1002 return S_OK;
1003}
1004
1005HRESULT Machine::setName(const com::Utf8Str &aName)
1006{
1007 // prohibit setting a UUID only as the machine name, or else it can
1008 // never be found by findMachine()
1009 Guid test(aName);
1010
1011 if (test.isValid())
1012 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1013
1014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1015
1016 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1017 if (FAILED(rc)) return rc;
1018
1019 i_setModified(IsModified_MachineData);
1020 mUserData.backup();
1021 mUserData->s.strName = aName;
1022
1023 return S_OK;
1024}
1025
1026HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1027{
1028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1029
1030 aDescription = mUserData->s.strDescription;
1031
1032 return S_OK;
1033}
1034
1035HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1036{
1037 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1038
1039 // this can be done in principle in any state as it doesn't affect the VM
1040 // significantly, but play safe by not messing around while complex
1041 // activities are going on
1042 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1043 if (FAILED(rc)) return rc;
1044
1045 i_setModified(IsModified_MachineData);
1046 mUserData.backup();
1047 mUserData->s.strDescription = aDescription;
1048
1049 return S_OK;
1050}
1051
1052HRESULT Machine::getId(com::Guid &aId)
1053{
1054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1055
1056 aId = mData->mUuid;
1057
1058 return S_OK;
1059}
1060
1061HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1062{
1063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1064 aGroups.resize(mUserData->s.llGroups.size());
1065 size_t i = 0;
1066 for (StringsList::const_iterator
1067 it = mUserData->s.llGroups.begin();
1068 it != mUserData->s.llGroups.end();
1069 ++it, ++i)
1070 aGroups[i] = (*it);
1071
1072 return S_OK;
1073}
1074
1075HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1076{
1077 StringsList llGroups;
1078 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1079 if (FAILED(rc))
1080 return rc;
1081
1082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1083
1084 rc = i_checkStateDependency(MutableOrSavedStateDep);
1085 if (FAILED(rc)) return rc;
1086
1087 i_setModified(IsModified_MachineData);
1088 mUserData.backup();
1089 mUserData->s.llGroups = llGroups;
1090
1091 return S_OK;
1092}
1093
1094HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1095{
1096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1097
1098 aOSTypeId = mUserData->s.strOsType;
1099
1100 return S_OK;
1101}
1102
1103HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1104{
1105 /* look up the object by Id to check it is valid */
1106 ComObjPtr<GuestOSType> pGuestOSType;
1107 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1108
1109 /* when setting, always use the "etalon" value for consistency -- lookup
1110 * by ID is case-insensitive and the input value may have different case */
1111 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1112
1113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1114
1115 HRESULT rc = i_checkStateDependency(MutableStateDep);
1116 if (FAILED(rc)) return rc;
1117
1118 i_setModified(IsModified_MachineData);
1119 mUserData.backup();
1120 mUserData->s.strOsType = osTypeId;
1121
1122 return S_OK;
1123}
1124
1125HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1126{
1127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1128
1129 *aFirmwareType = mHWData->mFirmwareType;
1130
1131 return S_OK;
1132}
1133
1134HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1135{
1136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1137
1138 HRESULT rc = i_checkStateDependency(MutableStateDep);
1139 if (FAILED(rc)) return rc;
1140
1141 i_setModified(IsModified_MachineData);
1142 mHWData.backup();
1143 mHWData->mFirmwareType = aFirmwareType;
1144 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1145 alock.release();
1146
1147 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aPointingHIDType = mHWData->mPointingHIDType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 i_setModified(IsModified_MachineData);
1192 mHWData.backup();
1193 mHWData->mPointingHIDType = aPointingHIDType;
1194
1195 return S_OK;
1196}
1197
1198HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1199{
1200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1201
1202 *aChipsetType = mHWData->mChipsetType;
1203
1204 return S_OK;
1205}
1206
1207HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1208{
1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1210
1211 HRESULT rc = i_checkStateDependency(MutableStateDep);
1212 if (FAILED(rc)) return rc;
1213
1214 if (aChipsetType != mHWData->mChipsetType)
1215 {
1216 i_setModified(IsModified_MachineData);
1217 mHWData.backup();
1218 mHWData->mChipsetType = aChipsetType;
1219
1220 // Resize network adapter array, to be finalized on commit/rollback.
1221 // We must not throw away entries yet, otherwise settings are lost
1222 // without a way to roll back.
1223 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1224 size_t oldCount = mNetworkAdapters.size();
1225 if (newCount > oldCount)
1226 {
1227 mNetworkAdapters.resize(newCount);
1228 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1229 {
1230 unconst(mNetworkAdapters[slot]).createObject();
1231 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1232 }
1233 }
1234 }
1235
1236 return S_OK;
1237}
1238
1239HRESULT Machine::getIommuType(IommuType_T *aIommuType)
1240{
1241 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1242
1243 *aIommuType = mHWData->mIommuType;
1244
1245 return S_OK;
1246}
1247
1248HRESULT Machine::setIommuType(IommuType_T aIommuType)
1249{
1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1251
1252 HRESULT rc = i_checkStateDependency(MutableStateDep);
1253 if (FAILED(rc)) return rc;
1254
1255 if (aIommuType != mHWData->mIommuType)
1256 {
1257 i_setModified(IsModified_MachineData);
1258 mHWData.backup();
1259 mHWData->mIommuType = aIommuType;
1260 }
1261
1262 return S_OK;
1263}
1264
1265HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1266{
1267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1268
1269 aParavirtDebug = mHWData->mParavirtDebug;
1270 return S_OK;
1271}
1272
1273HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1274{
1275 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1276
1277 HRESULT rc = i_checkStateDependency(MutableStateDep);
1278 if (FAILED(rc)) return rc;
1279
1280 /** @todo Parse/validate options? */
1281 if (aParavirtDebug != mHWData->mParavirtDebug)
1282 {
1283 i_setModified(IsModified_MachineData);
1284 mHWData.backup();
1285 mHWData->mParavirtDebug = aParavirtDebug;
1286 }
1287
1288 return S_OK;
1289}
1290
1291HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1292{
1293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1294
1295 *aParavirtProvider = mHWData->mParavirtProvider;
1296
1297 return S_OK;
1298}
1299
1300HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1301{
1302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1303
1304 HRESULT rc = i_checkStateDependency(MutableStateDep);
1305 if (FAILED(rc)) return rc;
1306
1307 if (aParavirtProvider != mHWData->mParavirtProvider)
1308 {
1309 i_setModified(IsModified_MachineData);
1310 mHWData.backup();
1311 mHWData->mParavirtProvider = aParavirtProvider;
1312 }
1313
1314 return S_OK;
1315}
1316
1317HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1318{
1319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1320
1321 *aParavirtProvider = mHWData->mParavirtProvider;
1322 switch (mHWData->mParavirtProvider)
1323 {
1324 case ParavirtProvider_None:
1325 case ParavirtProvider_HyperV:
1326 case ParavirtProvider_KVM:
1327 case ParavirtProvider_Minimal:
1328 break;
1329
1330 /* Resolve dynamic provider types to the effective types. */
1331 default:
1332 {
1333 ComObjPtr<GuestOSType> pGuestOSType;
1334 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1335 pGuestOSType);
1336 if (FAILED(hrc2) || pGuestOSType.isNull())
1337 {
1338 *aParavirtProvider = ParavirtProvider_None;
1339 break;
1340 }
1341
1342 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1343 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1344
1345 switch (mHWData->mParavirtProvider)
1346 {
1347 case ParavirtProvider_Legacy:
1348 {
1349 if (fOsXGuest)
1350 *aParavirtProvider = ParavirtProvider_Minimal;
1351 else
1352 *aParavirtProvider = ParavirtProvider_None;
1353 break;
1354 }
1355
1356 case ParavirtProvider_Default:
1357 {
1358 if (fOsXGuest)
1359 *aParavirtProvider = ParavirtProvider_Minimal;
1360 else if ( mUserData->s.strOsType == "Windows10"
1361 || mUserData->s.strOsType == "Windows10_64"
1362 || mUserData->s.strOsType == "Windows81"
1363 || mUserData->s.strOsType == "Windows81_64"
1364 || mUserData->s.strOsType == "Windows8"
1365 || mUserData->s.strOsType == "Windows8_64"
1366 || mUserData->s.strOsType == "Windows7"
1367 || mUserData->s.strOsType == "Windows7_64"
1368 || mUserData->s.strOsType == "WindowsVista"
1369 || mUserData->s.strOsType == "WindowsVista_64"
1370 || mUserData->s.strOsType == "Windows2012"
1371 || mUserData->s.strOsType == "Windows2012_64"
1372 || mUserData->s.strOsType == "Windows2008"
1373 || mUserData->s.strOsType == "Windows2008_64")
1374 {
1375 *aParavirtProvider = ParavirtProvider_HyperV;
1376 }
1377 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1378 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1379 || mUserData->s.strOsType == "Linux"
1380 || mUserData->s.strOsType == "Linux_64"
1381 || mUserData->s.strOsType == "ArchLinux"
1382 || mUserData->s.strOsType == "ArchLinux_64"
1383 || mUserData->s.strOsType == "Debian"
1384 || mUserData->s.strOsType == "Debian_64"
1385 || mUserData->s.strOsType == "Fedora"
1386 || mUserData->s.strOsType == "Fedora_64"
1387 || mUserData->s.strOsType == "Gentoo"
1388 || mUserData->s.strOsType == "Gentoo_64"
1389 || mUserData->s.strOsType == "Mandriva"
1390 || mUserData->s.strOsType == "Mandriva_64"
1391 || mUserData->s.strOsType == "OpenSUSE"
1392 || mUserData->s.strOsType == "OpenSUSE_64"
1393 || mUserData->s.strOsType == "Oracle"
1394 || mUserData->s.strOsType == "Oracle_64"
1395 || mUserData->s.strOsType == "RedHat"
1396 || mUserData->s.strOsType == "RedHat_64"
1397 || mUserData->s.strOsType == "Turbolinux"
1398 || mUserData->s.strOsType == "Turbolinux_64"
1399 || mUserData->s.strOsType == "Ubuntu"
1400 || mUserData->s.strOsType == "Ubuntu_64"
1401 || mUserData->s.strOsType == "Xandros"
1402 || mUserData->s.strOsType == "Xandros_64")
1403 {
1404 *aParavirtProvider = ParavirtProvider_KVM;
1405 }
1406 else
1407 *aParavirtProvider = ParavirtProvider_None;
1408 break;
1409 }
1410
1411 default: AssertFailedBreak(); /* Shut up MSC. */
1412 }
1413 break;
1414 }
1415 }
1416
1417 Assert( *aParavirtProvider == ParavirtProvider_None
1418 || *aParavirtProvider == ParavirtProvider_Minimal
1419 || *aParavirtProvider == ParavirtProvider_HyperV
1420 || *aParavirtProvider == ParavirtProvider_KVM);
1421 return S_OK;
1422}
1423
1424HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1425{
1426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1427
1428 aHardwareVersion = mHWData->mHWVersion;
1429
1430 return S_OK;
1431}
1432
1433HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1434{
1435 /* check known version */
1436 Utf8Str hwVersion = aHardwareVersion;
1437 if ( hwVersion.compare("1") != 0
1438 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1439 return setError(E_INVALIDARG,
1440 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1441
1442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1443
1444 HRESULT rc = i_checkStateDependency(MutableStateDep);
1445 if (FAILED(rc)) return rc;
1446
1447 i_setModified(IsModified_MachineData);
1448 mHWData.backup();
1449 mHWData->mHWVersion = aHardwareVersion;
1450
1451 return S_OK;
1452}
1453
1454HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1455{
1456 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1457
1458 if (!mHWData->mHardwareUUID.isZero())
1459 aHardwareUUID = mHWData->mHardwareUUID;
1460 else
1461 aHardwareUUID = mData->mUuid;
1462
1463 return S_OK;
1464}
1465
1466HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1467{
1468 if (!aHardwareUUID.isValid())
1469 return E_INVALIDARG;
1470
1471 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1472
1473 HRESULT rc = i_checkStateDependency(MutableStateDep);
1474 if (FAILED(rc)) return rc;
1475
1476 i_setModified(IsModified_MachineData);
1477 mHWData.backup();
1478 if (aHardwareUUID == mData->mUuid)
1479 mHWData->mHardwareUUID.clear();
1480 else
1481 mHWData->mHardwareUUID = aHardwareUUID;
1482
1483 return S_OK;
1484}
1485
1486HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1487{
1488 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1489
1490 *aMemorySize = mHWData->mMemorySize;
1491
1492 return S_OK;
1493}
1494
1495HRESULT Machine::setMemorySize(ULONG aMemorySize)
1496{
1497 /* check RAM limits */
1498 if ( aMemorySize < MM_RAM_MIN_IN_MB
1499 || aMemorySize > MM_RAM_MAX_IN_MB
1500 )
1501 return setError(E_INVALIDARG,
1502 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1503 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1504
1505 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1506
1507 HRESULT rc = i_checkStateDependency(MutableStateDep);
1508 if (FAILED(rc)) return rc;
1509
1510 i_setModified(IsModified_MachineData);
1511 mHWData.backup();
1512 mHWData->mMemorySize = aMemorySize;
1513
1514 return S_OK;
1515}
1516
1517HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1518{
1519 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1520
1521 *aCPUCount = mHWData->mCPUCount;
1522
1523 return S_OK;
1524}
1525
1526HRESULT Machine::setCPUCount(ULONG aCPUCount)
1527{
1528 /* check CPU limits */
1529 if ( aCPUCount < SchemaDefs::MinCPUCount
1530 || aCPUCount > SchemaDefs::MaxCPUCount
1531 )
1532 return setError(E_INVALIDARG,
1533 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1534 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1535
1536 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1537
1538 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1539 if (mHWData->mCPUHotPlugEnabled)
1540 {
1541 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1542 {
1543 if (mHWData->mCPUAttached[idx])
1544 return setError(E_INVALIDARG,
1545 tr("There is still a CPU attached to socket %lu."
1546 "Detach the CPU before removing the socket"),
1547 aCPUCount, idx+1);
1548 }
1549 }
1550
1551 HRESULT rc = i_checkStateDependency(MutableStateDep);
1552 if (FAILED(rc)) return rc;
1553
1554 i_setModified(IsModified_MachineData);
1555 mHWData.backup();
1556 mHWData->mCPUCount = aCPUCount;
1557
1558 return S_OK;
1559}
1560
1561HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1562{
1563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1564
1565 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1566
1567 return S_OK;
1568}
1569
1570HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1571{
1572 HRESULT rc = S_OK;
1573
1574 /* check throttle limits */
1575 if ( aCPUExecutionCap < 1
1576 || aCPUExecutionCap > 100
1577 )
1578 return setError(E_INVALIDARG,
1579 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1580 aCPUExecutionCap, 1, 100);
1581
1582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1583
1584 alock.release();
1585 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1586 alock.acquire();
1587 if (FAILED(rc)) return rc;
1588
1589 i_setModified(IsModified_MachineData);
1590 mHWData.backup();
1591 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1592
1593 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1594 if (Global::IsOnline(mData->mMachineState))
1595 i_saveSettings(NULL);
1596
1597 return S_OK;
1598}
1599
1600HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1601{
1602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1603
1604 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1605
1606 return S_OK;
1607}
1608
1609HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1610{
1611 HRESULT rc = S_OK;
1612
1613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1614
1615 rc = i_checkStateDependency(MutableStateDep);
1616 if (FAILED(rc)) return rc;
1617
1618 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1619 {
1620 if (aCPUHotPlugEnabled)
1621 {
1622 i_setModified(IsModified_MachineData);
1623 mHWData.backup();
1624
1625 /* Add the amount of CPUs currently attached */
1626 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1627 mHWData->mCPUAttached[i] = true;
1628 }
1629 else
1630 {
1631 /*
1632 * We can disable hotplug only if the amount of maximum CPUs is equal
1633 * to the amount of attached CPUs
1634 */
1635 unsigned cCpusAttached = 0;
1636 unsigned iHighestId = 0;
1637
1638 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1639 {
1640 if (mHWData->mCPUAttached[i])
1641 {
1642 cCpusAttached++;
1643 iHighestId = i;
1644 }
1645 }
1646
1647 if ( (cCpusAttached != mHWData->mCPUCount)
1648 || (iHighestId >= mHWData->mCPUCount))
1649 return setError(E_INVALIDARG,
1650 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1651
1652 i_setModified(IsModified_MachineData);
1653 mHWData.backup();
1654 }
1655 }
1656
1657 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1658
1659 return rc;
1660}
1661
1662HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1663{
1664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1665
1666 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1667
1668 return S_OK;
1669}
1670
1671HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1672{
1673 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1674
1675 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1676 if (SUCCEEDED(hrc))
1677 {
1678 i_setModified(IsModified_MachineData);
1679 mHWData.backup();
1680 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1681 }
1682 return hrc;
1683}
1684
1685HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1686{
1687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1688 aCPUProfile = mHWData->mCpuProfile;
1689 return S_OK;
1690}
1691
1692HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1693{
1694 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1695 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1696 if (SUCCEEDED(hrc))
1697 {
1698 i_setModified(IsModified_MachineData);
1699 mHWData.backup();
1700 /* Empty equals 'host'. */
1701 if (aCPUProfile.isNotEmpty())
1702 mHWData->mCpuProfile = aCPUProfile;
1703 else
1704 mHWData->mCpuProfile = "host";
1705 }
1706 return hrc;
1707}
1708
1709HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1710{
1711#ifdef VBOX_WITH_USB_CARDREADER
1712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1713
1714 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1715
1716 return S_OK;
1717#else
1718 NOREF(aEmulatedUSBCardReaderEnabled);
1719 return E_NOTIMPL;
1720#endif
1721}
1722
1723HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1724{
1725#ifdef VBOX_WITH_USB_CARDREADER
1726 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1727
1728 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1729 if (FAILED(rc)) return rc;
1730
1731 i_setModified(IsModified_MachineData);
1732 mHWData.backup();
1733 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1734
1735 return S_OK;
1736#else
1737 NOREF(aEmulatedUSBCardReaderEnabled);
1738 return E_NOTIMPL;
1739#endif
1740}
1741
1742HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1743{
1744 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1745
1746 *aHPETEnabled = mHWData->mHPETEnabled;
1747
1748 return S_OK;
1749}
1750
1751HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1752{
1753 HRESULT rc = S_OK;
1754
1755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1756
1757 rc = i_checkStateDependency(MutableStateDep);
1758 if (FAILED(rc)) return rc;
1759
1760 i_setModified(IsModified_MachineData);
1761 mHWData.backup();
1762
1763 mHWData->mHPETEnabled = aHPETEnabled;
1764
1765 return rc;
1766}
1767
1768/** @todo this method should not be public */
1769HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1770{
1771 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1772
1773 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1774
1775 return S_OK;
1776}
1777
1778/**
1779 * Set the memory balloon size.
1780 *
1781 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1782 * we have to make sure that we never call IGuest from here.
1783 */
1784HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1785{
1786 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1787#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1788 /* check limits */
1789 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1790 return setError(E_INVALIDARG,
1791 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1792 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1793
1794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1795
1796 i_setModified(IsModified_MachineData);
1797 mHWData.backup();
1798 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1799
1800 return S_OK;
1801#else
1802 NOREF(aMemoryBalloonSize);
1803 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1804#endif
1805}
1806
1807HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1808{
1809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1810
1811 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1812 return S_OK;
1813}
1814
1815HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1816{
1817#ifdef VBOX_WITH_PAGE_SHARING
1818 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1819
1820 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1821 i_setModified(IsModified_MachineData);
1822 mHWData.backup();
1823 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1824 return S_OK;
1825#else
1826 NOREF(aPageFusionEnabled);
1827 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1828#endif
1829}
1830
1831HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1832{
1833 /* mBIOSSettings is constant during life time, no need to lock */
1834 aBIOSSettings = mBIOSSettings;
1835
1836 return S_OK;
1837}
1838
1839HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1840{
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 aRecordingSettings = mRecordingSettings;
1844
1845 return S_OK;
1846}
1847
1848HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1849{
1850 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 aGraphicsAdapter = mGraphicsAdapter;
1853
1854 return S_OK;
1855}
1856
1857HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1858{
1859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1860
1861 switch (aProperty)
1862 {
1863 case CPUPropertyType_PAE:
1864 *aValue = mHWData->mPAEEnabled;
1865 break;
1866
1867 case CPUPropertyType_LongMode:
1868 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1869 *aValue = TRUE;
1870 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1871 *aValue = FALSE;
1872#if HC_ARCH_BITS == 64
1873 else
1874 *aValue = TRUE;
1875#else
1876 else
1877 {
1878 *aValue = FALSE;
1879
1880 ComObjPtr<GuestOSType> pGuestOSType;
1881 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1882 pGuestOSType);
1883 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1884 {
1885 if (pGuestOSType->i_is64Bit())
1886 {
1887 ComObjPtr<Host> pHost = mParent->i_host();
1888 alock.release();
1889
1890 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1891 if (FAILED(hrc2))
1892 *aValue = FALSE;
1893 }
1894 }
1895 }
1896#endif
1897 break;
1898
1899 case CPUPropertyType_TripleFaultReset:
1900 *aValue = mHWData->mTripleFaultReset;
1901 break;
1902
1903 case CPUPropertyType_APIC:
1904 *aValue = mHWData->mAPIC;
1905 break;
1906
1907 case CPUPropertyType_X2APIC:
1908 *aValue = mHWData->mX2APIC;
1909 break;
1910
1911 case CPUPropertyType_IBPBOnVMExit:
1912 *aValue = mHWData->mIBPBOnVMExit;
1913 break;
1914
1915 case CPUPropertyType_IBPBOnVMEntry:
1916 *aValue = mHWData->mIBPBOnVMEntry;
1917 break;
1918
1919 case CPUPropertyType_SpecCtrl:
1920 *aValue = mHWData->mSpecCtrl;
1921 break;
1922
1923 case CPUPropertyType_SpecCtrlByHost:
1924 *aValue = mHWData->mSpecCtrlByHost;
1925 break;
1926
1927 case CPUPropertyType_HWVirt:
1928 *aValue = mHWData->mNestedHWVirt;
1929 break;
1930
1931 case CPUPropertyType_L1DFlushOnEMTScheduling:
1932 *aValue = mHWData->mL1DFlushOnSched;
1933 break;
1934
1935 case CPUPropertyType_L1DFlushOnVMEntry:
1936 *aValue = mHWData->mL1DFlushOnVMEntry;
1937 break;
1938
1939 case CPUPropertyType_MDSClearOnEMTScheduling:
1940 *aValue = mHWData->mMDSClearOnSched;
1941 break;
1942
1943 case CPUPropertyType_MDSClearOnVMEntry:
1944 *aValue = mHWData->mMDSClearOnVMEntry;
1945 break;
1946
1947 default:
1948 return E_INVALIDARG;
1949 }
1950 return S_OK;
1951}
1952
1953HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1954{
1955 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1956
1957 HRESULT rc = i_checkStateDependency(MutableStateDep);
1958 if (FAILED(rc)) return rc;
1959
1960 switch (aProperty)
1961 {
1962 case CPUPropertyType_PAE:
1963 i_setModified(IsModified_MachineData);
1964 mHWData.backup();
1965 mHWData->mPAEEnabled = !!aValue;
1966 break;
1967
1968 case CPUPropertyType_LongMode:
1969 i_setModified(IsModified_MachineData);
1970 mHWData.backup();
1971 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1972 break;
1973
1974 case CPUPropertyType_TripleFaultReset:
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mTripleFaultReset = !!aValue;
1978 break;
1979
1980 case CPUPropertyType_APIC:
1981 if (mHWData->mX2APIC)
1982 aValue = TRUE;
1983 i_setModified(IsModified_MachineData);
1984 mHWData.backup();
1985 mHWData->mAPIC = !!aValue;
1986 break;
1987
1988 case CPUPropertyType_X2APIC:
1989 i_setModified(IsModified_MachineData);
1990 mHWData.backup();
1991 mHWData->mX2APIC = !!aValue;
1992 if (aValue)
1993 mHWData->mAPIC = !!aValue;
1994 break;
1995
1996 case CPUPropertyType_IBPBOnVMExit:
1997 i_setModified(IsModified_MachineData);
1998 mHWData.backup();
1999 mHWData->mIBPBOnVMExit = !!aValue;
2000 break;
2001
2002 case CPUPropertyType_IBPBOnVMEntry:
2003 i_setModified(IsModified_MachineData);
2004 mHWData.backup();
2005 mHWData->mIBPBOnVMEntry = !!aValue;
2006 break;
2007
2008 case CPUPropertyType_SpecCtrl:
2009 i_setModified(IsModified_MachineData);
2010 mHWData.backup();
2011 mHWData->mSpecCtrl = !!aValue;
2012 break;
2013
2014 case CPUPropertyType_SpecCtrlByHost:
2015 i_setModified(IsModified_MachineData);
2016 mHWData.backup();
2017 mHWData->mSpecCtrlByHost = !!aValue;
2018 break;
2019
2020 case CPUPropertyType_HWVirt:
2021 i_setModified(IsModified_MachineData);
2022 mHWData.backup();
2023 mHWData->mNestedHWVirt = !!aValue;
2024 break;
2025
2026 case CPUPropertyType_L1DFlushOnEMTScheduling:
2027 i_setModified(IsModified_MachineData);
2028 mHWData.backup();
2029 mHWData->mL1DFlushOnSched = !!aValue;
2030 break;
2031
2032 case CPUPropertyType_L1DFlushOnVMEntry:
2033 i_setModified(IsModified_MachineData);
2034 mHWData.backup();
2035 mHWData->mL1DFlushOnVMEntry = !!aValue;
2036 break;
2037
2038 case CPUPropertyType_MDSClearOnEMTScheduling:
2039 i_setModified(IsModified_MachineData);
2040 mHWData.backup();
2041 mHWData->mMDSClearOnSched = !!aValue;
2042 break;
2043
2044 case CPUPropertyType_MDSClearOnVMEntry:
2045 i_setModified(IsModified_MachineData);
2046 mHWData.backup();
2047 mHWData->mMDSClearOnVMEntry = !!aValue;
2048 break;
2049
2050 default:
2051 return E_INVALIDARG;
2052 }
2053 return S_OK;
2054}
2055
2056HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2057 ULONG *aValEcx, ULONG *aValEdx)
2058{
2059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2060 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2061 {
2062 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2063 it != mHWData->mCpuIdLeafList.end();
2064 ++it)
2065 {
2066 if (aOrdinal == 0)
2067 {
2068 const settings::CpuIdLeaf &rLeaf= *it;
2069 *aIdx = rLeaf.idx;
2070 *aSubIdx = rLeaf.idxSub;
2071 *aValEax = rLeaf.uEax;
2072 *aValEbx = rLeaf.uEbx;
2073 *aValEcx = rLeaf.uEcx;
2074 *aValEdx = rLeaf.uEdx;
2075 return S_OK;
2076 }
2077 aOrdinal--;
2078 }
2079 }
2080 return E_INVALIDARG;
2081}
2082
2083HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2084{
2085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2086
2087 /*
2088 * Search the list.
2089 */
2090 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2091 {
2092 const settings::CpuIdLeaf &rLeaf= *it;
2093 if ( rLeaf.idx == aIdx
2094 && ( aSubIdx == UINT32_MAX
2095 || rLeaf.idxSub == aSubIdx) )
2096 {
2097 *aValEax = rLeaf.uEax;
2098 *aValEbx = rLeaf.uEbx;
2099 *aValEcx = rLeaf.uEcx;
2100 *aValEdx = rLeaf.uEdx;
2101 return S_OK;
2102 }
2103 }
2104
2105 return E_INVALIDARG;
2106}
2107
2108
2109HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2110{
2111 /*
2112 * Validate input before taking locks and checking state.
2113 */
2114 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2115 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2116 if ( aIdx >= UINT32_C(0x20)
2117 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2118 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2119 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2120
2121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2122 HRESULT rc = i_checkStateDependency(MutableStateDep);
2123 if (FAILED(rc)) return rc;
2124
2125 /*
2126 * Impose a maximum number of leaves.
2127 */
2128 if (mHWData->mCpuIdLeafList.size() > 256)
2129 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2130
2131 /*
2132 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2133 */
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136
2137 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2138 {
2139 settings::CpuIdLeaf &rLeaf= *it;
2140 if ( rLeaf.idx == aIdx
2141 && ( aSubIdx == UINT32_MAX
2142 || rLeaf.idxSub == aSubIdx) )
2143 it = mHWData->mCpuIdLeafList.erase(it);
2144 else
2145 ++it;
2146 }
2147
2148 settings::CpuIdLeaf NewLeaf;
2149 NewLeaf.idx = aIdx;
2150 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2151 NewLeaf.uEax = aValEax;
2152 NewLeaf.uEbx = aValEbx;
2153 NewLeaf.uEcx = aValEcx;
2154 NewLeaf.uEdx = aValEdx;
2155 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2156 return S_OK;
2157}
2158
2159HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2160{
2161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2162
2163 HRESULT rc = i_checkStateDependency(MutableStateDep);
2164 if (FAILED(rc)) return rc;
2165
2166 /*
2167 * Do the removal.
2168 */
2169 bool fModified = mHWData.isBackedUp();
2170 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2171 {
2172 settings::CpuIdLeaf &rLeaf= *it;
2173 if ( rLeaf.idx == aIdx
2174 && ( aSubIdx == UINT32_MAX
2175 || rLeaf.idxSub == aSubIdx) )
2176 {
2177 if (!fModified)
2178 {
2179 fModified = true;
2180 i_setModified(IsModified_MachineData);
2181 mHWData.backup();
2182 // Start from the beginning, since mHWData.backup() creates
2183 // a new list, causing iterator mixup. This makes sure that
2184 // the settings are not unnecessarily marked as modified,
2185 // at the price of extra list walking.
2186 it = mHWData->mCpuIdLeafList.begin();
2187 }
2188 else
2189 it = mHWData->mCpuIdLeafList.erase(it);
2190 }
2191 else
2192 ++it;
2193 }
2194
2195 return S_OK;
2196}
2197
2198HRESULT Machine::removeAllCPUIDLeaves()
2199{
2200 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2201
2202 HRESULT rc = i_checkStateDependency(MutableStateDep);
2203 if (FAILED(rc)) return rc;
2204
2205 if (mHWData->mCpuIdLeafList.size() > 0)
2206 {
2207 i_setModified(IsModified_MachineData);
2208 mHWData.backup();
2209
2210 mHWData->mCpuIdLeafList.clear();
2211 }
2212
2213 return S_OK;
2214}
2215HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2216{
2217 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2218
2219 switch(aProperty)
2220 {
2221 case HWVirtExPropertyType_Enabled:
2222 *aValue = mHWData->mHWVirtExEnabled;
2223 break;
2224
2225 case HWVirtExPropertyType_VPID:
2226 *aValue = mHWData->mHWVirtExVPIDEnabled;
2227 break;
2228
2229 case HWVirtExPropertyType_NestedPaging:
2230 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2231 break;
2232
2233 case HWVirtExPropertyType_UnrestrictedExecution:
2234 *aValue = mHWData->mHWVirtExUXEnabled;
2235 break;
2236
2237 case HWVirtExPropertyType_LargePages:
2238 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2239#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2240 *aValue = FALSE;
2241#endif
2242 break;
2243
2244 case HWVirtExPropertyType_Force:
2245 *aValue = mHWData->mHWVirtExForceEnabled;
2246 break;
2247
2248 case HWVirtExPropertyType_UseNativeApi:
2249 *aValue = mHWData->mHWVirtExUseNativeApi;
2250 break;
2251
2252 case HWVirtExPropertyType_VirtVmsaveVmload:
2253 *aValue = mHWData->mHWVirtExVirtVmsaveVmload;
2254 break;
2255
2256 default:
2257 return E_INVALIDARG;
2258 }
2259 return S_OK;
2260}
2261
2262HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2263{
2264 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2265
2266 HRESULT rc = i_checkStateDependency(MutableStateDep);
2267 if (FAILED(rc)) return rc;
2268
2269 switch (aProperty)
2270 {
2271 case HWVirtExPropertyType_Enabled:
2272 i_setModified(IsModified_MachineData);
2273 mHWData.backup();
2274 mHWData->mHWVirtExEnabled = !!aValue;
2275 break;
2276
2277 case HWVirtExPropertyType_VPID:
2278 i_setModified(IsModified_MachineData);
2279 mHWData.backup();
2280 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2281 break;
2282
2283 case HWVirtExPropertyType_NestedPaging:
2284 i_setModified(IsModified_MachineData);
2285 mHWData.backup();
2286 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2287 break;
2288
2289 case HWVirtExPropertyType_UnrestrictedExecution:
2290 i_setModified(IsModified_MachineData);
2291 mHWData.backup();
2292 mHWData->mHWVirtExUXEnabled = !!aValue;
2293 break;
2294
2295 case HWVirtExPropertyType_LargePages:
2296 i_setModified(IsModified_MachineData);
2297 mHWData.backup();
2298 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2299 break;
2300
2301 case HWVirtExPropertyType_Force:
2302 i_setModified(IsModified_MachineData);
2303 mHWData.backup();
2304 mHWData->mHWVirtExForceEnabled = !!aValue;
2305 break;
2306
2307 case HWVirtExPropertyType_UseNativeApi:
2308 i_setModified(IsModified_MachineData);
2309 mHWData.backup();
2310 mHWData->mHWVirtExUseNativeApi = !!aValue;
2311 break;
2312
2313 case HWVirtExPropertyType_VirtVmsaveVmload:
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316 mHWData->mHWVirtExVirtVmsaveVmload = !!aValue;
2317 break;
2318
2319 default:
2320 return E_INVALIDARG;
2321 }
2322
2323 return S_OK;
2324}
2325
2326HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2327{
2328 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2329
2330 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2331
2332 return S_OK;
2333}
2334
2335HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2336{
2337 /** @todo (r=dmik):
2338 * 1. Allow to change the name of the snapshot folder containing snapshots
2339 * 2. Rename the folder on disk instead of just changing the property
2340 * value (to be smart and not to leave garbage). Note that it cannot be
2341 * done here because the change may be rolled back. Thus, the right
2342 * place is #saveSettings().
2343 */
2344
2345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2346
2347 HRESULT rc = i_checkStateDependency(MutableStateDep);
2348 if (FAILED(rc)) return rc;
2349
2350 if (!mData->mCurrentSnapshot.isNull())
2351 return setError(E_FAIL,
2352 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2353
2354 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2355
2356 if (strSnapshotFolder.isEmpty())
2357 strSnapshotFolder = "Snapshots";
2358 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2359 if (RT_FAILURE(vrc))
2360 return setErrorBoth(E_FAIL, vrc,
2361 tr("Invalid snapshot folder '%s' (%Rrc)"),
2362 strSnapshotFolder.c_str(), vrc);
2363
2364 i_setModified(IsModified_MachineData);
2365 mUserData.backup();
2366
2367 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2368
2369 return S_OK;
2370}
2371
2372HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2373{
2374 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2375
2376 aMediumAttachments.resize(mMediumAttachments->size());
2377 size_t i = 0;
2378 for (MediumAttachmentList::const_iterator
2379 it = mMediumAttachments->begin();
2380 it != mMediumAttachments->end();
2381 ++it, ++i)
2382 aMediumAttachments[i] = *it;
2383
2384 return S_OK;
2385}
2386
2387HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2388{
2389 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2390
2391 Assert(!!mVRDEServer);
2392
2393 aVRDEServer = mVRDEServer;
2394
2395 return S_OK;
2396}
2397
2398HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2399{
2400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2401
2402 aAudioAdapter = mAudioAdapter;
2403
2404 return S_OK;
2405}
2406
2407HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2408{
2409#ifdef VBOX_WITH_VUSB
2410 clearError();
2411 MultiResult rc(S_OK);
2412
2413# ifdef VBOX_WITH_USB
2414 rc = mParent->i_host()->i_checkUSBProxyService();
2415 if (FAILED(rc)) return rc;
2416# endif
2417
2418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2419
2420 aUSBControllers.resize(mUSBControllers->size());
2421 size_t i = 0;
2422 for (USBControllerList::const_iterator
2423 it = mUSBControllers->begin();
2424 it != mUSBControllers->end();
2425 ++it, ++i)
2426 aUSBControllers[i] = *it;
2427
2428 return S_OK;
2429#else
2430 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2431 * extended error info to indicate that USB is simply not available
2432 * (w/o treating it as a failure), for example, as in OSE */
2433 NOREF(aUSBControllers);
2434 ReturnComNotImplemented();
2435#endif /* VBOX_WITH_VUSB */
2436}
2437
2438HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2439{
2440#ifdef VBOX_WITH_VUSB
2441 clearError();
2442 MultiResult rc(S_OK);
2443
2444# ifdef VBOX_WITH_USB
2445 rc = mParent->i_host()->i_checkUSBProxyService();
2446 if (FAILED(rc)) return rc;
2447# endif
2448
2449 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2450
2451 aUSBDeviceFilters = mUSBDeviceFilters;
2452 return rc;
2453#else
2454 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2455 * extended error info to indicate that USB is simply not available
2456 * (w/o treating it as a failure), for example, as in OSE */
2457 NOREF(aUSBDeviceFilters);
2458 ReturnComNotImplemented();
2459#endif /* VBOX_WITH_VUSB */
2460}
2461
2462HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2463{
2464 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2465
2466 aSettingsFilePath = mData->m_strConfigFileFull;
2467
2468 return S_OK;
2469}
2470
2471HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2472{
2473 RT_NOREF(aSettingsFilePath);
2474 ReturnComNotImplemented();
2475}
2476
2477HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2478{
2479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2480
2481 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2482 if (FAILED(rc)) return rc;
2483
2484 if (!mData->pMachineConfigFile->fileExists())
2485 // this is a new machine, and no config file exists yet:
2486 *aSettingsModified = TRUE;
2487 else
2488 *aSettingsModified = (mData->flModifications != 0);
2489
2490 return S_OK;
2491}
2492
2493HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2494{
2495 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2496
2497 *aSessionState = mData->mSession.mState;
2498
2499 return S_OK;
2500}
2501
2502HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2503{
2504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2505
2506 aSessionName = mData->mSession.mName;
2507
2508 return S_OK;
2509}
2510
2511HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2512{
2513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2514
2515 *aSessionPID = mData->mSession.mPID;
2516
2517 return S_OK;
2518}
2519
2520HRESULT Machine::getState(MachineState_T *aState)
2521{
2522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2523
2524 *aState = mData->mMachineState;
2525 Assert(mData->mMachineState != MachineState_Null);
2526
2527 return S_OK;
2528}
2529
2530HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2531{
2532 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 aStateFilePath = mSSData->strStateFilePath;
2544
2545 return S_OK;
2546}
2547
2548HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2549{
2550 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2551
2552 i_getLogFolder(aLogFolder);
2553
2554 return S_OK;
2555}
2556
2557HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2558{
2559 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2560
2561 aCurrentSnapshot = mData->mCurrentSnapshot;
2562
2563 return S_OK;
2564}
2565
2566HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2567{
2568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2569
2570 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2571 ? 0
2572 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2573
2574 return S_OK;
2575}
2576
2577HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2578{
2579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2580
2581 /* Note: for machines with no snapshots, we always return FALSE
2582 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2583 * reasons :) */
2584
2585 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2586 ? FALSE
2587 : mData->mCurrentStateModified;
2588
2589 return S_OK;
2590}
2591
2592HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2593{
2594 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2595
2596 aSharedFolders.resize(mHWData->mSharedFolders.size());
2597 size_t i = 0;
2598 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2599 it = mHWData->mSharedFolders.begin();
2600 it != mHWData->mSharedFolders.end();
2601 ++it, ++i)
2602 aSharedFolders[i] = *it;
2603
2604 return S_OK;
2605}
2606
2607HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2608{
2609 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2610
2611 *aClipboardMode = mHWData->mClipboardMode;
2612
2613 return S_OK;
2614}
2615
2616HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2617{
2618 HRESULT rc = S_OK;
2619
2620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 alock.release();
2623 rc = i_onClipboardModeChange(aClipboardMode);
2624 alock.acquire();
2625 if (FAILED(rc)) return rc;
2626
2627 i_setModified(IsModified_MachineData);
2628 mHWData.backup();
2629 mHWData->mClipboardMode = aClipboardMode;
2630
2631 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2632 if (Global::IsOnline(mData->mMachineState))
2633 i_saveSettings(NULL);
2634
2635 return S_OK;
2636}
2637
2638HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2639{
2640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2641
2642 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2643
2644 return S_OK;
2645}
2646
2647HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2648{
2649 HRESULT rc = S_OK;
2650
2651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2652
2653 alock.release();
2654 rc = i_onClipboardFileTransferModeChange(aEnabled);
2655 alock.acquire();
2656 if (FAILED(rc)) return rc;
2657
2658 i_setModified(IsModified_MachineData);
2659 mHWData.backup();
2660 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2661
2662 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2663 if (Global::IsOnline(mData->mMachineState))
2664 i_saveSettings(NULL);
2665
2666 return S_OK;
2667}
2668
2669HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2670{
2671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2672
2673 *aDnDMode = mHWData->mDnDMode;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2679{
2680 HRESULT rc = S_OK;
2681
2682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 alock.release();
2685 rc = i_onDnDModeChange(aDnDMode);
2686
2687 alock.acquire();
2688 if (FAILED(rc)) return rc;
2689
2690 i_setModified(IsModified_MachineData);
2691 mHWData.backup();
2692 mHWData->mDnDMode = aDnDMode;
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::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2702{
2703 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2704
2705 aStorageControllers.resize(mStorageControllers->size());
2706 size_t i = 0;
2707 for (StorageControllerList::const_iterator
2708 it = mStorageControllers->begin();
2709 it != mStorageControllers->end();
2710 ++it, ++i)
2711 aStorageControllers[i] = *it;
2712
2713 return S_OK;
2714}
2715
2716HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2717{
2718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 *aEnabled = mUserData->s.fTeleporterEnabled;
2721
2722 return S_OK;
2723}
2724
2725HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2726{
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 /* Only allow it to be set to true when PoweredOff or Aborted.
2730 (Clearing it is always permitted.) */
2731 if ( aTeleporterEnabled
2732 && mData->mRegistered
2733 && ( !i_isSessionMachine()
2734 || ( mData->mMachineState != MachineState_PoweredOff
2735 && mData->mMachineState != MachineState_Teleported
2736 && mData->mMachineState != MachineState_Aborted
2737 )
2738 )
2739 )
2740 return setError(VBOX_E_INVALID_VM_STATE,
2741 tr("The machine is not powered off (state is %s)"),
2742 Global::stringifyMachineState(mData->mMachineState));
2743
2744 i_setModified(IsModified_MachineData);
2745 mUserData.backup();
2746 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2747
2748 return S_OK;
2749}
2750
2751HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2752{
2753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2754
2755 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2756
2757 return S_OK;
2758}
2759
2760HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2761{
2762 if (aTeleporterPort >= _64K)
2763 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2764
2765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2768 if (FAILED(rc)) return rc;
2769
2770 i_setModified(IsModified_MachineData);
2771 mUserData.backup();
2772 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2773
2774 return S_OK;
2775}
2776
2777HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2778{
2779 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2780
2781 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2782
2783 return S_OK;
2784}
2785
2786HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2787{
2788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2789
2790 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2791 if (FAILED(rc)) return rc;
2792
2793 i_setModified(IsModified_MachineData);
2794 mUserData.backup();
2795 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2796
2797 return S_OK;
2798}
2799
2800HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2801{
2802 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2803 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2809{
2810 /*
2811 * Hash the password first.
2812 */
2813 com::Utf8Str aT = aTeleporterPassword;
2814
2815 if (!aT.isEmpty())
2816 {
2817 if (VBoxIsPasswordHashed(&aT))
2818 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2819 VBoxHashPassword(&aT);
2820 }
2821
2822 /*
2823 * Do the update.
2824 */
2825 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2826 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2827 if (SUCCEEDED(hrc))
2828 {
2829 i_setModified(IsModified_MachineData);
2830 mUserData.backup();
2831 mUserData->s.strTeleporterPassword = aT;
2832 }
2833
2834 return hrc;
2835}
2836
2837HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2838{
2839 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2840
2841 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2847{
2848 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2849
2850 /* Only allow it to be set to true when PoweredOff or Aborted.
2851 (Clearing it is always permitted.) */
2852 if ( aRTCUseUTC
2853 && mData->mRegistered
2854 && ( !i_isSessionMachine()
2855 || ( mData->mMachineState != MachineState_PoweredOff
2856 && mData->mMachineState != MachineState_Teleported
2857 && mData->mMachineState != MachineState_Aborted
2858 )
2859 )
2860 )
2861 return setError(VBOX_E_INVALID_VM_STATE,
2862 tr("The machine is not powered off (state is %s)"),
2863 Global::stringifyMachineState(mData->mMachineState));
2864
2865 i_setModified(IsModified_MachineData);
2866 mUserData.backup();
2867 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2868
2869 return S_OK;
2870}
2871
2872HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2873{
2874 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2875
2876 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2877
2878 return S_OK;
2879}
2880
2881HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2882{
2883 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2884
2885 HRESULT rc = i_checkStateDependency(MutableStateDep);
2886 if (FAILED(rc)) return rc;
2887
2888 i_setModified(IsModified_MachineData);
2889 mHWData.backup();
2890 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2891
2892 return S_OK;
2893}
2894
2895HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2896{
2897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2898
2899 *aIOCacheSize = mHWData->mIOCacheSize;
2900
2901 return S_OK;
2902}
2903
2904HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2905{
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = i_checkStateDependency(MutableStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 i_setModified(IsModified_MachineData);
2912 mHWData.backup();
2913 mHWData->mIOCacheSize = aIOCacheSize;
2914
2915 return S_OK;
2916}
2917
2918
2919/**
2920 * @note Locks objects!
2921 */
2922HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2923 LockType_T aLockType)
2924{
2925 /* check the session state */
2926 SessionState_T state;
2927 HRESULT rc = aSession->COMGETTER(State)(&state);
2928 if (FAILED(rc)) return rc;
2929
2930 if (state != SessionState_Unlocked)
2931 return setError(VBOX_E_INVALID_OBJECT_STATE,
2932 tr("The given session is busy"));
2933
2934 // get the client's IInternalSessionControl interface
2935 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2936 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2937 E_INVALIDARG);
2938
2939 // session name (only used in some code paths)
2940 Utf8Str strSessionName;
2941
2942 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2943
2944 if (!mData->mRegistered)
2945 return setError(E_UNEXPECTED,
2946 tr("The machine '%s' is not registered"),
2947 mUserData->s.strName.c_str());
2948
2949 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2950
2951 SessionState_T oldState = mData->mSession.mState;
2952 /* Hack: in case the session is closing and there is a progress object
2953 * which allows waiting for the session to be closed, take the opportunity
2954 * and do a limited wait (max. 1 second). This helps a lot when the system
2955 * is busy and thus session closing can take a little while. */
2956 if ( mData->mSession.mState == SessionState_Unlocking
2957 && mData->mSession.mProgress)
2958 {
2959 alock.release();
2960 mData->mSession.mProgress->WaitForCompletion(1000);
2961 alock.acquire();
2962 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2963 }
2964
2965 // try again now
2966 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2967 // (i.e. session machine exists)
2968 && (aLockType == LockType_Shared) // caller wants a shared link to the
2969 // existing session that holds the write lock:
2970 )
2971 {
2972 // OK, share the session... we are now dealing with three processes:
2973 // 1) VBoxSVC (where this code runs);
2974 // 2) process C: the caller's client process (who wants a shared session);
2975 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2976
2977 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2978 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2979 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2980 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2981 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2982
2983 /*
2984 * Release the lock before calling the client process. It's safe here
2985 * since the only thing to do after we get the lock again is to add
2986 * the remote control to the list (which doesn't directly influence
2987 * anything).
2988 */
2989 alock.release();
2990
2991 // get the console of the session holding the write lock (this is a remote call)
2992 ComPtr<IConsole> pConsoleW;
2993 if (mData->mSession.mLockType == LockType_VM)
2994 {
2995 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2996 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2997 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2998 if (FAILED(rc))
2999 // the failure may occur w/o any error info (from RPC), so provide one
3000 return setError(VBOX_E_VM_ERROR,
3001 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3002 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3003 }
3004
3005 // share the session machine and W's console with the caller's session
3006 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3007 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3008 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3009
3010 if (FAILED(rc))
3011 // the failure may occur w/o any error info (from RPC), so provide one
3012 return setError(VBOX_E_VM_ERROR,
3013 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3014 alock.acquire();
3015
3016 // need to revalidate the state after acquiring the lock again
3017 if (mData->mSession.mState != SessionState_Locked)
3018 {
3019 pSessionControl->Uninitialize();
3020 return setError(VBOX_E_INVALID_SESSION_STATE,
3021 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3022 mUserData->s.strName.c_str());
3023 }
3024
3025 // add the caller's session to the list
3026 mData->mSession.mRemoteControls.push_back(pSessionControl);
3027 }
3028 else if ( mData->mSession.mState == SessionState_Locked
3029 || mData->mSession.mState == SessionState_Unlocking
3030 )
3031 {
3032 // sharing not permitted, or machine still unlocking:
3033 return setError(VBOX_E_INVALID_OBJECT_STATE,
3034 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3035 mUserData->s.strName.c_str());
3036 }
3037 else
3038 {
3039 // machine is not locked: then write-lock the machine (create the session machine)
3040
3041 // must not be busy
3042 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3043
3044 // get the caller's session PID
3045 RTPROCESS pid = NIL_RTPROCESS;
3046 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3047 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3048 Assert(pid != NIL_RTPROCESS);
3049
3050 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3051
3052 if (fLaunchingVMProcess)
3053 {
3054 if (mData->mSession.mPID == NIL_RTPROCESS)
3055 {
3056 // two or more clients racing for a lock, the one which set the
3057 // session state to Spawning will win, the others will get an
3058 // error as we can't decide here if waiting a little would help
3059 // (only for shared locks this would avoid an error)
3060 return setError(VBOX_E_INVALID_OBJECT_STATE,
3061 tr("The machine '%s' already has a lock request pending"),
3062 mUserData->s.strName.c_str());
3063 }
3064
3065 // this machine is awaiting for a spawning session to be opened:
3066 // then the calling process must be the one that got started by
3067 // LaunchVMProcess()
3068
3069 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3070 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3071
3072#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3073 /* Hardened windows builds spawns three processes when a VM is
3074 launched, the 3rd one is the one that will end up here. */
3075 RTPROCESS pidParent;
3076 int vrc = RTProcQueryParent(pid, &pidParent);
3077 if (RT_SUCCESS(vrc))
3078 vrc = RTProcQueryParent(pidParent, &pidParent);
3079 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3080 || vrc == VERR_ACCESS_DENIED)
3081 {
3082 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3083 mData->mSession.mPID = pid;
3084 }
3085#endif
3086
3087 if (mData->mSession.mPID != pid)
3088 return setError(E_ACCESSDENIED,
3089 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3090 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3091 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3092 }
3093
3094 // create the mutable SessionMachine from the current machine
3095 ComObjPtr<SessionMachine> sessionMachine;
3096 sessionMachine.createObject();
3097 rc = sessionMachine->init(this);
3098 AssertComRC(rc);
3099
3100 /* NOTE: doing return from this function after this point but
3101 * before the end is forbidden since it may call SessionMachine::uninit()
3102 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3103 * lock while still holding the Machine lock in alock so that a deadlock
3104 * is possible due to the wrong lock order. */
3105
3106 if (SUCCEEDED(rc))
3107 {
3108 /*
3109 * Set the session state to Spawning to protect against subsequent
3110 * attempts to open a session and to unregister the machine after
3111 * we release the lock.
3112 */
3113 SessionState_T origState = mData->mSession.mState;
3114 mData->mSession.mState = SessionState_Spawning;
3115
3116#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3117 /* Get the client token ID to be passed to the client process */
3118 Utf8Str strTokenId;
3119 sessionMachine->i_getTokenId(strTokenId);
3120 Assert(!strTokenId.isEmpty());
3121#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3122 /* Get the client token to be passed to the client process */
3123 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3124 /* The token is now "owned" by pToken, fix refcount */
3125 if (!pToken.isNull())
3126 pToken->Release();
3127#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3128
3129 /*
3130 * Release the lock before calling the client process -- it will call
3131 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3132 * because the state is Spawning, so that LaunchVMProcess() and
3133 * LockMachine() calls will fail. This method, called before we
3134 * acquire the lock again, will fail because of the wrong PID.
3135 *
3136 * Note that mData->mSession.mRemoteControls accessed outside
3137 * the lock may not be modified when state is Spawning, so it's safe.
3138 */
3139 alock.release();
3140
3141 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3142#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3143 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3144#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3145 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3146 /* Now the token is owned by the client process. */
3147 pToken.setNull();
3148#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3149 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3150
3151 /* The failure may occur w/o any error info (from RPC), so provide one */
3152 if (FAILED(rc))
3153 setError(VBOX_E_VM_ERROR,
3154 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3155
3156 // get session name, either to remember or to compare against
3157 // the already known session name.
3158 {
3159 Bstr bstrSessionName;
3160 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3161 if (SUCCEEDED(rc2))
3162 strSessionName = bstrSessionName;
3163 }
3164
3165 if ( SUCCEEDED(rc)
3166 && fLaunchingVMProcess
3167 )
3168 {
3169 /* complete the remote session initialization */
3170
3171 /* get the console from the direct session */
3172 ComPtr<IConsole> console;
3173 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3174 ComAssertComRC(rc);
3175
3176 if (SUCCEEDED(rc) && !console)
3177 {
3178 ComAssert(!!console);
3179 rc = E_FAIL;
3180 }
3181
3182 /* assign machine & console to the remote session */
3183 if (SUCCEEDED(rc))
3184 {
3185 /*
3186 * after LaunchVMProcess(), the first and the only
3187 * entry in remoteControls is that remote session
3188 */
3189 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3190 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3191 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3192
3193 /* The failure may occur w/o any error info (from RPC), so provide one */
3194 if (FAILED(rc))
3195 setError(VBOX_E_VM_ERROR,
3196 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3197 }
3198
3199 if (FAILED(rc))
3200 pSessionControl->Uninitialize();
3201 }
3202
3203 /* acquire the lock again */
3204 alock.acquire();
3205
3206 /* Restore the session state */
3207 mData->mSession.mState = origState;
3208 }
3209
3210 // finalize spawning anyway (this is why we don't return on errors above)
3211 if (fLaunchingVMProcess)
3212 {
3213 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3214 /* Note that the progress object is finalized later */
3215 /** @todo Consider checking mData->mSession.mProgress for cancellation
3216 * around here. */
3217
3218 /* We don't reset mSession.mPID here because it is necessary for
3219 * SessionMachine::uninit() to reap the child process later. */
3220
3221 if (FAILED(rc))
3222 {
3223 /* Close the remote session, remove the remote control from the list
3224 * and reset session state to Closed (@note keep the code in sync
3225 * with the relevant part in checkForSpawnFailure()). */
3226
3227 Assert(mData->mSession.mRemoteControls.size() == 1);
3228 if (mData->mSession.mRemoteControls.size() == 1)
3229 {
3230 ErrorInfoKeeper eik;
3231 mData->mSession.mRemoteControls.front()->Uninitialize();
3232 }
3233
3234 mData->mSession.mRemoteControls.clear();
3235 mData->mSession.mState = SessionState_Unlocked;
3236 }
3237 }
3238 else
3239 {
3240 /* memorize PID of the directly opened session */
3241 if (SUCCEEDED(rc))
3242 mData->mSession.mPID = pid;
3243 }
3244
3245 if (SUCCEEDED(rc))
3246 {
3247 mData->mSession.mLockType = aLockType;
3248 /* memorize the direct session control and cache IUnknown for it */
3249 mData->mSession.mDirectControl = pSessionControl;
3250 mData->mSession.mState = SessionState_Locked;
3251 if (!fLaunchingVMProcess)
3252 mData->mSession.mName = strSessionName;
3253 /* associate the SessionMachine with this Machine */
3254 mData->mSession.mMachine = sessionMachine;
3255
3256 /* request an IUnknown pointer early from the remote party for later
3257 * identity checks (it will be internally cached within mDirectControl
3258 * at least on XPCOM) */
3259 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3260 NOREF(unk);
3261 }
3262
3263 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3264 * would break the lock order */
3265 alock.release();
3266
3267 /* uninitialize the created session machine on failure */
3268 if (FAILED(rc))
3269 sessionMachine->uninit();
3270 }
3271
3272 if (SUCCEEDED(rc))
3273 {
3274 /*
3275 * tell the client watcher thread to update the set of
3276 * machines that have open sessions
3277 */
3278 mParent->i_updateClientWatcher();
3279
3280 if (oldState != SessionState_Locked)
3281 /* fire an event */
3282 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3283 }
3284
3285 return rc;
3286}
3287
3288/**
3289 * @note Locks objects!
3290 */
3291HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3292 const com::Utf8Str &aName,
3293 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3294 ComPtr<IProgress> &aProgress)
3295{
3296 Utf8Str strFrontend(aName);
3297 /* "emergencystop" doesn't need the session, so skip the checks/interface
3298 * retrieval. This code doesn't quite fit in here, but introducing a
3299 * special API method would be even more effort, and would require explicit
3300 * support by every API client. It's better to hide the feature a bit. */
3301 if (strFrontend != "emergencystop")
3302 CheckComArgNotNull(aSession);
3303
3304 HRESULT rc = S_OK;
3305 if (strFrontend.isEmpty())
3306 {
3307 Bstr bstrFrontend;
3308 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3309 if (FAILED(rc))
3310 return rc;
3311 strFrontend = bstrFrontend;
3312 if (strFrontend.isEmpty())
3313 {
3314 ComPtr<ISystemProperties> systemProperties;
3315 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3316 if (FAILED(rc))
3317 return rc;
3318 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3319 if (FAILED(rc))
3320 return rc;
3321 strFrontend = bstrFrontend;
3322 }
3323 /* paranoia - emergencystop is not a valid default */
3324 if (strFrontend == "emergencystop")
3325 strFrontend = Utf8Str::Empty;
3326 }
3327 /* default frontend: Qt GUI */
3328 if (strFrontend.isEmpty())
3329 strFrontend = "GUI/Qt";
3330
3331 if (strFrontend != "emergencystop")
3332 {
3333 /* check the session state */
3334 SessionState_T state;
3335 rc = aSession->COMGETTER(State)(&state);
3336 if (FAILED(rc))
3337 return rc;
3338
3339 if (state != SessionState_Unlocked)
3340 return setError(VBOX_E_INVALID_OBJECT_STATE,
3341 tr("The given session is busy"));
3342
3343 /* get the IInternalSessionControl interface */
3344 ComPtr<IInternalSessionControl> control(aSession);
3345 ComAssertMsgRet(!control.isNull(),
3346 ("No IInternalSessionControl interface"),
3347 E_INVALIDARG);
3348
3349 /* get the teleporter enable state for the progress object init. */
3350 BOOL fTeleporterEnabled;
3351 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3352 if (FAILED(rc))
3353 return rc;
3354
3355 /* create a progress object */
3356 ComObjPtr<ProgressProxy> progress;
3357 progress.createObject();
3358 rc = progress->init(mParent,
3359 static_cast<IMachine*>(this),
3360 Bstr(tr("Starting VM")).raw(),
3361 TRUE /* aCancelable */,
3362 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3363 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3364 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3365 2 /* uFirstOperationWeight */,
3366 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3367
3368 if (SUCCEEDED(rc))
3369 {
3370 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3371 if (SUCCEEDED(rc))
3372 {
3373 aProgress = progress;
3374
3375 /* signal the client watcher thread */
3376 mParent->i_updateClientWatcher();
3377
3378 /* fire an event */
3379 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3380 }
3381 }
3382 }
3383 else
3384 {
3385 /* no progress object - either instant success or failure */
3386 aProgress = NULL;
3387
3388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3389
3390 if (mData->mSession.mState != SessionState_Locked)
3391 return setError(VBOX_E_INVALID_OBJECT_STATE,
3392 tr("The machine '%s' is not locked by a session"),
3393 mUserData->s.strName.c_str());
3394
3395 /* must have a VM process associated - do not kill normal API clients
3396 * with an open session */
3397 if (!Global::IsOnline(mData->mMachineState))
3398 return setError(VBOX_E_INVALID_OBJECT_STATE,
3399 tr("The machine '%s' does not have a VM process"),
3400 mUserData->s.strName.c_str());
3401
3402 /* forcibly terminate the VM process */
3403 if (mData->mSession.mPID != NIL_RTPROCESS)
3404 RTProcTerminate(mData->mSession.mPID);
3405
3406 /* signal the client watcher thread, as most likely the client has
3407 * been terminated */
3408 mParent->i_updateClientWatcher();
3409 }
3410
3411 return rc;
3412}
3413
3414HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3415{
3416 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3417 return setError(E_INVALIDARG,
3418 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3419 aPosition, SchemaDefs::MaxBootPosition);
3420
3421 if (aDevice == DeviceType_USB)
3422 return setError(E_NOTIMPL,
3423 tr("Booting from USB device is currently not supported"));
3424
3425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3426
3427 HRESULT rc = i_checkStateDependency(MutableStateDep);
3428 if (FAILED(rc)) return rc;
3429
3430 i_setModified(IsModified_MachineData);
3431 mHWData.backup();
3432 mHWData->mBootOrder[aPosition - 1] = aDevice;
3433
3434 return S_OK;
3435}
3436
3437HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3438{
3439 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3440 return setError(E_INVALIDARG,
3441 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3442 aPosition, SchemaDefs::MaxBootPosition);
3443
3444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3445
3446 *aDevice = mHWData->mBootOrder[aPosition - 1];
3447
3448 return S_OK;
3449}
3450
3451HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3452 LONG aControllerPort,
3453 LONG aDevice,
3454 DeviceType_T aType,
3455 const ComPtr<IMedium> &aMedium)
3456{
3457 IMedium *aM = aMedium;
3458 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3459 aName.c_str(), aControllerPort, aDevice, aType, aM));
3460
3461 // request the host lock first, since might be calling Host methods for getting host drives;
3462 // next, protect the media tree all the while we're in here, as well as our member variables
3463 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3464 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3465
3466 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3467 if (FAILED(rc)) return rc;
3468
3469 /// @todo NEWMEDIA implicit machine registration
3470 if (!mData->mRegistered)
3471 return setError(VBOX_E_INVALID_OBJECT_STATE,
3472 tr("Cannot attach storage devices to an unregistered machine"));
3473
3474 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3475
3476 /* Check for an existing controller. */
3477 ComObjPtr<StorageController> ctl;
3478 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3479 if (FAILED(rc)) return rc;
3480
3481 StorageControllerType_T ctrlType;
3482 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3483 if (FAILED(rc))
3484 return setError(E_FAIL,
3485 tr("Could not get type of controller '%s'"),
3486 aName.c_str());
3487
3488 bool fSilent = false;
3489 Utf8Str strReconfig;
3490
3491 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3492 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3493 if ( mData->mMachineState == MachineState_Paused
3494 && strReconfig == "1")
3495 fSilent = true;
3496
3497 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3498 bool fHotplug = false;
3499 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3500 fHotplug = true;
3501
3502 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3503 return setError(VBOX_E_INVALID_VM_STATE,
3504 tr("Controller '%s' does not support hotplugging"),
3505 aName.c_str());
3506
3507 // check that the port and device are not out of range
3508 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3509 if (FAILED(rc)) return rc;
3510
3511 /* check if the device slot is already busy */
3512 MediumAttachment *pAttachTemp;
3513 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3514 aName,
3515 aControllerPort,
3516 aDevice)))
3517 {
3518 Medium *pMedium = pAttachTemp->i_getMedium();
3519 if (pMedium)
3520 {
3521 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3522 return setError(VBOX_E_OBJECT_IN_USE,
3523 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3524 pMedium->i_getLocationFull().c_str(),
3525 aControllerPort,
3526 aDevice,
3527 aName.c_str());
3528 }
3529 else
3530 return setError(VBOX_E_OBJECT_IN_USE,
3531 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3532 aControllerPort, aDevice, aName.c_str());
3533 }
3534
3535 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3536 if (aMedium && medium.isNull())
3537 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3538
3539 AutoCaller mediumCaller(medium);
3540 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3541
3542 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3543
3544 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3545 && !medium.isNull()
3546 && ( medium->i_getType() != MediumType_Readonly
3547 || medium->i_getDeviceType() != DeviceType_DVD)
3548 )
3549 return setError(VBOX_E_OBJECT_IN_USE,
3550 tr("Medium '%s' is already attached to this virtual machine"),
3551 medium->i_getLocationFull().c_str());
3552
3553 if (!medium.isNull())
3554 {
3555 MediumType_T mtype = medium->i_getType();
3556 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3557 // For DVDs it's not written to the config file, so needs no global config
3558 // version bump. For floppies it's a new attribute "type", which is ignored
3559 // by older VirtualBox version, so needs no global config version bump either.
3560 // For hard disks this type is not accepted.
3561 if (mtype == MediumType_MultiAttach)
3562 {
3563 // This type is new with VirtualBox 4.0 and therefore requires settings
3564 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3565 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3566 // two reasons: The medium type is a property of the media registry tree, which
3567 // can reside in the global config file (for pre-4.0 media); we would therefore
3568 // possibly need to bump the global config version. We don't want to do that though
3569 // because that might make downgrading to pre-4.0 impossible.
3570 // As a result, we can only use these two new types if the medium is NOT in the
3571 // global registry:
3572 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3573 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3574 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3575 )
3576 return setError(VBOX_E_INVALID_OBJECT_STATE,
3577 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3578 "to machines that were created with VirtualBox 4.0 or later"),
3579 medium->i_getLocationFull().c_str());
3580 }
3581 }
3582
3583 bool fIndirect = false;
3584 if (!medium.isNull())
3585 fIndirect = medium->i_isReadOnly();
3586 bool associate = true;
3587
3588 do
3589 {
3590 if ( aType == DeviceType_HardDisk
3591 && mMediumAttachments.isBackedUp())
3592 {
3593 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3594
3595 /* check if the medium was attached to the VM before we started
3596 * changing attachments in which case the attachment just needs to
3597 * be restored */
3598 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3599 {
3600 AssertReturn(!fIndirect, E_FAIL);
3601
3602 /* see if it's the same bus/channel/device */
3603 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3604 {
3605 /* the simplest case: restore the whole attachment
3606 * and return, nothing else to do */
3607 mMediumAttachments->push_back(pAttachTemp);
3608
3609 /* Reattach the medium to the VM. */
3610 if (fHotplug || fSilent)
3611 {
3612 mediumLock.release();
3613 treeLock.release();
3614 alock.release();
3615
3616 MediumLockList *pMediumLockList(new MediumLockList());
3617
3618 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3619 medium /* pToLockWrite */,
3620 false /* fMediumLockWriteAll */,
3621 NULL,
3622 *pMediumLockList);
3623 alock.acquire();
3624 if (FAILED(rc))
3625 delete pMediumLockList;
3626 else
3627 {
3628 mData->mSession.mLockedMedia.Unlock();
3629 alock.release();
3630 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3631 mData->mSession.mLockedMedia.Lock();
3632 alock.acquire();
3633 }
3634 alock.release();
3635
3636 if (SUCCEEDED(rc))
3637 {
3638 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3639 /* Remove lock list in case of error. */
3640 if (FAILED(rc))
3641 {
3642 mData->mSession.mLockedMedia.Unlock();
3643 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3644 mData->mSession.mLockedMedia.Lock();
3645 }
3646 }
3647 }
3648
3649 return S_OK;
3650 }
3651
3652 /* bus/channel/device differ; we need a new attachment object,
3653 * but don't try to associate it again */
3654 associate = false;
3655 break;
3656 }
3657 }
3658
3659 /* go further only if the attachment is to be indirect */
3660 if (!fIndirect)
3661 break;
3662
3663 /* perform the so called smart attachment logic for indirect
3664 * attachments. Note that smart attachment is only applicable to base
3665 * hard disks. */
3666
3667 if (medium->i_getParent().isNull())
3668 {
3669 /* first, investigate the backup copy of the current hard disk
3670 * attachments to make it possible to re-attach existing diffs to
3671 * another device slot w/o losing their contents */
3672 if (mMediumAttachments.isBackedUp())
3673 {
3674 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3675
3676 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3677 uint32_t foundLevel = 0;
3678
3679 for (MediumAttachmentList::const_iterator
3680 it = oldAtts.begin();
3681 it != oldAtts.end();
3682 ++it)
3683 {
3684 uint32_t level = 0;
3685 MediumAttachment *pAttach = *it;
3686 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3687 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3688 if (pMedium.isNull())
3689 continue;
3690
3691 if (pMedium->i_getBase(&level) == medium)
3692 {
3693 /* skip the hard disk if its currently attached (we
3694 * cannot attach the same hard disk twice) */
3695 if (i_findAttachment(*mMediumAttachments.data(),
3696 pMedium))
3697 continue;
3698
3699 /* matched device, channel and bus (i.e. attached to the
3700 * same place) will win and immediately stop the search;
3701 * otherwise the attachment that has the youngest
3702 * descendant of medium will be used
3703 */
3704 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3705 {
3706 /* the simplest case: restore the whole attachment
3707 * and return, nothing else to do */
3708 mMediumAttachments->push_back(*it);
3709
3710 /* Reattach the medium to the VM. */
3711 if (fHotplug || fSilent)
3712 {
3713 mediumLock.release();
3714 treeLock.release();
3715 alock.release();
3716
3717 MediumLockList *pMediumLockList(new MediumLockList());
3718
3719 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3720 medium /* pToLockWrite */,
3721 false /* fMediumLockWriteAll */,
3722 NULL,
3723 *pMediumLockList);
3724 alock.acquire();
3725 if (FAILED(rc))
3726 delete pMediumLockList;
3727 else
3728 {
3729 mData->mSession.mLockedMedia.Unlock();
3730 alock.release();
3731 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3732 mData->mSession.mLockedMedia.Lock();
3733 alock.acquire();
3734 }
3735 alock.release();
3736
3737 if (SUCCEEDED(rc))
3738 {
3739 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3740 /* Remove lock list in case of error. */
3741 if (FAILED(rc))
3742 {
3743 mData->mSession.mLockedMedia.Unlock();
3744 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3745 mData->mSession.mLockedMedia.Lock();
3746 }
3747 }
3748 }
3749
3750 return S_OK;
3751 }
3752 else if ( foundIt == oldAtts.end()
3753 || level > foundLevel /* prefer younger */
3754 )
3755 {
3756 foundIt = it;
3757 foundLevel = level;
3758 }
3759 }
3760 }
3761
3762 if (foundIt != oldAtts.end())
3763 {
3764 /* use the previously attached hard disk */
3765 medium = (*foundIt)->i_getMedium();
3766 mediumCaller.attach(medium);
3767 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3768 mediumLock.attach(medium);
3769 /* not implicit, doesn't require association with this VM */
3770 fIndirect = false;
3771 associate = false;
3772 /* go right to the MediumAttachment creation */
3773 break;
3774 }
3775 }
3776
3777 /* must give up the medium lock and medium tree lock as below we
3778 * go over snapshots, which needs a lock with higher lock order. */
3779 mediumLock.release();
3780 treeLock.release();
3781
3782 /* then, search through snapshots for the best diff in the given
3783 * hard disk's chain to base the new diff on */
3784
3785 ComObjPtr<Medium> base;
3786 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3787 while (snap)
3788 {
3789 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3790
3791 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3792
3793 MediumAttachment *pAttachFound = NULL;
3794 uint32_t foundLevel = 0;
3795
3796 for (MediumAttachmentList::const_iterator
3797 it = snapAtts.begin();
3798 it != snapAtts.end();
3799 ++it)
3800 {
3801 MediumAttachment *pAttach = *it;
3802 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3803 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3804 if (pMedium.isNull())
3805 continue;
3806
3807 uint32_t level = 0;
3808 if (pMedium->i_getBase(&level) == medium)
3809 {
3810 /* matched device, channel and bus (i.e. attached to the
3811 * same place) will win and immediately stop the search;
3812 * otherwise the attachment that has the youngest
3813 * descendant of medium will be used
3814 */
3815 if ( pAttach->i_getDevice() == aDevice
3816 && pAttach->i_getPort() == aControllerPort
3817 && pAttach->i_getControllerName() == aName
3818 )
3819 {
3820 pAttachFound = pAttach;
3821 break;
3822 }
3823 else if ( !pAttachFound
3824 || level > foundLevel /* prefer younger */
3825 )
3826 {
3827 pAttachFound = pAttach;
3828 foundLevel = level;
3829 }
3830 }
3831 }
3832
3833 if (pAttachFound)
3834 {
3835 base = pAttachFound->i_getMedium();
3836 break;
3837 }
3838
3839 snap = snap->i_getParent();
3840 }
3841
3842 /* re-lock medium tree and the medium, as we need it below */
3843 treeLock.acquire();
3844 mediumLock.acquire();
3845
3846 /* found a suitable diff, use it as a base */
3847 if (!base.isNull())
3848 {
3849 medium = base;
3850 mediumCaller.attach(medium);
3851 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3852 mediumLock.attach(medium);
3853 }
3854 }
3855
3856 Utf8Str strFullSnapshotFolder;
3857 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3858
3859 ComObjPtr<Medium> diff;
3860 diff.createObject();
3861 // store this diff in the same registry as the parent
3862 Guid uuidRegistryParent;
3863 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3864 {
3865 // parent image has no registry: this can happen if we're attaching a new immutable
3866 // image that has not yet been attached (medium then points to the base and we're
3867 // creating the diff image for the immutable, and the parent is not yet registered);
3868 // put the parent in the machine registry then
3869 mediumLock.release();
3870 treeLock.release();
3871 alock.release();
3872 i_addMediumToRegistry(medium);
3873 alock.acquire();
3874 treeLock.acquire();
3875 mediumLock.acquire();
3876 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3877 }
3878 rc = diff->init(mParent,
3879 medium->i_getPreferredDiffFormat(),
3880 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3881 uuidRegistryParent,
3882 DeviceType_HardDisk);
3883 if (FAILED(rc)) return rc;
3884
3885 /* Apply the normal locking logic to the entire chain. */
3886 MediumLockList *pMediumLockList(new MediumLockList());
3887 mediumLock.release();
3888 treeLock.release();
3889 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3890 diff /* pToLockWrite */,
3891 false /* fMediumLockWriteAll */,
3892 medium,
3893 *pMediumLockList);
3894 treeLock.acquire();
3895 mediumLock.acquire();
3896 if (SUCCEEDED(rc))
3897 {
3898 mediumLock.release();
3899 treeLock.release();
3900 rc = pMediumLockList->Lock();
3901 treeLock.acquire();
3902 mediumLock.acquire();
3903 if (FAILED(rc))
3904 setError(rc,
3905 tr("Could not lock medium when creating diff '%s'"),
3906 diff->i_getLocationFull().c_str());
3907 else
3908 {
3909 /* will release the lock before the potentially lengthy
3910 * operation, so protect with the special state */
3911 MachineState_T oldState = mData->mMachineState;
3912 i_setMachineState(MachineState_SettingUp);
3913
3914 mediumLock.release();
3915 treeLock.release();
3916 alock.release();
3917
3918 rc = medium->i_createDiffStorage(diff,
3919 medium->i_getPreferredDiffVariant(),
3920 pMediumLockList,
3921 NULL /* aProgress */,
3922 true /* aWait */,
3923 false /* aNotify */);
3924
3925 alock.acquire();
3926 treeLock.acquire();
3927 mediumLock.acquire();
3928
3929 i_setMachineState(oldState);
3930 }
3931 }
3932
3933 /* Unlock the media and free the associated memory. */
3934 delete pMediumLockList;
3935
3936 if (FAILED(rc)) return rc;
3937
3938 /* use the created diff for the actual attachment */
3939 medium = diff;
3940 mediumCaller.attach(medium);
3941 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3942 mediumLock.attach(medium);
3943 }
3944 while (0);
3945
3946 ComObjPtr<MediumAttachment> attachment;
3947 attachment.createObject();
3948 rc = attachment->init(this,
3949 medium,
3950 aName,
3951 aControllerPort,
3952 aDevice,
3953 aType,
3954 fIndirect,
3955 false /* fPassthrough */,
3956 false /* fTempEject */,
3957 false /* fNonRotational */,
3958 false /* fDiscard */,
3959 fHotplug /* fHotPluggable */,
3960 Utf8Str::Empty);
3961 if (FAILED(rc)) return rc;
3962
3963 if (associate && !medium.isNull())
3964 {
3965 // as the last step, associate the medium to the VM
3966 rc = medium->i_addBackReference(mData->mUuid);
3967 // here we can fail because of Deleting, or being in process of creating a Diff
3968 if (FAILED(rc)) return rc;
3969
3970 mediumLock.release();
3971 treeLock.release();
3972 alock.release();
3973 i_addMediumToRegistry(medium);
3974 alock.acquire();
3975 treeLock.acquire();
3976 mediumLock.acquire();
3977 }
3978
3979 /* success: finally remember the attachment */
3980 i_setModified(IsModified_Storage);
3981 mMediumAttachments.backup();
3982 mMediumAttachments->push_back(attachment);
3983
3984 mediumLock.release();
3985 treeLock.release();
3986 alock.release();
3987
3988 if (fHotplug || fSilent)
3989 {
3990 if (!medium.isNull())
3991 {
3992 MediumLockList *pMediumLockList(new MediumLockList());
3993
3994 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3995 medium /* pToLockWrite */,
3996 false /* fMediumLockWriteAll */,
3997 NULL,
3998 *pMediumLockList);
3999 alock.acquire();
4000 if (FAILED(rc))
4001 delete pMediumLockList;
4002 else
4003 {
4004 mData->mSession.mLockedMedia.Unlock();
4005 alock.release();
4006 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4007 mData->mSession.mLockedMedia.Lock();
4008 alock.acquire();
4009 }
4010 alock.release();
4011 }
4012
4013 if (SUCCEEDED(rc))
4014 {
4015 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4016 /* Remove lock list in case of error. */
4017 if (FAILED(rc))
4018 {
4019 mData->mSession.mLockedMedia.Unlock();
4020 mData->mSession.mLockedMedia.Remove(attachment);
4021 mData->mSession.mLockedMedia.Lock();
4022 }
4023 }
4024 }
4025
4026 /* Save modified registries, but skip this machine as it's the caller's
4027 * job to save its settings like all other settings changes. */
4028 mParent->i_unmarkRegistryModified(i_getId());
4029 mParent->i_saveModifiedRegistries();
4030
4031 if (SUCCEEDED(rc))
4032 {
4033 if (fIndirect && medium != aM)
4034 mParent->i_onMediumConfigChanged(medium);
4035 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4036 }
4037
4038 return rc;
4039}
4040
4041HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4042 LONG aDevice)
4043{
4044 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4045 aName.c_str(), aControllerPort, aDevice));
4046
4047 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4048
4049 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4050 if (FAILED(rc)) return rc;
4051
4052 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4053
4054 /* Check for an existing controller. */
4055 ComObjPtr<StorageController> ctl;
4056 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4057 if (FAILED(rc)) return rc;
4058
4059 StorageControllerType_T ctrlType;
4060 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4061 if (FAILED(rc))
4062 return setError(E_FAIL,
4063 tr("Could not get type of controller '%s'"),
4064 aName.c_str());
4065
4066 bool fSilent = false;
4067 Utf8Str strReconfig;
4068
4069 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4070 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4071 if ( mData->mMachineState == MachineState_Paused
4072 && strReconfig == "1")
4073 fSilent = true;
4074
4075 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4076 bool fHotplug = false;
4077 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4078 fHotplug = true;
4079
4080 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4081 return setError(VBOX_E_INVALID_VM_STATE,
4082 tr("Controller '%s' does not support hotplugging"),
4083 aName.c_str());
4084
4085 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4086 aName,
4087 aControllerPort,
4088 aDevice);
4089 if (!pAttach)
4090 return setError(VBOX_E_OBJECT_NOT_FOUND,
4091 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4092 aDevice, aControllerPort, aName.c_str());
4093
4094 if (fHotplug && !pAttach->i_getHotPluggable())
4095 return setError(VBOX_E_NOT_SUPPORTED,
4096 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4097 aDevice, aControllerPort, aName.c_str());
4098
4099 /*
4100 * The VM has to detach the device before we delete any implicit diffs.
4101 * If this fails we can roll back without loosing data.
4102 */
4103 if (fHotplug || fSilent)
4104 {
4105 alock.release();
4106 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4107 alock.acquire();
4108 }
4109 if (FAILED(rc)) return rc;
4110
4111 /* If we are here everything went well and we can delete the implicit now. */
4112 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4113
4114 alock.release();
4115
4116 /* Save modified registries, but skip this machine as it's the caller's
4117 * job to save its settings like all other settings changes. */
4118 mParent->i_unmarkRegistryModified(i_getId());
4119 mParent->i_saveModifiedRegistries();
4120
4121 if (SUCCEEDED(rc))
4122 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4123
4124 return rc;
4125}
4126
4127HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4128 LONG aDevice, BOOL aPassthrough)
4129{
4130 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4131 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4132
4133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4134
4135 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4136 if (FAILED(rc)) return rc;
4137
4138 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4139
4140 /* Check for an existing controller. */
4141 ComObjPtr<StorageController> ctl;
4142 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4143 if (FAILED(rc)) return rc;
4144
4145 StorageControllerType_T ctrlType;
4146 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4147 if (FAILED(rc))
4148 return setError(E_FAIL,
4149 tr("Could not get type of controller '%s'"),
4150 aName.c_str());
4151
4152 bool fSilent = false;
4153 Utf8Str strReconfig;
4154
4155 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4156 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4157 if ( mData->mMachineState == MachineState_Paused
4158 && strReconfig == "1")
4159 fSilent = true;
4160
4161 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4162 bool fHotplug = false;
4163 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4164 fHotplug = true;
4165
4166 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4167 return setError(VBOX_E_INVALID_VM_STATE,
4168 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4169 aName.c_str());
4170
4171 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4172 aName,
4173 aControllerPort,
4174 aDevice);
4175 if (!pAttach)
4176 return setError(VBOX_E_OBJECT_NOT_FOUND,
4177 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4178 aDevice, aControllerPort, aName.c_str());
4179
4180
4181 i_setModified(IsModified_Storage);
4182 mMediumAttachments.backup();
4183
4184 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4185
4186 if (pAttach->i_getType() != DeviceType_DVD)
4187 return setError(E_INVALIDARG,
4188 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4189 aDevice, aControllerPort, aName.c_str());
4190
4191 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4192
4193 pAttach->i_updatePassthrough(!!aPassthrough);
4194
4195 attLock.release();
4196 alock.release();
4197 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4198 if (SUCCEEDED(rc) && fValueChanged)
4199 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4200
4201 return rc;
4202}
4203
4204HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4205 LONG aDevice, BOOL aTemporaryEject)
4206{
4207
4208 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4209 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4210
4211 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4212
4213 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4214 if (FAILED(rc)) return rc;
4215
4216 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4217 aName,
4218 aControllerPort,
4219 aDevice);
4220 if (!pAttach)
4221 return setError(VBOX_E_OBJECT_NOT_FOUND,
4222 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4223 aDevice, aControllerPort, aName.c_str());
4224
4225
4226 i_setModified(IsModified_Storage);
4227 mMediumAttachments.backup();
4228
4229 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4230
4231 if (pAttach->i_getType() != DeviceType_DVD)
4232 return setError(E_INVALIDARG,
4233 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4234 aDevice, aControllerPort, aName.c_str());
4235 pAttach->i_updateTempEject(!!aTemporaryEject);
4236
4237 return S_OK;
4238}
4239
4240HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4241 LONG aDevice, BOOL aNonRotational)
4242{
4243
4244 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4245 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4246
4247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4248
4249 HRESULT rc = i_checkStateDependency(MutableStateDep);
4250 if (FAILED(rc)) return rc;
4251
4252 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4253
4254 if (Global::IsOnlineOrTransient(mData->mMachineState))
4255 return setError(VBOX_E_INVALID_VM_STATE,
4256 tr("Invalid machine state: %s"),
4257 Global::stringifyMachineState(mData->mMachineState));
4258
4259 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4260 aName,
4261 aControllerPort,
4262 aDevice);
4263 if (!pAttach)
4264 return setError(VBOX_E_OBJECT_NOT_FOUND,
4265 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4266 aDevice, aControllerPort, aName.c_str());
4267
4268
4269 i_setModified(IsModified_Storage);
4270 mMediumAttachments.backup();
4271
4272 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4273
4274 if (pAttach->i_getType() != DeviceType_HardDisk)
4275 return setError(E_INVALIDARG,
4276 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"),
4277 aDevice, aControllerPort, aName.c_str());
4278 pAttach->i_updateNonRotational(!!aNonRotational);
4279
4280 return S_OK;
4281}
4282
4283HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4284 LONG aDevice, BOOL aDiscard)
4285{
4286
4287 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4288 aName.c_str(), aControllerPort, aDevice, aDiscard));
4289
4290 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4291
4292 HRESULT rc = i_checkStateDependency(MutableStateDep);
4293 if (FAILED(rc)) return rc;
4294
4295 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4296
4297 if (Global::IsOnlineOrTransient(mData->mMachineState))
4298 return setError(VBOX_E_INVALID_VM_STATE,
4299 tr("Invalid machine state: %s"),
4300 Global::stringifyMachineState(mData->mMachineState));
4301
4302 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4303 aName,
4304 aControllerPort,
4305 aDevice);
4306 if (!pAttach)
4307 return setError(VBOX_E_OBJECT_NOT_FOUND,
4308 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4309 aDevice, aControllerPort, aName.c_str());
4310
4311
4312 i_setModified(IsModified_Storage);
4313 mMediumAttachments.backup();
4314
4315 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4316
4317 if (pAttach->i_getType() != DeviceType_HardDisk)
4318 return setError(E_INVALIDARG,
4319 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"),
4320 aDevice, aControllerPort, aName.c_str());
4321 pAttach->i_updateDiscard(!!aDiscard);
4322
4323 return S_OK;
4324}
4325
4326HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4327 LONG aDevice, BOOL aHotPluggable)
4328{
4329 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4330 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4331
4332 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4333
4334 HRESULT rc = i_checkStateDependency(MutableStateDep);
4335 if (FAILED(rc)) return rc;
4336
4337 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4338
4339 if (Global::IsOnlineOrTransient(mData->mMachineState))
4340 return setError(VBOX_E_INVALID_VM_STATE,
4341 tr("Invalid machine state: %s"),
4342 Global::stringifyMachineState(mData->mMachineState));
4343
4344 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4345 aName,
4346 aControllerPort,
4347 aDevice);
4348 if (!pAttach)
4349 return setError(VBOX_E_OBJECT_NOT_FOUND,
4350 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4351 aDevice, aControllerPort, aName.c_str());
4352
4353 /* Check for an existing controller. */
4354 ComObjPtr<StorageController> ctl;
4355 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4356 if (FAILED(rc)) return rc;
4357
4358 StorageControllerType_T ctrlType;
4359 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4360 if (FAILED(rc))
4361 return setError(E_FAIL,
4362 tr("Could not get type of controller '%s'"),
4363 aName.c_str());
4364
4365 if (!i_isControllerHotplugCapable(ctrlType))
4366 return setError(VBOX_E_NOT_SUPPORTED,
4367 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4368 aName.c_str());
4369
4370 i_setModified(IsModified_Storage);
4371 mMediumAttachments.backup();
4372
4373 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4374
4375 if (pAttach->i_getType() == DeviceType_Floppy)
4376 return setError(E_INVALIDARG,
4377 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"),
4378 aDevice, aControllerPort, aName.c_str());
4379 pAttach->i_updateHotPluggable(!!aHotPluggable);
4380
4381 return S_OK;
4382}
4383
4384HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4385 LONG aDevice)
4386{
4387 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4388 aName.c_str(), aControllerPort, aDevice));
4389
4390 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4391}
4392
4393HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4394 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4395{
4396 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4397 aName.c_str(), aControllerPort, aDevice));
4398
4399 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4400
4401 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4402 if (FAILED(rc)) return rc;
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4410 aName,
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418
4419 i_setModified(IsModified_Storage);
4420 mMediumAttachments.backup();
4421
4422 IBandwidthGroup *iB = aBandwidthGroup;
4423 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4424 if (aBandwidthGroup && group.isNull())
4425 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4426
4427 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4428
4429 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4430 if (strBandwidthGroupOld.isNotEmpty())
4431 {
4432 /* Get the bandwidth group object and release it - this must not fail. */
4433 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4434 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4435 Assert(SUCCEEDED(rc));
4436
4437 pBandwidthGroupOld->i_release();
4438 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4439 }
4440
4441 if (!group.isNull())
4442 {
4443 group->i_reference();
4444 pAttach->i_updateBandwidthGroup(group->i_getName());
4445 }
4446
4447 return S_OK;
4448}
4449
4450HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4451 LONG aControllerPort,
4452 LONG aDevice,
4453 DeviceType_T aType)
4454{
4455 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4456 aName.c_str(), aControllerPort, aDevice, aType));
4457
4458 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4459}
4460
4461
4462HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4463 LONG aControllerPort,
4464 LONG aDevice,
4465 BOOL aForce)
4466{
4467 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4468 aName.c_str(), aControllerPort, aForce));
4469
4470 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4471}
4472
4473HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4474 LONG aControllerPort,
4475 LONG aDevice,
4476 const ComPtr<IMedium> &aMedium,
4477 BOOL aForce)
4478{
4479 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4480 aName.c_str(), aControllerPort, aDevice, aForce));
4481
4482 // request the host lock first, since might be calling Host methods for getting host drives;
4483 // next, protect the media tree all the while we're in here, as well as our member variables
4484 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4485 this->lockHandle(),
4486 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4487
4488 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4489 aName,
4490 aControllerPort,
4491 aDevice);
4492 if (pAttach.isNull())
4493 return setError(VBOX_E_OBJECT_NOT_FOUND,
4494 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4495 aDevice, aControllerPort, aName.c_str());
4496
4497 /* Remember previously mounted medium. The medium before taking the
4498 * backup is not necessarily the same thing. */
4499 ComObjPtr<Medium> oldmedium;
4500 oldmedium = pAttach->i_getMedium();
4501
4502 IMedium *iM = aMedium;
4503 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4504 if (aMedium && pMedium.isNull())
4505 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4506
4507 AutoCaller mediumCaller(pMedium);
4508 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4509
4510 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4511 if (pMedium)
4512 {
4513 DeviceType_T mediumType = pAttach->i_getType();
4514 switch (mediumType)
4515 {
4516 case DeviceType_DVD:
4517 case DeviceType_Floppy:
4518 break;
4519
4520 default:
4521 return setError(VBOX_E_INVALID_OBJECT_STATE,
4522 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4523 aControllerPort,
4524 aDevice,
4525 aName.c_str());
4526 }
4527 }
4528
4529 i_setModified(IsModified_Storage);
4530 mMediumAttachments.backup();
4531
4532 {
4533 // The backup operation makes the pAttach reference point to the
4534 // old settings. Re-get the correct reference.
4535 pAttach = i_findAttachment(*mMediumAttachments.data(),
4536 aName,
4537 aControllerPort,
4538 aDevice);
4539 if (!oldmedium.isNull())
4540 oldmedium->i_removeBackReference(mData->mUuid);
4541 if (!pMedium.isNull())
4542 {
4543 pMedium->i_addBackReference(mData->mUuid);
4544
4545 mediumLock.release();
4546 multiLock.release();
4547 i_addMediumToRegistry(pMedium);
4548 multiLock.acquire();
4549 mediumLock.acquire();
4550 }
4551
4552 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4553 pAttach->i_updateMedium(pMedium);
4554 }
4555
4556 i_setModified(IsModified_Storage);
4557
4558 mediumLock.release();
4559 multiLock.release();
4560 HRESULT rc = i_onMediumChange(pAttach, aForce);
4561 multiLock.acquire();
4562 mediumLock.acquire();
4563
4564 /* On error roll back this change only. */
4565 if (FAILED(rc))
4566 {
4567 if (!pMedium.isNull())
4568 pMedium->i_removeBackReference(mData->mUuid);
4569 pAttach = i_findAttachment(*mMediumAttachments.data(),
4570 aName,
4571 aControllerPort,
4572 aDevice);
4573 /* If the attachment is gone in the meantime, bail out. */
4574 if (pAttach.isNull())
4575 return rc;
4576 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4577 if (!oldmedium.isNull())
4578 oldmedium->i_addBackReference(mData->mUuid);
4579 pAttach->i_updateMedium(oldmedium);
4580 }
4581
4582 mediumLock.release();
4583 multiLock.release();
4584
4585 /* Save modified registries, but skip this machine as it's the caller's
4586 * job to save its settings like all other settings changes. */
4587 mParent->i_unmarkRegistryModified(i_getId());
4588 mParent->i_saveModifiedRegistries();
4589
4590 return rc;
4591}
4592HRESULT Machine::getMedium(const com::Utf8Str &aName,
4593 LONG aControllerPort,
4594 LONG aDevice,
4595 ComPtr<IMedium> &aMedium)
4596{
4597 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4598 aName.c_str(), aControllerPort, aDevice));
4599
4600 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4601
4602 aMedium = NULL;
4603
4604 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4605 aName,
4606 aControllerPort,
4607 aDevice);
4608 if (pAttach.isNull())
4609 return setError(VBOX_E_OBJECT_NOT_FOUND,
4610 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4611 aDevice, aControllerPort, aName.c_str());
4612
4613 aMedium = pAttach->i_getMedium();
4614
4615 return S_OK;
4616}
4617
4618HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4619{
4620 if (aSlot < RT_ELEMENTS(mSerialPorts))
4621 {
4622 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4623 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4624 return S_OK;
4625 }
4626 return setError(E_INVALIDARG, tr("Serial port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mSerialPorts));
4627}
4628
4629HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4630{
4631 if (aSlot < RT_ELEMENTS(mParallelPorts))
4632 {
4633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4634 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4635 return S_OK;
4636 }
4637 return setError(E_INVALIDARG, tr("Parallel port slot %RU32 is out of bounds (max %zu)"), aSlot, RT_ELEMENTS(mParallelPorts));
4638}
4639
4640
4641HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4642{
4643 /* Do not assert if slot is out of range, just return the advertised
4644 status. testdriver/vbox.py triggers this in logVmInfo. */
4645 if (aSlot >= mNetworkAdapters.size())
4646 return setError(E_INVALIDARG,
4647 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4648 aSlot, mNetworkAdapters.size());
4649
4650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4651
4652 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4653
4654 return S_OK;
4655}
4656
4657HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4658{
4659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4660
4661 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4662 size_t i = 0;
4663 for (settings::StringsMap::const_iterator
4664 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4665 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4666 ++it, ++i)
4667 aKeys[i] = it->first;
4668
4669 return S_OK;
4670}
4671
4672 /**
4673 * @note Locks this object for reading.
4674 */
4675HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4676 com::Utf8Str &aValue)
4677{
4678 /* start with nothing found */
4679 aValue = "";
4680
4681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4682
4683 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4684 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4685 // found:
4686 aValue = it->second; // source is a Utf8Str
4687
4688 /* return the result to caller (may be empty) */
4689 return S_OK;
4690}
4691
4692 /**
4693 * @note Locks mParent for writing + this object for writing.
4694 */
4695HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4696{
4697 /* Because control characters in aKey have caused problems in the settings
4698 * they are rejected unless the key should be deleted. */
4699 if (!aValue.isEmpty())
4700 {
4701 for (size_t i = 0; i < aKey.length(); ++i)
4702 {
4703 char ch = aKey[i];
4704 if (RTLocCIsCntrl(ch))
4705 return E_INVALIDARG;
4706 }
4707 }
4708
4709 Utf8Str strOldValue; // empty
4710
4711 // locking note: we only hold the read lock briefly to look up the old value,
4712 // then release it and call the onExtraCanChange callbacks. There is a small
4713 // chance of a race insofar as the callback might be called twice if two callers
4714 // change the same key at the same time, but that's a much better solution
4715 // than the deadlock we had here before. The actual changing of the extradata
4716 // is then performed under the write lock and race-free.
4717
4718 // look up the old value first; if nothing has changed then we need not do anything
4719 {
4720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4721
4722 // For snapshots don't even think about allowing changes, extradata
4723 // is global for a machine, so there is nothing snapshot specific.
4724 if (i_isSnapshotMachine())
4725 return setError(VBOX_E_INVALID_VM_STATE,
4726 tr("Cannot set extradata for a snapshot"));
4727
4728 // check if the right IMachine instance is used
4729 if (mData->mRegistered && !i_isSessionMachine())
4730 return setError(VBOX_E_INVALID_VM_STATE,
4731 tr("Cannot set extradata for an immutable machine"));
4732
4733 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4734 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4735 strOldValue = it->second;
4736 }
4737
4738 bool fChanged;
4739 if ((fChanged = (strOldValue != aValue)))
4740 {
4741 // ask for permission from all listeners outside the locks;
4742 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4743 // lock to copy the list of callbacks to invoke
4744 Bstr bstrError;
4745 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4746 {
4747 const char *sep = bstrError.isEmpty() ? "" : ": ";
4748 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4749 return setError(E_ACCESSDENIED,
4750 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4751 aKey.c_str(),
4752 aValue.c_str(),
4753 sep,
4754 bstrError.raw());
4755 }
4756
4757 // data is changing and change not vetoed: then write it out under the lock
4758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4759
4760 if (aValue.isEmpty())
4761 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4762 else
4763 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4764 // creates a new key if needed
4765
4766 bool fNeedsGlobalSaveSettings = false;
4767 // This saving of settings is tricky: there is no "old state" for the
4768 // extradata items at all (unlike all other settings), so the old/new
4769 // settings comparison would give a wrong result!
4770 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4771
4772 if (fNeedsGlobalSaveSettings)
4773 {
4774 // save the global settings; for that we should hold only the VirtualBox lock
4775 alock.release();
4776 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4777 mParent->i_saveSettings();
4778 }
4779 }
4780
4781 // fire notification outside the lock
4782 if (fChanged)
4783 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4784
4785 return S_OK;
4786}
4787
4788HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4789{
4790 aProgress = NULL;
4791 NOREF(aSettingsFilePath);
4792 ReturnComNotImplemented();
4793}
4794
4795HRESULT Machine::saveSettings()
4796{
4797 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4798
4799 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4800 if (FAILED(rc)) return rc;
4801
4802 /* the settings file path may never be null */
4803 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4804
4805 /* save all VM data excluding snapshots */
4806 bool fNeedsGlobalSaveSettings = false;
4807 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4808 mlock.release();
4809
4810 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4811 {
4812 // save the global settings; for that we should hold only the VirtualBox lock
4813 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4814 rc = mParent->i_saveSettings();
4815 }
4816
4817 return rc;
4818}
4819
4820
4821HRESULT Machine::discardSettings()
4822{
4823 /*
4824 * We need to take the machine list lock here as well as the machine one
4825 * or we'll get into trouble should any media stuff require rolling back.
4826 *
4827 * Details:
4828 *
4829 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4830 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4831 * 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]
4832 * 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
4833 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4834 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4835 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4836 * 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
4837 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4838 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4839 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4840 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4841 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4842 * 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]
4843 * 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] (*)
4844 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4845 * 0:005> k
4846 * # Child-SP RetAddr Call Site
4847 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4848 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4849 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4850 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4851 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4852 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4853 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4854 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4855 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4856 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4857 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4858 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4859 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4860 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4861 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4862 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4863 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4864 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4865 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4866 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4867 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4868 *
4869 */
4870 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4871 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /*
4877 * during this rollback, the session will be notified if data has
4878 * been actually changed
4879 */
4880 i_rollback(true /* aNotify */);
4881
4882 return S_OK;
4883}
4884
4885/** @note Locks objects! */
4886HRESULT Machine::unregister(AutoCaller &autoCaller,
4887 CleanupMode_T aCleanupMode,
4888 std::vector<ComPtr<IMedium> > &aMedia)
4889{
4890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4891
4892 Guid id(i_getId());
4893
4894 if (mData->mSession.mState != SessionState_Unlocked)
4895 return setError(VBOX_E_INVALID_OBJECT_STATE,
4896 tr("Cannot unregister the machine '%s' while it is locked"),
4897 mUserData->s.strName.c_str());
4898
4899 // wait for state dependents to drop to zero
4900 i_ensureNoStateDependencies();
4901
4902 if (!mData->mAccessible)
4903 {
4904 // inaccessible machines can only be unregistered; uninitialize ourselves
4905 // here because currently there may be no unregistered that are inaccessible
4906 // (this state combination is not supported). Note releasing the caller and
4907 // leaving the lock before calling uninit()
4908 alock.release();
4909 autoCaller.release();
4910
4911 uninit();
4912
4913 mParent->i_unregisterMachine(this, id);
4914 // calls VirtualBox::i_saveSettings()
4915
4916 return S_OK;
4917 }
4918
4919 HRESULT rc = S_OK;
4920 mData->llFilesToDelete.clear();
4921
4922 if (!mSSData->strStateFilePath.isEmpty())
4923 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4924
4925 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4926 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4927 mData->llFilesToDelete.push_back(strNVRAMFile);
4928
4929 // This list collects the medium objects from all medium attachments
4930 // which we will detach from the machine and its snapshots, in a specific
4931 // order which allows for closing all media without getting "media in use"
4932 // errors, simply by going through the list from the front to the back:
4933 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4934 // and must be closed before the parent media from the snapshots, or closing the parents
4935 // will fail because they still have children);
4936 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4937 // the root ("first") snapshot of the machine.
4938 MediaList llMedia;
4939
4940 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4941 && mMediumAttachments->size()
4942 )
4943 {
4944 // we have media attachments: detach them all and add the Medium objects to our list
4945 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4946 }
4947
4948 if (mData->mFirstSnapshot)
4949 {
4950 // add the media from the medium attachments of the snapshots to llMedia
4951 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4952 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4953 // into the children first
4954
4955 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4956 MachineState_T oldState = mData->mMachineState;
4957 mData->mMachineState = MachineState_DeletingSnapshot;
4958
4959 // make a copy of the first snapshot reference so the refcount does not
4960 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4961 // (would hang due to the AutoCaller voodoo)
4962 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4963
4964 // GO!
4965 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4966
4967 mData->mMachineState = oldState;
4968 }
4969
4970 if (FAILED(rc))
4971 {
4972 i_rollbackMedia();
4973 return rc;
4974 }
4975
4976 // commit all the media changes made above
4977 i_commitMedia();
4978
4979 mData->mRegistered = false;
4980
4981 // machine lock no longer needed
4982 alock.release();
4983
4984 /* Make sure that the settings of the current VM are not saved, because
4985 * they are rather crippled at this point to meet the cleanup expectations
4986 * and there's no point destroying the VM config on disk just because. */
4987 mParent->i_unmarkRegistryModified(id);
4988
4989 // return media to caller
4990 aMedia.resize(llMedia.size());
4991 size_t i = 0;
4992 for (MediaList::const_iterator
4993 it = llMedia.begin();
4994 it != llMedia.end();
4995 ++it, ++i)
4996 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4997
4998 mParent->i_unregisterMachine(this, id);
4999 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5000
5001 return S_OK;
5002}
5003
5004/**
5005 * Task record for deleting a machine config.
5006 */
5007class Machine::DeleteConfigTask
5008 : public Machine::Task
5009{
5010public:
5011 DeleteConfigTask(Machine *m,
5012 Progress *p,
5013 const Utf8Str &t,
5014 const RTCList<ComPtr<IMedium> > &llMediums,
5015 const StringsList &llFilesToDelete)
5016 : Task(m, p, t),
5017 m_llMediums(llMediums),
5018 m_llFilesToDelete(llFilesToDelete)
5019 {}
5020
5021private:
5022 void handler()
5023 {
5024 try
5025 {
5026 m_pMachine->i_deleteConfigHandler(*this);
5027 }
5028 catch (...)
5029 {
5030 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5031 }
5032 }
5033
5034 RTCList<ComPtr<IMedium> > m_llMediums;
5035 StringsList m_llFilesToDelete;
5036
5037 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5038};
5039
5040/**
5041 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5042 * SessionMachine::taskHandler().
5043 *
5044 * @note Locks this object for writing.
5045 *
5046 * @param task
5047 * @return
5048 */
5049void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5050{
5051 LogFlowThisFuncEnter();
5052
5053 AutoCaller autoCaller(this);
5054 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5055 if (FAILED(autoCaller.rc()))
5056 {
5057 /* we might have been uninitialized because the session was accidentally
5058 * closed by the client, so don't assert */
5059 HRESULT rc = setError(E_FAIL,
5060 tr("The session has been accidentally closed"));
5061 task.m_pProgress->i_notifyComplete(rc);
5062 LogFlowThisFuncLeave();
5063 return;
5064 }
5065
5066 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5067
5068 HRESULT rc = S_OK;
5069
5070 try
5071 {
5072 ULONG uLogHistoryCount = 3;
5073 ComPtr<ISystemProperties> systemProperties;
5074 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5075 if (FAILED(rc)) throw rc;
5076
5077 if (!systemProperties.isNull())
5078 {
5079 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5080 if (FAILED(rc)) throw rc;
5081 }
5082
5083 MachineState_T oldState = mData->mMachineState;
5084 i_setMachineState(MachineState_SettingUp);
5085 alock.release();
5086 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5087 {
5088 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5089 {
5090 AutoCaller mac(pMedium);
5091 if (FAILED(mac.rc())) throw mac.rc();
5092 Utf8Str strLocation = pMedium->i_getLocationFull();
5093 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5094 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5095 if (FAILED(rc)) throw rc;
5096 }
5097 if (pMedium->i_isMediumFormatFile())
5098 {
5099 ComPtr<IProgress> pProgress2;
5100 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5101 if (FAILED(rc)) throw rc;
5102 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5103 if (FAILED(rc)) throw rc;
5104 }
5105
5106 /* Close the medium, deliberately without checking the return
5107 * code, and without leaving any trace in the error info, as
5108 * a failure here is a very minor issue, which shouldn't happen
5109 * as above we even managed to delete the medium. */
5110 {
5111 ErrorInfoKeeper eik;
5112 pMedium->Close();
5113 }
5114 }
5115 i_setMachineState(oldState);
5116 alock.acquire();
5117
5118 // delete the files pushed on the task list by Machine::Delete()
5119 // (this includes saved states of the machine and snapshots and
5120 // medium storage files from the IMedium list passed in, and the
5121 // machine XML file)
5122 for (StringsList::const_iterator
5123 it = task.m_llFilesToDelete.begin();
5124 it != task.m_llFilesToDelete.end();
5125 ++it)
5126 {
5127 const Utf8Str &strFile = *it;
5128 LogFunc(("Deleting file %s\n", strFile.c_str()));
5129 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5130 if (FAILED(rc)) throw rc;
5131
5132 int vrc = RTFileDelete(strFile.c_str());
5133 if (RT_FAILURE(vrc))
5134 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5135 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5136 }
5137
5138 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5139 if (FAILED(rc)) throw rc;
5140
5141 /* delete the settings only when the file actually exists */
5142 if (mData->pMachineConfigFile->fileExists())
5143 {
5144 /* Delete any backup or uncommitted XML files. Ignore failures.
5145 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5146 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5147 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5148 RTFileDelete(otherXml.c_str());
5149 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5150 RTFileDelete(otherXml.c_str());
5151
5152 /* delete the Logs folder, nothing important should be left
5153 * there (we don't check for errors because the user might have
5154 * some private files there that we don't want to delete) */
5155 Utf8Str logFolder;
5156 getLogFolder(logFolder);
5157 Assert(logFolder.length());
5158 if (RTDirExists(logFolder.c_str()))
5159 {
5160 /* Delete all VBox.log[.N] files from the Logs folder
5161 * (this must be in sync with the rotation logic in
5162 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5163 * files that may have been created by the GUI. */
5164 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5165 RTFileDelete(log.c_str());
5166 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5167 RTFileDelete(log.c_str());
5168 for (ULONG i = uLogHistoryCount; i > 0; i--)
5169 {
5170 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5171 RTFileDelete(log.c_str());
5172 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5173 RTFileDelete(log.c_str());
5174 }
5175#if defined(RT_OS_WINDOWS)
5176 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5177 RTFileDelete(log.c_str());
5178 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5179 RTFileDelete(log.c_str());
5180#endif
5181
5182 RTDirRemove(logFolder.c_str());
5183 }
5184
5185 /* delete the Snapshots folder, nothing important should be left
5186 * there (we don't check for errors because the user might have
5187 * some private files there that we don't want to delete) */
5188 Utf8Str strFullSnapshotFolder;
5189 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5190 Assert(!strFullSnapshotFolder.isEmpty());
5191 if (RTDirExists(strFullSnapshotFolder.c_str()))
5192 RTDirRemove(strFullSnapshotFolder.c_str());
5193
5194 // delete the directory that contains the settings file, but only
5195 // if it matches the VM name
5196 Utf8Str settingsDir;
5197 if (i_isInOwnDir(&settingsDir))
5198 RTDirRemove(settingsDir.c_str());
5199 }
5200
5201 alock.release();
5202
5203 mParent->i_saveModifiedRegistries();
5204 }
5205 catch (HRESULT aRC) { rc = aRC; }
5206
5207 task.m_pProgress->i_notifyComplete(rc);
5208
5209 LogFlowThisFuncLeave();
5210}
5211
5212HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5213{
5214 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5215
5216 HRESULT rc = i_checkStateDependency(MutableStateDep);
5217 if (FAILED(rc)) return rc;
5218
5219 if (mData->mRegistered)
5220 return setError(VBOX_E_INVALID_VM_STATE,
5221 tr("Cannot delete settings of a registered machine"));
5222
5223 // collect files to delete
5224 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5225 // machine config file
5226 if (mData->pMachineConfigFile->fileExists())
5227 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5228 // backup of machine config file
5229 Utf8Str strTmp(mData->m_strConfigFileFull);
5230 strTmp.append("-prev");
5231 if (RTFileExists(strTmp.c_str()))
5232 llFilesToDelete.push_back(strTmp);
5233
5234 RTCList<ComPtr<IMedium> > llMediums;
5235 for (size_t i = 0; i < aMedia.size(); ++i)
5236 {
5237 IMedium *pIMedium(aMedia[i]);
5238 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5239 if (pMedium.isNull())
5240 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5241 SafeArray<BSTR> ids;
5242 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5243 if (FAILED(rc)) return rc;
5244 /* At this point the medium should not have any back references
5245 * anymore. If it has it is attached to another VM and *must* not
5246 * deleted. */
5247 if (ids.size() < 1)
5248 llMediums.append(pMedium);
5249 }
5250
5251 ComObjPtr<Progress> pProgress;
5252 pProgress.createObject();
5253 rc = pProgress->init(i_getVirtualBox(),
5254 static_cast<IMachine*>(this) /* aInitiator */,
5255 tr("Deleting files"),
5256 true /* fCancellable */,
5257 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5258 tr("Collecting file inventory"));
5259 if (FAILED(rc))
5260 return rc;
5261
5262 /* create and start the task on a separate thread (note that it will not
5263 * start working until we release alock) */
5264 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5265 rc = pTask->createThread();
5266 pTask = NULL;
5267 if (FAILED(rc))
5268 return rc;
5269
5270 pProgress.queryInterfaceTo(aProgress.asOutParam());
5271
5272 LogFlowFuncLeave();
5273
5274 return S_OK;
5275}
5276
5277HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5278{
5279 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5280
5281 ComObjPtr<Snapshot> pSnapshot;
5282 HRESULT rc;
5283
5284 if (aNameOrId.isEmpty())
5285 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5286 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5287 else
5288 {
5289 Guid uuid(aNameOrId);
5290 if (uuid.isValid())
5291 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5292 else
5293 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5294 }
5295 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5296
5297 return rc;
5298}
5299
5300HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5301 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5302{
5303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5304
5305 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5306 if (FAILED(rc)) return rc;
5307
5308 ComObjPtr<SharedFolder> sharedFolder;
5309 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5310 if (SUCCEEDED(rc))
5311 return setError(VBOX_E_OBJECT_IN_USE,
5312 tr("Shared folder named '%s' already exists"),
5313 aName.c_str());
5314
5315 sharedFolder.createObject();
5316 rc = sharedFolder->init(i_getMachine(),
5317 aName,
5318 aHostPath,
5319 !!aWritable,
5320 !!aAutomount,
5321 aAutoMountPoint,
5322 true /* fFailOnError */);
5323 if (FAILED(rc)) return rc;
5324
5325 i_setModified(IsModified_SharedFolders);
5326 mHWData.backup();
5327 mHWData->mSharedFolders.push_back(sharedFolder);
5328
5329 /* inform the direct session if any */
5330 alock.release();
5331 i_onSharedFolderChange();
5332
5333 return S_OK;
5334}
5335
5336HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5337{
5338 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5339
5340 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5341 if (FAILED(rc)) return rc;
5342
5343 ComObjPtr<SharedFolder> sharedFolder;
5344 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5345 if (FAILED(rc)) return rc;
5346
5347 i_setModified(IsModified_SharedFolders);
5348 mHWData.backup();
5349 mHWData->mSharedFolders.remove(sharedFolder);
5350
5351 /* inform the direct session if any */
5352 alock.release();
5353 i_onSharedFolderChange();
5354
5355 return S_OK;
5356}
5357
5358HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5359{
5360 /* start with No */
5361 *aCanShow = FALSE;
5362
5363 ComPtr<IInternalSessionControl> directControl;
5364 {
5365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5366
5367 if (mData->mSession.mState != SessionState_Locked)
5368 return setError(VBOX_E_INVALID_VM_STATE,
5369 tr("Machine is not locked for session (session state: %s)"),
5370 Global::stringifySessionState(mData->mSession.mState));
5371
5372 if (mData->mSession.mLockType == LockType_VM)
5373 directControl = mData->mSession.mDirectControl;
5374 }
5375
5376 /* ignore calls made after #OnSessionEnd() is called */
5377 if (!directControl)
5378 return S_OK;
5379
5380 LONG64 dummy;
5381 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5382}
5383
5384HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5385{
5386 ComPtr<IInternalSessionControl> directControl;
5387 {
5388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5389
5390 if (mData->mSession.mState != SessionState_Locked)
5391 return setError(E_FAIL,
5392 tr("Machine is not locked for session (session state: %s)"),
5393 Global::stringifySessionState(mData->mSession.mState));
5394
5395 if (mData->mSession.mLockType == LockType_VM)
5396 directControl = mData->mSession.mDirectControl;
5397 }
5398
5399 /* ignore calls made after #OnSessionEnd() is called */
5400 if (!directControl)
5401 return S_OK;
5402
5403 BOOL dummy;
5404 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5405}
5406
5407#ifdef VBOX_WITH_GUEST_PROPS
5408/**
5409 * Look up a guest property in VBoxSVC's internal structures.
5410 */
5411HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5412 com::Utf8Str &aValue,
5413 LONG64 *aTimestamp,
5414 com::Utf8Str &aFlags) const
5415{
5416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5417
5418 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5419 if (it != mHWData->mGuestProperties.end())
5420 {
5421 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5422 aValue = it->second.strValue;
5423 *aTimestamp = it->second.mTimestamp;
5424 GuestPropWriteFlags(it->second.mFlags, szFlags);
5425 aFlags = Utf8Str(szFlags);
5426 }
5427
5428 return S_OK;
5429}
5430
5431/**
5432 * Query the VM that a guest property belongs to for the property.
5433 * @returns E_ACCESSDENIED if the VM process is not available or not
5434 * currently handling queries and the lookup should then be done in
5435 * VBoxSVC.
5436 */
5437HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5438 com::Utf8Str &aValue,
5439 LONG64 *aTimestamp,
5440 com::Utf8Str &aFlags) const
5441{
5442 HRESULT rc = S_OK;
5443 Bstr bstrValue;
5444 Bstr bstrFlags;
5445
5446 ComPtr<IInternalSessionControl> directControl;
5447 {
5448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5449 if (mData->mSession.mLockType == LockType_VM)
5450 directControl = mData->mSession.mDirectControl;
5451 }
5452
5453 /* ignore calls made after #OnSessionEnd() is called */
5454 if (!directControl)
5455 rc = E_ACCESSDENIED;
5456 else
5457 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5458 0 /* accessMode */,
5459 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5460
5461 aValue = bstrValue;
5462 aFlags = bstrFlags;
5463
5464 return rc;
5465}
5466#endif // VBOX_WITH_GUEST_PROPS
5467
5468HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5469 com::Utf8Str &aValue,
5470 LONG64 *aTimestamp,
5471 com::Utf8Str &aFlags)
5472{
5473#ifndef VBOX_WITH_GUEST_PROPS
5474 ReturnComNotImplemented();
5475#else // VBOX_WITH_GUEST_PROPS
5476
5477 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5478
5479 if (rc == E_ACCESSDENIED)
5480 /* The VM is not running or the service is not (yet) accessible */
5481 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5482 return rc;
5483#endif // VBOX_WITH_GUEST_PROPS
5484}
5485
5486HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5487{
5488 LONG64 dummyTimestamp;
5489 com::Utf8Str dummyFlags;
5490 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5491 return rc;
5492
5493}
5494HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5495{
5496 com::Utf8Str dummyFlags;
5497 com::Utf8Str dummyValue;
5498 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5499 return rc;
5500}
5501
5502#ifdef VBOX_WITH_GUEST_PROPS
5503/**
5504 * Set a guest property in VBoxSVC's internal structures.
5505 */
5506HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5507 const com::Utf8Str &aFlags, bool fDelete)
5508{
5509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5510 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5511 if (FAILED(rc)) return rc;
5512
5513 try
5514 {
5515 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5516 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5517 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5518
5519 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5520 if (it == mHWData->mGuestProperties.end())
5521 {
5522 if (!fDelete)
5523 {
5524 i_setModified(IsModified_MachineData);
5525 mHWData.backupEx();
5526
5527 RTTIMESPEC time;
5528 HWData::GuestProperty prop;
5529 prop.strValue = Bstr(aValue).raw();
5530 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5531 prop.mFlags = fFlags;
5532 mHWData->mGuestProperties[aName] = prop;
5533 }
5534 }
5535 else
5536 {
5537 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5538 {
5539 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5540 }
5541 else
5542 {
5543 i_setModified(IsModified_MachineData);
5544 mHWData.backupEx();
5545
5546 /* The backupEx() operation invalidates our iterator,
5547 * so get a new one. */
5548 it = mHWData->mGuestProperties.find(aName);
5549 Assert(it != mHWData->mGuestProperties.end());
5550
5551 if (!fDelete)
5552 {
5553 RTTIMESPEC time;
5554 it->second.strValue = aValue;
5555 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5556 it->second.mFlags = fFlags;
5557 }
5558 else
5559 mHWData->mGuestProperties.erase(it);
5560 }
5561 }
5562
5563 if (SUCCEEDED(rc))
5564 {
5565 alock.release();
5566
5567 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5568 }
5569 }
5570 catch (std::bad_alloc &)
5571 {
5572 rc = E_OUTOFMEMORY;
5573 }
5574
5575 return rc;
5576}
5577
5578/**
5579 * Set a property on the VM that that property belongs to.
5580 * @returns E_ACCESSDENIED if the VM process is not available or not
5581 * currently handling queries and the setting should then be done in
5582 * VBoxSVC.
5583 */
5584HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5585 const com::Utf8Str &aFlags, bool fDelete)
5586{
5587 HRESULT rc;
5588
5589 try
5590 {
5591 ComPtr<IInternalSessionControl> directControl;
5592 {
5593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5594 if (mData->mSession.mLockType == LockType_VM)
5595 directControl = mData->mSession.mDirectControl;
5596 }
5597
5598 Bstr dummy1; /* will not be changed (setter) */
5599 Bstr dummy2; /* will not be changed (setter) */
5600 LONG64 dummy64;
5601 if (!directControl)
5602 rc = E_ACCESSDENIED;
5603 else
5604 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5605 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5606 fDelete ? 2 : 1 /* accessMode */,
5607 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5608 }
5609 catch (std::bad_alloc &)
5610 {
5611 rc = E_OUTOFMEMORY;
5612 }
5613
5614 return rc;
5615}
5616#endif // VBOX_WITH_GUEST_PROPS
5617
5618HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5619 const com::Utf8Str &aFlags)
5620{
5621#ifndef VBOX_WITH_GUEST_PROPS
5622 ReturnComNotImplemented();
5623#else // VBOX_WITH_GUEST_PROPS
5624 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5625 if (rc == E_ACCESSDENIED)
5626 /* The VM is not running or the service is not (yet) accessible */
5627 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5628 return rc;
5629#endif // VBOX_WITH_GUEST_PROPS
5630}
5631
5632HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5633{
5634 return setGuestProperty(aProperty, aValue, "");
5635}
5636
5637HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5638{
5639#ifndef VBOX_WITH_GUEST_PROPS
5640 ReturnComNotImplemented();
5641#else // VBOX_WITH_GUEST_PROPS
5642 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5643 if (rc == E_ACCESSDENIED)
5644 /* The VM is not running or the service is not (yet) accessible */
5645 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5646 return rc;
5647#endif // VBOX_WITH_GUEST_PROPS
5648}
5649
5650#ifdef VBOX_WITH_GUEST_PROPS
5651/**
5652 * Enumerate the guest properties in VBoxSVC's internal structures.
5653 */
5654HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5655 std::vector<com::Utf8Str> &aNames,
5656 std::vector<com::Utf8Str> &aValues,
5657 std::vector<LONG64> &aTimestamps,
5658 std::vector<com::Utf8Str> &aFlags)
5659{
5660 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5661 Utf8Str strPatterns(aPatterns);
5662
5663 /*
5664 * Look for matching patterns and build up a list.
5665 */
5666 HWData::GuestPropertyMap propMap;
5667 for (HWData::GuestPropertyMap::const_iterator
5668 it = mHWData->mGuestProperties.begin();
5669 it != mHWData->mGuestProperties.end();
5670 ++it)
5671 {
5672 if ( strPatterns.isEmpty()
5673 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5674 RTSTR_MAX,
5675 it->first.c_str(),
5676 RTSTR_MAX,
5677 NULL)
5678 )
5679 propMap.insert(*it);
5680 }
5681
5682 alock.release();
5683
5684 /*
5685 * And build up the arrays for returning the property information.
5686 */
5687 size_t cEntries = propMap.size();
5688
5689 aNames.resize(cEntries);
5690 aValues.resize(cEntries);
5691 aTimestamps.resize(cEntries);
5692 aFlags.resize(cEntries);
5693
5694 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5695 size_t i = 0;
5696 for (HWData::GuestPropertyMap::const_iterator
5697 it = propMap.begin();
5698 it != propMap.end();
5699 ++it, ++i)
5700 {
5701 aNames[i] = it->first;
5702 aValues[i] = it->second.strValue;
5703 aTimestamps[i] = it->second.mTimestamp;
5704 GuestPropWriteFlags(it->second.mFlags, szFlags);
5705 aFlags[i] = Utf8Str(szFlags);
5706 }
5707
5708 return S_OK;
5709}
5710
5711/**
5712 * Enumerate the properties managed by a VM.
5713 * @returns E_ACCESSDENIED if the VM process is not available or not
5714 * currently handling queries and the setting should then be done in
5715 * VBoxSVC.
5716 */
5717HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5718 std::vector<com::Utf8Str> &aNames,
5719 std::vector<com::Utf8Str> &aValues,
5720 std::vector<LONG64> &aTimestamps,
5721 std::vector<com::Utf8Str> &aFlags)
5722{
5723 HRESULT rc;
5724 ComPtr<IInternalSessionControl> directControl;
5725 {
5726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5727 if (mData->mSession.mLockType == LockType_VM)
5728 directControl = mData->mSession.mDirectControl;
5729 }
5730
5731 com::SafeArray<BSTR> bNames;
5732 com::SafeArray<BSTR> bValues;
5733 com::SafeArray<LONG64> bTimestamps;
5734 com::SafeArray<BSTR> bFlags;
5735
5736 if (!directControl)
5737 rc = E_ACCESSDENIED;
5738 else
5739 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5740 ComSafeArrayAsOutParam(bNames),
5741 ComSafeArrayAsOutParam(bValues),
5742 ComSafeArrayAsOutParam(bTimestamps),
5743 ComSafeArrayAsOutParam(bFlags));
5744 size_t i;
5745 aNames.resize(bNames.size());
5746 for (i = 0; i < bNames.size(); ++i)
5747 aNames[i] = Utf8Str(bNames[i]);
5748 aValues.resize(bValues.size());
5749 for (i = 0; i < bValues.size(); ++i)
5750 aValues[i] = Utf8Str(bValues[i]);
5751 aTimestamps.resize(bTimestamps.size());
5752 for (i = 0; i < bTimestamps.size(); ++i)
5753 aTimestamps[i] = bTimestamps[i];
5754 aFlags.resize(bFlags.size());
5755 for (i = 0; i < bFlags.size(); ++i)
5756 aFlags[i] = Utf8Str(bFlags[i]);
5757
5758 return rc;
5759}
5760#endif // VBOX_WITH_GUEST_PROPS
5761HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5762 std::vector<com::Utf8Str> &aNames,
5763 std::vector<com::Utf8Str> &aValues,
5764 std::vector<LONG64> &aTimestamps,
5765 std::vector<com::Utf8Str> &aFlags)
5766{
5767#ifndef VBOX_WITH_GUEST_PROPS
5768 ReturnComNotImplemented();
5769#else // VBOX_WITH_GUEST_PROPS
5770
5771 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5772
5773 if (rc == E_ACCESSDENIED)
5774 /* The VM is not running or the service is not (yet) accessible */
5775 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5776 return rc;
5777#endif // VBOX_WITH_GUEST_PROPS
5778}
5779
5780HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5781 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5782{
5783 MediumAttachmentList atts;
5784
5785 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5786 if (FAILED(rc)) return rc;
5787
5788 aMediumAttachments.resize(atts.size());
5789 size_t i = 0;
5790 for (MediumAttachmentList::const_iterator
5791 it = atts.begin();
5792 it != atts.end();
5793 ++it, ++i)
5794 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5795
5796 return S_OK;
5797}
5798
5799HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5800 LONG aControllerPort,
5801 LONG aDevice,
5802 ComPtr<IMediumAttachment> &aAttachment)
5803{
5804 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5805 aName.c_str(), aControllerPort, aDevice));
5806
5807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5808
5809 aAttachment = NULL;
5810
5811 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5812 aName,
5813 aControllerPort,
5814 aDevice);
5815 if (pAttach.isNull())
5816 return setError(VBOX_E_OBJECT_NOT_FOUND,
5817 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5818 aDevice, aControllerPort, aName.c_str());
5819
5820 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5821
5822 return S_OK;
5823}
5824
5825
5826HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5827 StorageBus_T aConnectionType,
5828 ComPtr<IStorageController> &aController)
5829{
5830 if ( (aConnectionType <= StorageBus_Null)
5831 || (aConnectionType > StorageBus_VirtioSCSI))
5832 return setError(E_INVALIDARG,
5833 tr("Invalid connection type: %d"),
5834 aConnectionType);
5835
5836 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5837
5838 HRESULT rc = i_checkStateDependency(MutableStateDep);
5839 if (FAILED(rc)) return rc;
5840
5841 /* try to find one with the name first. */
5842 ComObjPtr<StorageController> ctrl;
5843
5844 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5845 if (SUCCEEDED(rc))
5846 return setError(VBOX_E_OBJECT_IN_USE,
5847 tr("Storage controller named '%s' already exists"),
5848 aName.c_str());
5849
5850 ctrl.createObject();
5851
5852 /* get a new instance number for the storage controller */
5853 ULONG ulInstance = 0;
5854 bool fBootable = true;
5855 for (StorageControllerList::const_iterator
5856 it = mStorageControllers->begin();
5857 it != mStorageControllers->end();
5858 ++it)
5859 {
5860 if ((*it)->i_getStorageBus() == aConnectionType)
5861 {
5862 ULONG ulCurInst = (*it)->i_getInstance();
5863
5864 if (ulCurInst >= ulInstance)
5865 ulInstance = ulCurInst + 1;
5866
5867 /* Only one controller of each type can be marked as bootable. */
5868 if ((*it)->i_getBootable())
5869 fBootable = false;
5870 }
5871 }
5872
5873 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5874 if (FAILED(rc)) return rc;
5875
5876 i_setModified(IsModified_Storage);
5877 mStorageControllers.backup();
5878 mStorageControllers->push_back(ctrl);
5879
5880 ctrl.queryInterfaceTo(aController.asOutParam());
5881
5882 /* inform the direct session if any */
5883 alock.release();
5884 i_onStorageControllerChange(i_getId(), aName);
5885
5886 return S_OK;
5887}
5888
5889HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5890 ComPtr<IStorageController> &aStorageController)
5891{
5892 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5893
5894 ComObjPtr<StorageController> ctrl;
5895
5896 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5897 if (SUCCEEDED(rc))
5898 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5899
5900 return rc;
5901}
5902
5903HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5904 ULONG aInstance,
5905 ComPtr<IStorageController> &aStorageController)
5906{
5907 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5908
5909 for (StorageControllerList::const_iterator
5910 it = mStorageControllers->begin();
5911 it != mStorageControllers->end();
5912 ++it)
5913 {
5914 if ( (*it)->i_getStorageBus() == aConnectionType
5915 && (*it)->i_getInstance() == aInstance)
5916 {
5917 (*it).queryInterfaceTo(aStorageController.asOutParam());
5918 return S_OK;
5919 }
5920 }
5921
5922 return setError(VBOX_E_OBJECT_NOT_FOUND,
5923 tr("Could not find a storage controller with instance number '%lu'"),
5924 aInstance);
5925}
5926
5927HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5928{
5929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5930
5931 HRESULT rc = i_checkStateDependency(MutableStateDep);
5932 if (FAILED(rc)) return rc;
5933
5934 ComObjPtr<StorageController> ctrl;
5935
5936 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5937 if (SUCCEEDED(rc))
5938 {
5939 /* Ensure that only one controller of each type is marked as bootable. */
5940 if (aBootable == TRUE)
5941 {
5942 for (StorageControllerList::const_iterator
5943 it = mStorageControllers->begin();
5944 it != mStorageControllers->end();
5945 ++it)
5946 {
5947 ComObjPtr<StorageController> aCtrl = (*it);
5948
5949 if ( (aCtrl->i_getName() != aName)
5950 && aCtrl->i_getBootable() == TRUE
5951 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5952 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5953 {
5954 aCtrl->i_setBootable(FALSE);
5955 break;
5956 }
5957 }
5958 }
5959
5960 if (SUCCEEDED(rc))
5961 {
5962 ctrl->i_setBootable(aBootable);
5963 i_setModified(IsModified_Storage);
5964 }
5965 }
5966
5967 if (SUCCEEDED(rc))
5968 {
5969 /* inform the direct session if any */
5970 alock.release();
5971 i_onStorageControllerChange(i_getId(), aName);
5972 }
5973
5974 return rc;
5975}
5976
5977HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5978{
5979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5980
5981 HRESULT rc = i_checkStateDependency(MutableStateDep);
5982 if (FAILED(rc)) return rc;
5983
5984 ComObjPtr<StorageController> ctrl;
5985 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5986 if (FAILED(rc)) return rc;
5987
5988 MediumAttachmentList llDetachedAttachments;
5989 {
5990 /* find all attached devices to the appropriate storage controller and detach them all */
5991 // make a temporary list because detachDevice invalidates iterators into
5992 // mMediumAttachments
5993 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5994
5995 for (MediumAttachmentList::const_iterator
5996 it = llAttachments2.begin();
5997 it != llAttachments2.end();
5998 ++it)
5999 {
6000 MediumAttachment *pAttachTemp = *it;
6001
6002 AutoCaller localAutoCaller(pAttachTemp);
6003 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6004
6005 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6006
6007 if (pAttachTemp->i_getControllerName() == aName)
6008 {
6009 llDetachedAttachments.push_back(pAttachTemp);
6010 rc = i_detachDevice(pAttachTemp, alock, NULL);
6011 if (FAILED(rc)) return rc;
6012 }
6013 }
6014 }
6015
6016 /* send event about detached devices before removing parent controller */
6017 for (MediumAttachmentList::const_iterator
6018 it = llDetachedAttachments.begin();
6019 it != llDetachedAttachments.end();
6020 ++it)
6021 {
6022 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6023 }
6024
6025 /* We can remove it now. */
6026 i_setModified(IsModified_Storage);
6027 mStorageControllers.backup();
6028
6029 ctrl->i_unshare();
6030
6031 mStorageControllers->remove(ctrl);
6032
6033 /* inform the direct session if any */
6034 alock.release();
6035 i_onStorageControllerChange(i_getId(), aName);
6036
6037 return S_OK;
6038}
6039
6040HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6041 ComPtr<IUSBController> &aController)
6042{
6043 if ( (aType <= USBControllerType_Null)
6044 || (aType >= USBControllerType_Last))
6045 return setError(E_INVALIDARG,
6046 tr("Invalid USB controller type: %d"),
6047 aType);
6048
6049 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6050
6051 HRESULT rc = i_checkStateDependency(MutableStateDep);
6052 if (FAILED(rc)) return rc;
6053
6054 /* try to find one with the same type first. */
6055 ComObjPtr<USBController> ctrl;
6056
6057 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6058 if (SUCCEEDED(rc))
6059 return setError(VBOX_E_OBJECT_IN_USE,
6060 tr("USB controller named '%s' already exists"),
6061 aName.c_str());
6062
6063 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6064 ULONG maxInstances;
6065 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6066 if (FAILED(rc))
6067 return rc;
6068
6069 ULONG cInstances = i_getUSBControllerCountByType(aType);
6070 if (cInstances >= maxInstances)
6071 return setError(E_INVALIDARG,
6072 tr("Too many USB controllers of this type"));
6073
6074 ctrl.createObject();
6075
6076 rc = ctrl->init(this, aName, aType);
6077 if (FAILED(rc)) return rc;
6078
6079 i_setModified(IsModified_USB);
6080 mUSBControllers.backup();
6081 mUSBControllers->push_back(ctrl);
6082
6083 ctrl.queryInterfaceTo(aController.asOutParam());
6084
6085 /* inform the direct session if any */
6086 alock.release();
6087 i_onUSBControllerChange();
6088
6089 return S_OK;
6090}
6091
6092HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6093{
6094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6095
6096 ComObjPtr<USBController> ctrl;
6097
6098 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6099 if (SUCCEEDED(rc))
6100 ctrl.queryInterfaceTo(aController.asOutParam());
6101
6102 return rc;
6103}
6104
6105HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6106 ULONG *aControllers)
6107{
6108 if ( (aType <= USBControllerType_Null)
6109 || (aType >= USBControllerType_Last))
6110 return setError(E_INVALIDARG,
6111 tr("Invalid USB controller type: %d"),
6112 aType);
6113
6114 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6115
6116 ComObjPtr<USBController> ctrl;
6117
6118 *aControllers = i_getUSBControllerCountByType(aType);
6119
6120 return S_OK;
6121}
6122
6123HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6124{
6125
6126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6127
6128 HRESULT rc = i_checkStateDependency(MutableStateDep);
6129 if (FAILED(rc)) return rc;
6130
6131 ComObjPtr<USBController> ctrl;
6132 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6133 if (FAILED(rc)) return rc;
6134
6135 i_setModified(IsModified_USB);
6136 mUSBControllers.backup();
6137
6138 ctrl->i_unshare();
6139
6140 mUSBControllers->remove(ctrl);
6141
6142 /* inform the direct session if any */
6143 alock.release();
6144 i_onUSBControllerChange();
6145
6146 return S_OK;
6147}
6148
6149HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6150 ULONG *aOriginX,
6151 ULONG *aOriginY,
6152 ULONG *aWidth,
6153 ULONG *aHeight,
6154 BOOL *aEnabled)
6155{
6156 uint32_t u32OriginX= 0;
6157 uint32_t u32OriginY= 0;
6158 uint32_t u32Width = 0;
6159 uint32_t u32Height = 0;
6160 uint16_t u16Flags = 0;
6161
6162 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6163 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6164 if (RT_FAILURE(vrc))
6165 {
6166#ifdef RT_OS_WINDOWS
6167 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6168 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6169 * So just assign fEnable to TRUE again.
6170 * The right fix would be to change GUI API wrappers to make sure that parameters
6171 * are changed only if API succeeds.
6172 */
6173 *aEnabled = TRUE;
6174#endif
6175 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6176 tr("Saved guest size is not available (%Rrc)"),
6177 vrc);
6178 }
6179
6180 *aOriginX = u32OriginX;
6181 *aOriginY = u32OriginY;
6182 *aWidth = u32Width;
6183 *aHeight = u32Height;
6184 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6185
6186 return S_OK;
6187}
6188
6189HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6190 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6191{
6192 if (aScreenId != 0)
6193 return E_NOTIMPL;
6194
6195 if ( aBitmapFormat != BitmapFormat_BGR0
6196 && aBitmapFormat != BitmapFormat_BGRA
6197 && aBitmapFormat != BitmapFormat_RGBA
6198 && aBitmapFormat != BitmapFormat_PNG)
6199 return setError(E_NOTIMPL,
6200 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6201
6202 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6203
6204 uint8_t *pu8Data = NULL;
6205 uint32_t cbData = 0;
6206 uint32_t u32Width = 0;
6207 uint32_t u32Height = 0;
6208
6209 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6210
6211 if (RT_FAILURE(vrc))
6212 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6213 tr("Saved thumbnail data is not available (%Rrc)"),
6214 vrc);
6215
6216 HRESULT hr = S_OK;
6217
6218 *aWidth = u32Width;
6219 *aHeight = u32Height;
6220
6221 if (cbData > 0)
6222 {
6223 /* Convert pixels to the format expected by the API caller. */
6224 if (aBitmapFormat == BitmapFormat_BGR0)
6225 {
6226 /* [0] B, [1] G, [2] R, [3] 0. */
6227 aData.resize(cbData);
6228 memcpy(&aData.front(), pu8Data, cbData);
6229 }
6230 else if (aBitmapFormat == BitmapFormat_BGRA)
6231 {
6232 /* [0] B, [1] G, [2] R, [3] A. */
6233 aData.resize(cbData);
6234 for (uint32_t i = 0; i < cbData; i += 4)
6235 {
6236 aData[i] = pu8Data[i];
6237 aData[i + 1] = pu8Data[i + 1];
6238 aData[i + 2] = pu8Data[i + 2];
6239 aData[i + 3] = 0xff;
6240 }
6241 }
6242 else if (aBitmapFormat == BitmapFormat_RGBA)
6243 {
6244 /* [0] R, [1] G, [2] B, [3] A. */
6245 aData.resize(cbData);
6246 for (uint32_t i = 0; i < cbData; i += 4)
6247 {
6248 aData[i] = pu8Data[i + 2];
6249 aData[i + 1] = pu8Data[i + 1];
6250 aData[i + 2] = pu8Data[i];
6251 aData[i + 3] = 0xff;
6252 }
6253 }
6254 else if (aBitmapFormat == BitmapFormat_PNG)
6255 {
6256 uint8_t *pu8PNG = NULL;
6257 uint32_t cbPNG = 0;
6258 uint32_t cxPNG = 0;
6259 uint32_t cyPNG = 0;
6260
6261 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6262
6263 if (RT_SUCCESS(vrc))
6264 {
6265 aData.resize(cbPNG);
6266 if (cbPNG)
6267 memcpy(&aData.front(), pu8PNG, cbPNG);
6268 }
6269 else
6270 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6271 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6272 vrc);
6273
6274 RTMemFree(pu8PNG);
6275 }
6276 }
6277
6278 freeSavedDisplayScreenshot(pu8Data);
6279
6280 return hr;
6281}
6282
6283HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6284 ULONG *aWidth,
6285 ULONG *aHeight,
6286 std::vector<BitmapFormat_T> &aBitmapFormats)
6287{
6288 if (aScreenId != 0)
6289 return E_NOTIMPL;
6290
6291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6292
6293 uint8_t *pu8Data = NULL;
6294 uint32_t cbData = 0;
6295 uint32_t u32Width = 0;
6296 uint32_t u32Height = 0;
6297
6298 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6299
6300 if (RT_FAILURE(vrc))
6301 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6302 tr("Saved screenshot data is not available (%Rrc)"),
6303 vrc);
6304
6305 *aWidth = u32Width;
6306 *aHeight = u32Height;
6307 aBitmapFormats.resize(1);
6308 aBitmapFormats[0] = BitmapFormat_PNG;
6309
6310 freeSavedDisplayScreenshot(pu8Data);
6311
6312 return S_OK;
6313}
6314
6315HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6316 BitmapFormat_T aBitmapFormat,
6317 ULONG *aWidth,
6318 ULONG *aHeight,
6319 std::vector<BYTE> &aData)
6320{
6321 if (aScreenId != 0)
6322 return E_NOTIMPL;
6323
6324 if (aBitmapFormat != BitmapFormat_PNG)
6325 return E_NOTIMPL;
6326
6327 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6328
6329 uint8_t *pu8Data = NULL;
6330 uint32_t cbData = 0;
6331 uint32_t u32Width = 0;
6332 uint32_t u32Height = 0;
6333
6334 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6335
6336 if (RT_FAILURE(vrc))
6337 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6338 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6339 vrc);
6340
6341 *aWidth = u32Width;
6342 *aHeight = u32Height;
6343
6344 aData.resize(cbData);
6345 if (cbData)
6346 memcpy(&aData.front(), pu8Data, cbData);
6347
6348 freeSavedDisplayScreenshot(pu8Data);
6349
6350 return S_OK;
6351}
6352
6353HRESULT Machine::hotPlugCPU(ULONG aCpu)
6354{
6355 HRESULT rc = S_OK;
6356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6357
6358 if (!mHWData->mCPUHotPlugEnabled)
6359 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6360
6361 if (aCpu >= mHWData->mCPUCount)
6362 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6363
6364 if (mHWData->mCPUAttached[aCpu])
6365 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6366
6367 alock.release();
6368 rc = i_onCPUChange(aCpu, false);
6369 alock.acquire();
6370 if (FAILED(rc)) return rc;
6371
6372 i_setModified(IsModified_MachineData);
6373 mHWData.backup();
6374 mHWData->mCPUAttached[aCpu] = true;
6375
6376 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6377 if (Global::IsOnline(mData->mMachineState))
6378 i_saveSettings(NULL);
6379
6380 return S_OK;
6381}
6382
6383HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6384{
6385 HRESULT rc = S_OK;
6386
6387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6388
6389 if (!mHWData->mCPUHotPlugEnabled)
6390 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6391
6392 if (aCpu >= SchemaDefs::MaxCPUCount)
6393 return setError(E_INVALIDARG,
6394 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6395 SchemaDefs::MaxCPUCount);
6396
6397 if (!mHWData->mCPUAttached[aCpu])
6398 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6399
6400 /* CPU 0 can't be detached */
6401 if (aCpu == 0)
6402 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6403
6404 alock.release();
6405 rc = i_onCPUChange(aCpu, true);
6406 alock.acquire();
6407 if (FAILED(rc)) return rc;
6408
6409 i_setModified(IsModified_MachineData);
6410 mHWData.backup();
6411 mHWData->mCPUAttached[aCpu] = false;
6412
6413 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6414 if (Global::IsOnline(mData->mMachineState))
6415 i_saveSettings(NULL);
6416
6417 return S_OK;
6418}
6419
6420HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6421{
6422 *aAttached = false;
6423
6424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6425
6426 /* If hotplug is enabled the CPU is always enabled. */
6427 if (!mHWData->mCPUHotPlugEnabled)
6428 {
6429 if (aCpu < mHWData->mCPUCount)
6430 *aAttached = true;
6431 }
6432 else
6433 {
6434 if (aCpu < SchemaDefs::MaxCPUCount)
6435 *aAttached = mHWData->mCPUAttached[aCpu];
6436 }
6437
6438 return S_OK;
6439}
6440
6441HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6442{
6443 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6444
6445 Utf8Str log = i_getLogFilename(aIdx);
6446 if (!RTFileExists(log.c_str()))
6447 log.setNull();
6448 aFilename = log;
6449
6450 return S_OK;
6451}
6452
6453HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6454{
6455 if (aSize < 0)
6456 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6457
6458 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6459
6460 HRESULT rc = S_OK;
6461 Utf8Str log = i_getLogFilename(aIdx);
6462
6463 /* do not unnecessarily hold the lock while doing something which does
6464 * not need the lock and potentially takes a long time. */
6465 alock.release();
6466
6467 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6468 * keeps the SOAP reply size under 1M for the webservice (we're using
6469 * base64 encoded strings for binary data for years now, avoiding the
6470 * expansion of each byte array element to approx. 25 bytes of XML. */
6471 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6472 aData.resize(cbData);
6473
6474 RTFILE LogFile;
6475 int vrc = RTFileOpen(&LogFile, log.c_str(),
6476 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6477 if (RT_SUCCESS(vrc))
6478 {
6479 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6480 if (RT_SUCCESS(vrc))
6481 aData.resize(cbData);
6482 else
6483 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6484 tr("Could not read log file '%s' (%Rrc)"),
6485 log.c_str(), vrc);
6486 RTFileClose(LogFile);
6487 }
6488 else
6489 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6490 tr("Could not open log file '%s' (%Rrc)"),
6491 log.c_str(), vrc);
6492
6493 if (FAILED(rc))
6494 aData.resize(0);
6495
6496 return rc;
6497}
6498
6499
6500/**
6501 * Currently this method doesn't attach device to the running VM,
6502 * just makes sure it's plugged on next VM start.
6503 */
6504HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6505{
6506 // lock scope
6507 {
6508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6509
6510 HRESULT rc = i_checkStateDependency(MutableStateDep);
6511 if (FAILED(rc)) return rc;
6512
6513 ChipsetType_T aChipset = ChipsetType_PIIX3;
6514 COMGETTER(ChipsetType)(&aChipset);
6515
6516 if (aChipset != ChipsetType_ICH9)
6517 {
6518 return setError(E_INVALIDARG,
6519 tr("Host PCI attachment only supported with ICH9 chipset"));
6520 }
6521
6522 // check if device with this host PCI address already attached
6523 for (HWData::PCIDeviceAssignmentList::const_iterator
6524 it = mHWData->mPCIDeviceAssignments.begin();
6525 it != mHWData->mPCIDeviceAssignments.end();
6526 ++it)
6527 {
6528 LONG iHostAddress = -1;
6529 ComPtr<PCIDeviceAttachment> pAttach;
6530 pAttach = *it;
6531 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6532 if (iHostAddress == aHostAddress)
6533 return setError(E_INVALIDARG,
6534 tr("Device with host PCI address already attached to this VM"));
6535 }
6536
6537 ComObjPtr<PCIDeviceAttachment> pda;
6538 char name[32];
6539
6540 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6541 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6542 pda.createObject();
6543 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6544 i_setModified(IsModified_MachineData);
6545 mHWData.backup();
6546 mHWData->mPCIDeviceAssignments.push_back(pda);
6547 }
6548
6549 return S_OK;
6550}
6551
6552/**
6553 * Currently this method doesn't detach device from the running VM,
6554 * just makes sure it's not plugged on next VM start.
6555 */
6556HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6557{
6558 ComObjPtr<PCIDeviceAttachment> pAttach;
6559 bool fRemoved = false;
6560 HRESULT rc;
6561
6562 // lock scope
6563 {
6564 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6565
6566 rc = i_checkStateDependency(MutableStateDep);
6567 if (FAILED(rc)) return rc;
6568
6569 for (HWData::PCIDeviceAssignmentList::const_iterator
6570 it = mHWData->mPCIDeviceAssignments.begin();
6571 it != mHWData->mPCIDeviceAssignments.end();
6572 ++it)
6573 {
6574 LONG iHostAddress = -1;
6575 pAttach = *it;
6576 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6577 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6578 {
6579 i_setModified(IsModified_MachineData);
6580 mHWData.backup();
6581 mHWData->mPCIDeviceAssignments.remove(pAttach);
6582 fRemoved = true;
6583 break;
6584 }
6585 }
6586 }
6587
6588
6589 /* Fire event outside of the lock */
6590 if (fRemoved)
6591 {
6592 Assert(!pAttach.isNull());
6593 ComPtr<IEventSource> es;
6594 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6595 Assert(SUCCEEDED(rc));
6596 Bstr mid;
6597 rc = this->COMGETTER(Id)(mid.asOutParam());
6598 Assert(SUCCEEDED(rc));
6599 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6600 }
6601
6602 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6603 tr("No host PCI device %08x attached"),
6604 aHostAddress
6605 );
6606}
6607
6608HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6609{
6610 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6611
6612 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6613 size_t i = 0;
6614 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6615 it = mHWData->mPCIDeviceAssignments.begin();
6616 it != mHWData->mPCIDeviceAssignments.end();
6617 ++it, ++i)
6618 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6619
6620 return S_OK;
6621}
6622
6623HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6624{
6625 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6626
6627 return S_OK;
6628}
6629
6630HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6631{
6632 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6633
6634 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6635
6636 return S_OK;
6637}
6638
6639HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6640{
6641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6643 if (SUCCEEDED(hrc))
6644 {
6645 hrc = mHWData.backupEx();
6646 if (SUCCEEDED(hrc))
6647 {
6648 i_setModified(IsModified_MachineData);
6649 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6650 }
6651 }
6652 return hrc;
6653}
6654
6655HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6656{
6657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6658 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6659 return S_OK;
6660}
6661
6662HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6663{
6664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6665 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6666 if (SUCCEEDED(hrc))
6667 {
6668 hrc = mHWData.backupEx();
6669 if (SUCCEEDED(hrc))
6670 {
6671 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6672 if (SUCCEEDED(hrc))
6673 i_setModified(IsModified_MachineData);
6674 }
6675 }
6676 return hrc;
6677}
6678
6679HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6680{
6681 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6682
6683 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6684
6685 return S_OK;
6686}
6687
6688HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6689{
6690 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6691 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6692 if (SUCCEEDED(hrc))
6693 {
6694 hrc = mHWData.backupEx();
6695 if (SUCCEEDED(hrc))
6696 {
6697 i_setModified(IsModified_MachineData);
6698 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6699 }
6700 }
6701 return hrc;
6702}
6703
6704HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6705{
6706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6707
6708 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6709
6710 return S_OK;
6711}
6712
6713HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6714{
6715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6716
6717 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6718 if ( SUCCEEDED(hrc)
6719 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6720 {
6721 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6722 int vrc;
6723
6724 if (aAutostartEnabled)
6725 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6726 else
6727 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6728
6729 if (RT_SUCCESS(vrc))
6730 {
6731 hrc = mHWData.backupEx();
6732 if (SUCCEEDED(hrc))
6733 {
6734 i_setModified(IsModified_MachineData);
6735 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6736 }
6737 }
6738 else if (vrc == VERR_NOT_SUPPORTED)
6739 hrc = setError(VBOX_E_NOT_SUPPORTED,
6740 tr("The VM autostart feature is not supported on this platform"));
6741 else if (vrc == VERR_PATH_NOT_FOUND)
6742 hrc = setError(E_FAIL,
6743 tr("The path to the autostart database is not set"));
6744 else
6745 hrc = setError(E_UNEXPECTED,
6746 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6747 aAutostartEnabled ? "Adding" : "Removing",
6748 mUserData->s.strName.c_str(), vrc);
6749 }
6750 return hrc;
6751}
6752
6753HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6754{
6755 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6756
6757 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6758
6759 return S_OK;
6760}
6761
6762HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6763{
6764 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6765 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6766 if (SUCCEEDED(hrc))
6767 {
6768 hrc = mHWData.backupEx();
6769 if (SUCCEEDED(hrc))
6770 {
6771 i_setModified(IsModified_MachineData);
6772 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6773 }
6774 }
6775 return hrc;
6776}
6777
6778HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6779{
6780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6781
6782 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6783
6784 return S_OK;
6785}
6786
6787HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6788{
6789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6790 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6791 if ( SUCCEEDED(hrc)
6792 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6793 {
6794 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6795 int vrc;
6796
6797 if (aAutostopType != AutostopType_Disabled)
6798 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6799 else
6800 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6801
6802 if (RT_SUCCESS(vrc))
6803 {
6804 hrc = mHWData.backupEx();
6805 if (SUCCEEDED(hrc))
6806 {
6807 i_setModified(IsModified_MachineData);
6808 mHWData->mAutostart.enmAutostopType = aAutostopType;
6809 }
6810 }
6811 else if (vrc == VERR_NOT_SUPPORTED)
6812 hrc = setError(VBOX_E_NOT_SUPPORTED,
6813 tr("The VM autostop feature is not supported on this platform"));
6814 else if (vrc == VERR_PATH_NOT_FOUND)
6815 hrc = setError(E_FAIL,
6816 tr("The path to the autostart database is not set"));
6817 else
6818 hrc = setError(E_UNEXPECTED,
6819 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6820 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6821 mUserData->s.strName.c_str(), vrc);
6822 }
6823 return hrc;
6824}
6825
6826HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6827{
6828 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6829
6830 aDefaultFrontend = mHWData->mDefaultFrontend;
6831
6832 return S_OK;
6833}
6834
6835HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6836{
6837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6838 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6839 if (SUCCEEDED(hrc))
6840 {
6841 hrc = mHWData.backupEx();
6842 if (SUCCEEDED(hrc))
6843 {
6844 i_setModified(IsModified_MachineData);
6845 mHWData->mDefaultFrontend = aDefaultFrontend;
6846 }
6847 }
6848 return hrc;
6849}
6850
6851HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6852{
6853 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6854 size_t cbIcon = mUserData->s.ovIcon.size();
6855 aIcon.resize(cbIcon);
6856 if (cbIcon)
6857 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6858 return S_OK;
6859}
6860
6861HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6862{
6863 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6864 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6865 if (SUCCEEDED(hrc))
6866 {
6867 i_setModified(IsModified_MachineData);
6868 mUserData.backup();
6869 size_t cbIcon = aIcon.size();
6870 mUserData->s.ovIcon.resize(cbIcon);
6871 if (cbIcon)
6872 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6873 }
6874 return hrc;
6875}
6876
6877HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6878{
6879#ifdef VBOX_WITH_USB
6880 *aUSBProxyAvailable = true;
6881#else
6882 *aUSBProxyAvailable = false;
6883#endif
6884 return S_OK;
6885}
6886
6887HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6888{
6889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6890
6891 *aVMProcessPriority = mUserData->s.enmVMPriority;
6892
6893 return S_OK;
6894}
6895
6896HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6897{
6898 RT_NOREF(aVMProcessPriority);
6899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6900 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6901 if (SUCCEEDED(hrc))
6902 {
6903 hrc = mUserData.backupEx();
6904 if (SUCCEEDED(hrc))
6905 {
6906 i_setModified(IsModified_MachineData);
6907 mUserData->s.enmVMPriority = aVMProcessPriority;
6908 }
6909 }
6910 alock.release();
6911 if (SUCCEEDED(hrc))
6912 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6913 return hrc;
6914}
6915
6916HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6917 ComPtr<IProgress> &aProgress)
6918{
6919 ComObjPtr<Progress> pP;
6920 Progress *ppP = pP;
6921 IProgress *iP = static_cast<IProgress *>(ppP);
6922 IProgress **pProgress = &iP;
6923
6924 IMachine *pTarget = aTarget;
6925
6926 /* Convert the options. */
6927 RTCList<CloneOptions_T> optList;
6928 if (aOptions.size())
6929 for (size_t i = 0; i < aOptions.size(); ++i)
6930 optList.append(aOptions[i]);
6931
6932 if (optList.contains(CloneOptions_Link))
6933 {
6934 if (!i_isSnapshotMachine())
6935 return setError(E_INVALIDARG,
6936 tr("Linked clone can only be created from a snapshot"));
6937 if (aMode != CloneMode_MachineState)
6938 return setError(E_INVALIDARG,
6939 tr("Linked clone can only be created for a single machine state"));
6940 }
6941 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6942
6943 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6944
6945 HRESULT rc = pWorker->start(pProgress);
6946
6947 pP = static_cast<Progress *>(*pProgress);
6948 pP.queryInterfaceTo(aProgress.asOutParam());
6949
6950 return rc;
6951
6952}
6953
6954HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6955 const com::Utf8Str &aType,
6956 ComPtr<IProgress> &aProgress)
6957{
6958 LogFlowThisFuncEnter();
6959
6960 ComObjPtr<Progress> ptrProgress;
6961 HRESULT hrc = ptrProgress.createObject();
6962 if (SUCCEEDED(hrc))
6963 {
6964 /* Initialize our worker task */
6965 MachineMoveVM *pTask = NULL;
6966 try
6967 {
6968 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6969 }
6970 catch (std::bad_alloc &)
6971 {
6972 return E_OUTOFMEMORY;
6973 }
6974
6975 hrc = pTask->init();//no exceptions are thrown
6976
6977 if (SUCCEEDED(hrc))
6978 {
6979 hrc = pTask->createThread();
6980 pTask = NULL; /* Consumed by createThread(). */
6981 if (SUCCEEDED(hrc))
6982 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6983 else
6984 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6985 }
6986 else
6987 delete pTask;
6988 }
6989
6990 LogFlowThisFuncLeave();
6991 return hrc;
6992
6993}
6994
6995HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6996{
6997 NOREF(aProgress);
6998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6999
7000 // This check should always fail.
7001 HRESULT rc = i_checkStateDependency(MutableStateDep);
7002 if (FAILED(rc)) return rc;
7003
7004 AssertFailedReturn(E_NOTIMPL);
7005}
7006
7007HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7008{
7009 NOREF(aSavedStateFile);
7010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7011
7012 // This check should always fail.
7013 HRESULT rc = i_checkStateDependency(MutableStateDep);
7014 if (FAILED(rc)) return rc;
7015
7016 AssertFailedReturn(E_NOTIMPL);
7017}
7018
7019HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7020{
7021 NOREF(aFRemoveFile);
7022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7023
7024 // This check should always fail.
7025 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7026 if (FAILED(rc)) return rc;
7027
7028 AssertFailedReturn(E_NOTIMPL);
7029}
7030
7031// public methods for internal purposes
7032/////////////////////////////////////////////////////////////////////////////
7033
7034/**
7035 * Adds the given IsModified_* flag to the dirty flags of the machine.
7036 * This must be called either during i_loadSettings or under the machine write lock.
7037 * @param fl Flag
7038 * @param fAllowStateModification If state modifications are allowed.
7039 */
7040void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7041{
7042 mData->flModifications |= fl;
7043 if (fAllowStateModification && i_isStateModificationAllowed())
7044 mData->mCurrentStateModified = true;
7045}
7046
7047/**
7048 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7049 * care of the write locking.
7050 *
7051 * @param fModification The flag to add.
7052 * @param fAllowStateModification If state modifications are allowed.
7053 */
7054void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7055{
7056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7057 i_setModified(fModification, fAllowStateModification);
7058}
7059
7060/**
7061 * Saves the registry entry of this machine to the given configuration node.
7062 *
7063 * @param data Machine registry data.
7064 *
7065 * @note locks this object for reading.
7066 */
7067HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7068{
7069 AutoLimitedCaller autoCaller(this);
7070 AssertComRCReturnRC(autoCaller.rc());
7071
7072 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7073
7074 data.uuid = mData->mUuid;
7075 data.strSettingsFile = mData->m_strConfigFile;
7076
7077 return S_OK;
7078}
7079
7080/**
7081 * Calculates the absolute path of the given path taking the directory of the
7082 * machine settings file as the current directory.
7083 *
7084 * @param strPath Path to calculate the absolute path for.
7085 * @param aResult Where to put the result (used only on success, can be the
7086 * same Utf8Str instance as passed in @a aPath).
7087 * @return IPRT result.
7088 *
7089 * @note Locks this object for reading.
7090 */
7091int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7092{
7093 AutoCaller autoCaller(this);
7094 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7095
7096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7099
7100 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7101
7102 strSettingsDir.stripFilename();
7103 char szFolder[RTPATH_MAX];
7104 size_t cbFolder = sizeof(szFolder);
7105 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7106 if (RT_SUCCESS(vrc))
7107 aResult = szFolder;
7108
7109 return vrc;
7110}
7111
7112/**
7113 * Copies strSource to strTarget, making it relative to the machine folder
7114 * if it is a subdirectory thereof, or simply copying it otherwise.
7115 *
7116 * @param strSource Path to evaluate and copy.
7117 * @param strTarget Buffer to receive target path.
7118 *
7119 * @note Locks this object for reading.
7120 */
7121void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7122 Utf8Str &strTarget)
7123{
7124 AutoCaller autoCaller(this);
7125 AssertComRCReturn(autoCaller.rc(), (void)0);
7126
7127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7128
7129 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7130 // use strTarget as a temporary buffer to hold the machine settings dir
7131 strTarget = mData->m_strConfigFileFull;
7132 strTarget.stripFilename();
7133 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7134 {
7135 // is relative: then append what's left
7136 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7137 // for empty paths (only possible for subdirs) use "." to avoid
7138 // triggering default settings for not present config attributes.
7139 if (strTarget.isEmpty())
7140 strTarget = ".";
7141 }
7142 else
7143 // is not relative: then overwrite
7144 strTarget = strSource;
7145}
7146
7147/**
7148 * Returns the full path to the machine's log folder in the
7149 * \a aLogFolder argument.
7150 */
7151void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7152{
7153 AutoCaller autoCaller(this);
7154 AssertComRCReturnVoid(autoCaller.rc());
7155
7156 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7157
7158 char szTmp[RTPATH_MAX];
7159 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7160 if (RT_SUCCESS(vrc))
7161 {
7162 if (szTmp[0] && !mUserData.isNull())
7163 {
7164 char szTmp2[RTPATH_MAX];
7165 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7166 if (RT_SUCCESS(vrc))
7167 aLogFolder.printf("%s%c%s",
7168 szTmp2,
7169 RTPATH_DELIMITER,
7170 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7171 }
7172 else
7173 vrc = VERR_PATH_IS_RELATIVE;
7174 }
7175
7176 if (RT_FAILURE(vrc))
7177 {
7178 // fallback if VBOX_USER_LOGHOME is not set or invalid
7179 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7180 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7181 aLogFolder.append(RTPATH_DELIMITER);
7182 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7183 }
7184}
7185
7186/**
7187 * Returns the full path to the machine's log file for an given index.
7188 */
7189Utf8Str Machine::i_getLogFilename(ULONG idx)
7190{
7191 Utf8Str logFolder;
7192 getLogFolder(logFolder);
7193 Assert(logFolder.length());
7194
7195 Utf8Str log;
7196 if (idx == 0)
7197 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7198#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7199 else if (idx == 1)
7200 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7201 else
7202 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7203#else
7204 else
7205 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7206#endif
7207 return log;
7208}
7209
7210/**
7211 * Returns the full path to the machine's hardened log file.
7212 */
7213Utf8Str Machine::i_getHardeningLogFilename(void)
7214{
7215 Utf8Str strFilename;
7216 getLogFolder(strFilename);
7217 Assert(strFilename.length());
7218 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7219 return strFilename;
7220}
7221
7222/**
7223 * Returns the default NVRAM filename based on the location of the VM config.
7224 * Note that this is a relative path.
7225 */
7226Utf8Str Machine::i_getDefaultNVRAMFilename()
7227{
7228 AutoCaller autoCaller(this);
7229 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7230
7231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7232
7233 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7234 || i_isSnapshotMachine())
7235 return Utf8Str::Empty;
7236
7237 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7238 strNVRAMFilePath.stripPath();
7239 strNVRAMFilePath.stripSuffix();
7240 strNVRAMFilePath += ".nvram";
7241
7242 return strNVRAMFilePath;
7243}
7244
7245/**
7246 * Returns the NVRAM filename for a new snapshot. This intentionally works
7247 * similarly to the saved state file naming. Note that this is usually
7248 * a relative path, unless the snapshot folder is absolute.
7249 */
7250Utf8Str Machine::i_getSnapshotNVRAMFilename()
7251{
7252 AutoCaller autoCaller(this);
7253 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7258 return Utf8Str::Empty;
7259
7260 RTTIMESPEC ts;
7261 RTTimeNow(&ts);
7262 RTTIME time;
7263 RTTimeExplode(&time, &ts);
7264
7265 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7266 strNVRAMFilePath += RTPATH_DELIMITER;
7267 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7268 time.i32Year, time.u8Month, time.u8MonthDay,
7269 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7270
7271 return strNVRAMFilePath;
7272}
7273
7274/**
7275 * Composes a unique saved state filename based on the current system time. The filename is
7276 * granular to the second so this will work so long as no more than one snapshot is taken on
7277 * a machine per second.
7278 *
7279 * Before version 4.1, we used this formula for saved state files:
7280 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7281 * which no longer works because saved state files can now be shared between the saved state of the
7282 * "saved" machine and an online snapshot, and the following would cause problems:
7283 * 1) save machine
7284 * 2) create online snapshot from that machine state --> reusing saved state file
7285 * 3) save machine again --> filename would be reused, breaking the online snapshot
7286 *
7287 * So instead we now use a timestamp.
7288 *
7289 * @param strStateFilePath
7290 */
7291
7292void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7293{
7294 AutoCaller autoCaller(this);
7295 AssertComRCReturnVoid(autoCaller.rc());
7296
7297 {
7298 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7299 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7300 }
7301
7302 RTTIMESPEC ts;
7303 RTTimeNow(&ts);
7304 RTTIME time;
7305 RTTimeExplode(&time, &ts);
7306
7307 strStateFilePath += RTPATH_DELIMITER;
7308 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7309 time.i32Year, time.u8Month, time.u8MonthDay,
7310 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7311}
7312
7313/**
7314 * Returns whether at least one USB controller is present for the VM.
7315 */
7316bool Machine::i_isUSBControllerPresent()
7317{
7318 AutoCaller autoCaller(this);
7319 AssertComRCReturn(autoCaller.rc(), false);
7320
7321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7322
7323 return (mUSBControllers->size() > 0);
7324}
7325
7326
7327/**
7328 * @note Locks this object for writing, calls the client process
7329 * (inside the lock).
7330 */
7331HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7332 const Utf8Str &strFrontend,
7333 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7334 ProgressProxy *aProgress)
7335{
7336 LogFlowThisFuncEnter();
7337
7338 AssertReturn(aControl, E_FAIL);
7339 AssertReturn(aProgress, E_FAIL);
7340 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7341
7342 AutoCaller autoCaller(this);
7343 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7344
7345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7346
7347 if (!mData->mRegistered)
7348 return setError(E_UNEXPECTED,
7349 tr("The machine '%s' is not registered"),
7350 mUserData->s.strName.c_str());
7351
7352 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7353
7354 /* The process started when launching a VM with separate UI/VM processes is always
7355 * the UI process, i.e. needs special handling as it won't claim the session. */
7356 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7357
7358 if (fSeparate)
7359 {
7360 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7361 return setError(VBOX_E_INVALID_OBJECT_STATE,
7362 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7363 mUserData->s.strName.c_str());
7364 }
7365 else
7366 {
7367 if ( mData->mSession.mState == SessionState_Locked
7368 || mData->mSession.mState == SessionState_Spawning
7369 || mData->mSession.mState == SessionState_Unlocking)
7370 return setError(VBOX_E_INVALID_OBJECT_STATE,
7371 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7372 mUserData->s.strName.c_str());
7373
7374 /* may not be busy */
7375 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7376 }
7377
7378 /* Hardening logging */
7379#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7380 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7381 {
7382 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7383 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7384 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7385 {
7386 Utf8Str strStartupLogDir = strHardeningLogFile;
7387 strStartupLogDir.stripFilename();
7388 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7389 file without stripping the file. */
7390 }
7391 strSupHardeningLogArg.append(strHardeningLogFile);
7392
7393 /* Remove legacy log filename to avoid confusion. */
7394 Utf8Str strOldStartupLogFile;
7395 getLogFolder(strOldStartupLogFile);
7396 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7397 RTFileDelete(strOldStartupLogFile.c_str());
7398 }
7399#else
7400 Utf8Str strSupHardeningLogArg;
7401#endif
7402
7403 Utf8Str strAppOverride;
7404#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7405 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7406#endif
7407
7408 bool fUseVBoxSDS = false;
7409 Utf8Str strCanonicalName;
7410 if (false)
7411 { }
7412#ifdef VBOX_WITH_QTGUI
7413 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7414 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7415 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7416 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7417 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7418 {
7419 strCanonicalName = "GUI/Qt";
7420 fUseVBoxSDS = true;
7421 }
7422#endif
7423#ifdef VBOX_WITH_VBOXSDL
7424 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7425 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7426 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7427 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7428 {
7429 strCanonicalName = "GUI/SDL";
7430 fUseVBoxSDS = true;
7431 }
7432#endif
7433#ifdef VBOX_WITH_HEADLESS
7434 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7435 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7436 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7437 {
7438 strCanonicalName = "headless";
7439 }
7440#endif
7441 else
7442 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7443
7444 Utf8Str idStr = mData->mUuid.toString();
7445 Utf8Str const &strMachineName = mUserData->s.strName;
7446 RTPROCESS pid = NIL_RTPROCESS;
7447
7448#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7449 RT_NOREF(fUseVBoxSDS);
7450#else
7451 DWORD idCallerSession = ~(DWORD)0;
7452 if (fUseVBoxSDS)
7453 {
7454 /*
7455 * The VBoxSDS should be used for process launching the VM with
7456 * GUI only if the caller and the VBoxSDS are in different Windows
7457 * sessions and the caller in the interactive one.
7458 */
7459 fUseVBoxSDS = false;
7460
7461 /* Get windows session of the current process. The process token used
7462 due to several reasons:
7463 1. The token is absent for the current thread except someone set it
7464 for us.
7465 2. Needs to get the id of the session where the process is started.
7466 We only need to do this once, though. */
7467 static DWORD s_idCurrentSession = ~(DWORD)0;
7468 DWORD idCurrentSession = s_idCurrentSession;
7469 if (idCurrentSession == ~(DWORD)0)
7470 {
7471 HANDLE hCurrentProcessToken = NULL;
7472 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7473 {
7474 DWORD cbIgn = 0;
7475 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7476 s_idCurrentSession = idCurrentSession;
7477 else
7478 {
7479 idCurrentSession = ~(DWORD)0;
7480 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7481 }
7482 CloseHandle(hCurrentProcessToken);
7483 }
7484 else
7485 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7486 }
7487
7488 /* get the caller's session */
7489 HRESULT hrc = CoImpersonateClient();
7490 if (SUCCEEDED(hrc))
7491 {
7492 HANDLE hCallerThreadToken;
7493 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7494 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7495 &hCallerThreadToken))
7496 {
7497 SetLastError(NO_ERROR);
7498 DWORD cbIgn = 0;
7499 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7500 {
7501 /* Only need to use SDS if the session ID differs: */
7502 if (idCurrentSession != idCallerSession)
7503 {
7504 fUseVBoxSDS = false;
7505
7506 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7507 DWORD cbTokenGroups = 0;
7508 PTOKEN_GROUPS pTokenGroups = NULL;
7509 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7510 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7511 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7512 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7513 {
7514 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7515 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7516 PSID pInteractiveSid = NULL;
7517 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7518 {
7519 /* Iterate over the groups looking for the interactive SID: */
7520 fUseVBoxSDS = false;
7521 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7522 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7523 {
7524 fUseVBoxSDS = true;
7525 break;
7526 }
7527 FreeSid(pInteractiveSid);
7528 }
7529 }
7530 else
7531 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7532 RTMemTmpFree(pTokenGroups);
7533 }
7534 }
7535 else
7536 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7537 CloseHandle(hCallerThreadToken);
7538 }
7539 else
7540 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7541 CoRevertToSelf();
7542 }
7543 else
7544 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7545 }
7546 if (fUseVBoxSDS)
7547 {
7548 /* connect to VBoxSDS */
7549 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7550 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7551 if (FAILED(rc))
7552 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7553 strMachineName.c_str());
7554
7555 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7556 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7557 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7558 service to access the files. */
7559 rc = CoSetProxyBlanket(pVBoxSDS,
7560 RPC_C_AUTHN_DEFAULT,
7561 RPC_C_AUTHZ_DEFAULT,
7562 COLE_DEFAULT_PRINCIPAL,
7563 RPC_C_AUTHN_LEVEL_DEFAULT,
7564 RPC_C_IMP_LEVEL_IMPERSONATE,
7565 NULL,
7566 EOAC_DEFAULT);
7567 if (FAILED(rc))
7568 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7569
7570 size_t const cEnvVars = aEnvironmentChanges.size();
7571 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7572 for (size_t i = 0; i < cEnvVars; i++)
7573 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7574
7575 ULONG uPid = 0;
7576 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7577 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7578 idCallerSession, &uPid);
7579 if (FAILED(rc))
7580 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7581 pid = (RTPROCESS)uPid;
7582 }
7583 else
7584#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7585 {
7586 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7587 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7588 if (RT_FAILURE(vrc))
7589 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7590 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7591 }
7592
7593 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7594 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7595
7596 if (!fSeparate)
7597 {
7598 /*
7599 * Note that we don't release the lock here before calling the client,
7600 * because it doesn't need to call us back if called with a NULL argument.
7601 * Releasing the lock here is dangerous because we didn't prepare the
7602 * launch data yet, but the client we've just started may happen to be
7603 * too fast and call LockMachine() that will fail (because of PID, etc.),
7604 * so that the Machine will never get out of the Spawning session state.
7605 */
7606
7607 /* inform the session that it will be a remote one */
7608 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7609#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7610 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7611#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7612 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7613#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7614 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7615
7616 if (FAILED(rc))
7617 {
7618 /* restore the session state */
7619 mData->mSession.mState = SessionState_Unlocked;
7620 alock.release();
7621 mParent->i_addProcessToReap(pid);
7622 /* The failure may occur w/o any error info (from RPC), so provide one */
7623 return setError(VBOX_E_VM_ERROR,
7624 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7625 }
7626
7627 /* attach launch data to the machine */
7628 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7629 mData->mSession.mRemoteControls.push_back(aControl);
7630 mData->mSession.mProgress = aProgress;
7631 mData->mSession.mPID = pid;
7632 mData->mSession.mState = SessionState_Spawning;
7633 Assert(strCanonicalName.isNotEmpty());
7634 mData->mSession.mName = strCanonicalName;
7635 }
7636 else
7637 {
7638 /* For separate UI process we declare the launch as completed instantly, as the
7639 * actual headless VM start may or may not come. No point in remembering anything
7640 * yet, as what matters for us is when the headless VM gets started. */
7641 aProgress->i_notifyComplete(S_OK);
7642 }
7643
7644 alock.release();
7645 mParent->i_addProcessToReap(pid);
7646
7647 LogFlowThisFuncLeave();
7648 return S_OK;
7649}
7650
7651/**
7652 * Returns @c true if the given session machine instance has an open direct
7653 * session (and optionally also for direct sessions which are closing) and
7654 * returns the session control machine instance if so.
7655 *
7656 * Note that when the method returns @c false, the arguments remain unchanged.
7657 *
7658 * @param aMachine Session machine object.
7659 * @param aControl Direct session control object (optional).
7660 * @param aRequireVM If true then only allow VM sessions.
7661 * @param aAllowClosing If true then additionally a session which is currently
7662 * being closed will also be allowed.
7663 *
7664 * @note locks this object for reading.
7665 */
7666bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7667 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7668 bool aRequireVM /*= false*/,
7669 bool aAllowClosing /*= false*/)
7670{
7671 AutoLimitedCaller autoCaller(this);
7672 AssertComRCReturn(autoCaller.rc(), false);
7673
7674 /* just return false for inaccessible machines */
7675 if (getObjectState().getState() != ObjectState::Ready)
7676 return false;
7677
7678 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7679
7680 if ( ( mData->mSession.mState == SessionState_Locked
7681 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7682 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7683 )
7684 {
7685 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7686
7687 aMachine = mData->mSession.mMachine;
7688
7689 if (aControl != NULL)
7690 *aControl = mData->mSession.mDirectControl;
7691
7692 return true;
7693 }
7694
7695 return false;
7696}
7697
7698/**
7699 * Returns @c true if the given machine has an spawning direct session.
7700 *
7701 * @note locks this object for reading.
7702 */
7703bool Machine::i_isSessionSpawning()
7704{
7705 AutoLimitedCaller autoCaller(this);
7706 AssertComRCReturn(autoCaller.rc(), false);
7707
7708 /* just return false for inaccessible machines */
7709 if (getObjectState().getState() != ObjectState::Ready)
7710 return false;
7711
7712 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7713
7714 if (mData->mSession.mState == SessionState_Spawning)
7715 return true;
7716
7717 return false;
7718}
7719
7720/**
7721 * Called from the client watcher thread to check for unexpected client process
7722 * death during Session_Spawning state (e.g. before it successfully opened a
7723 * direct session).
7724 *
7725 * On Win32 and on OS/2, this method is called only when we've got the
7726 * direct client's process termination notification, so it always returns @c
7727 * true.
7728 *
7729 * On other platforms, this method returns @c true if the client process is
7730 * terminated and @c false if it's still alive.
7731 *
7732 * @note Locks this object for writing.
7733 */
7734bool Machine::i_checkForSpawnFailure()
7735{
7736 AutoCaller autoCaller(this);
7737 if (!autoCaller.isOk())
7738 {
7739 /* nothing to do */
7740 LogFlowThisFunc(("Already uninitialized!\n"));
7741 return true;
7742 }
7743
7744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7745
7746 if (mData->mSession.mState != SessionState_Spawning)
7747 {
7748 /* nothing to do */
7749 LogFlowThisFunc(("Not spawning any more!\n"));
7750 return true;
7751 }
7752
7753 HRESULT rc = S_OK;
7754
7755 /* PID not yet initialized, skip check. */
7756 if (mData->mSession.mPID == NIL_RTPROCESS)
7757 return false;
7758
7759 RTPROCSTATUS status;
7760 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7761
7762 if (vrc != VERR_PROCESS_RUNNING)
7763 {
7764 Utf8Str strExtraInfo;
7765
7766#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7767 /* If the startup logfile exists and is of non-zero length, tell the
7768 user to look there for more details to encourage them to attach it
7769 when reporting startup issues. */
7770 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7771 uint64_t cbStartupLogFile = 0;
7772 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7773 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7774 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7775#endif
7776
7777 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7778 rc = setError(E_FAIL,
7779 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7780 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7781 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7782 rc = setError(E_FAIL,
7783 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7784 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7785 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7786 rc = setError(E_FAIL,
7787 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7788 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7789 else
7790 rc = setErrorBoth(E_FAIL, vrc,
7791 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7792 i_getName().c_str(), vrc, strExtraInfo.c_str());
7793 }
7794
7795 if (FAILED(rc))
7796 {
7797 /* Close the remote session, remove the remote control from the list
7798 * and reset session state to Closed (@note keep the code in sync with
7799 * the relevant part in LockMachine()). */
7800
7801 Assert(mData->mSession.mRemoteControls.size() == 1);
7802 if (mData->mSession.mRemoteControls.size() == 1)
7803 {
7804 ErrorInfoKeeper eik;
7805 mData->mSession.mRemoteControls.front()->Uninitialize();
7806 }
7807
7808 mData->mSession.mRemoteControls.clear();
7809 mData->mSession.mState = SessionState_Unlocked;
7810
7811 /* finalize the progress after setting the state */
7812 if (!mData->mSession.mProgress.isNull())
7813 {
7814 mData->mSession.mProgress->notifyComplete(rc);
7815 mData->mSession.mProgress.setNull();
7816 }
7817
7818 mData->mSession.mPID = NIL_RTPROCESS;
7819
7820 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7821 return true;
7822 }
7823
7824 return false;
7825}
7826
7827/**
7828 * Checks whether the machine can be registered. If so, commits and saves
7829 * all settings.
7830 *
7831 * @note Must be called from mParent's write lock. Locks this object and
7832 * children for writing.
7833 */
7834HRESULT Machine::i_prepareRegister()
7835{
7836 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7837
7838 AutoLimitedCaller autoCaller(this);
7839 AssertComRCReturnRC(autoCaller.rc());
7840
7841 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7842
7843 /* wait for state dependents to drop to zero */
7844 i_ensureNoStateDependencies();
7845
7846 if (!mData->mAccessible)
7847 return setError(VBOX_E_INVALID_OBJECT_STATE,
7848 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7849 mUserData->s.strName.c_str(),
7850 mData->mUuid.toString().c_str());
7851
7852 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7853
7854 if (mData->mRegistered)
7855 return setError(VBOX_E_INVALID_OBJECT_STATE,
7856 tr("The machine '%s' with UUID {%s} is already registered"),
7857 mUserData->s.strName.c_str(),
7858 mData->mUuid.toString().c_str());
7859
7860 HRESULT rc = S_OK;
7861
7862 // Ensure the settings are saved. If we are going to be registered and
7863 // no config file exists yet, create it by calling i_saveSettings() too.
7864 if ( (mData->flModifications)
7865 || (!mData->pMachineConfigFile->fileExists())
7866 )
7867 {
7868 rc = i_saveSettings(NULL);
7869 // no need to check whether VirtualBox.xml needs saving too since
7870 // we can't have a machine XML file rename pending
7871 if (FAILED(rc)) return rc;
7872 }
7873
7874 /* more config checking goes here */
7875
7876 if (SUCCEEDED(rc))
7877 {
7878 /* we may have had implicit modifications we want to fix on success */
7879 i_commit();
7880
7881 mData->mRegistered = true;
7882 }
7883 else
7884 {
7885 /* we may have had implicit modifications we want to cancel on failure*/
7886 i_rollback(false /* aNotify */);
7887 }
7888
7889 return rc;
7890}
7891
7892/**
7893 * Increases the number of objects dependent on the machine state or on the
7894 * registered state. Guarantees that these two states will not change at least
7895 * until #i_releaseStateDependency() is called.
7896 *
7897 * Depending on the @a aDepType value, additional state checks may be made.
7898 * These checks will set extended error info on failure. See
7899 * #i_checkStateDependency() for more info.
7900 *
7901 * If this method returns a failure, the dependency is not added and the caller
7902 * is not allowed to rely on any particular machine state or registration state
7903 * value and may return the failed result code to the upper level.
7904 *
7905 * @param aDepType Dependency type to add.
7906 * @param aState Current machine state (NULL if not interested).
7907 * @param aRegistered Current registered state (NULL if not interested).
7908 *
7909 * @note Locks this object for writing.
7910 */
7911HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7912 MachineState_T *aState /* = NULL */,
7913 BOOL *aRegistered /* = NULL */)
7914{
7915 AutoCaller autoCaller(this);
7916 AssertComRCReturnRC(autoCaller.rc());
7917
7918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7919
7920 HRESULT rc = i_checkStateDependency(aDepType);
7921 if (FAILED(rc)) return rc;
7922
7923 {
7924 if (mData->mMachineStateChangePending != 0)
7925 {
7926 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7927 * drop to zero so don't add more. It may make sense to wait a bit
7928 * and retry before reporting an error (since the pending state
7929 * transition should be really quick) but let's just assert for
7930 * now to see if it ever happens on practice. */
7931
7932 AssertFailed();
7933
7934 return setError(E_ACCESSDENIED,
7935 tr("Machine state change is in progress. Please retry the operation later."));
7936 }
7937
7938 ++mData->mMachineStateDeps;
7939 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7940 }
7941
7942 if (aState)
7943 *aState = mData->mMachineState;
7944 if (aRegistered)
7945 *aRegistered = mData->mRegistered;
7946
7947 return S_OK;
7948}
7949
7950/**
7951 * Decreases the number of objects dependent on the machine state.
7952 * Must always complete the #i_addStateDependency() call after the state
7953 * dependency is no more necessary.
7954 */
7955void Machine::i_releaseStateDependency()
7956{
7957 AutoCaller autoCaller(this);
7958 AssertComRCReturnVoid(autoCaller.rc());
7959
7960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7961
7962 /* releaseStateDependency() w/o addStateDependency()? */
7963 AssertReturnVoid(mData->mMachineStateDeps != 0);
7964 -- mData->mMachineStateDeps;
7965
7966 if (mData->mMachineStateDeps == 0)
7967 {
7968 /* inform i_ensureNoStateDependencies() that there are no more deps */
7969 if (mData->mMachineStateChangePending != 0)
7970 {
7971 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7972 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7973 }
7974 }
7975}
7976
7977Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7978{
7979 /* start with nothing found */
7980 Utf8Str strResult("");
7981
7982 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7983
7984 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7985 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7986 // found:
7987 strResult = it->second; // source is a Utf8Str
7988
7989 return strResult;
7990}
7991
7992// protected methods
7993/////////////////////////////////////////////////////////////////////////////
7994
7995/**
7996 * Performs machine state checks based on the @a aDepType value. If a check
7997 * fails, this method will set extended error info, otherwise it will return
7998 * S_OK. It is supposed, that on failure, the caller will immediately return
7999 * the return value of this method to the upper level.
8000 *
8001 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8002 *
8003 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8004 * current state of this machine object allows to change settings of the
8005 * machine (i.e. the machine is not registered, or registered but not running
8006 * and not saved). It is useful to call this method from Machine setters
8007 * before performing any change.
8008 *
8009 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8010 * as for MutableStateDep except that if the machine is saved, S_OK is also
8011 * returned. This is useful in setters which allow changing machine
8012 * properties when it is in the saved state.
8013 *
8014 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8015 * if the current state of this machine object allows to change runtime
8016 * changeable settings of the machine (i.e. the machine is not registered, or
8017 * registered but either running or not running and not saved). It is useful
8018 * to call this method from Machine setters before performing any changes to
8019 * runtime changeable settings.
8020 *
8021 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8022 * the same as for MutableOrRunningStateDep except that if the machine is
8023 * saved, S_OK is also returned. This is useful in setters which allow
8024 * changing runtime and saved state changeable machine properties.
8025 *
8026 * @param aDepType Dependency type to check.
8027 *
8028 * @note Non Machine based classes should use #i_addStateDependency() and
8029 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8030 * template.
8031 *
8032 * @note This method must be called from under this object's read or write
8033 * lock.
8034 */
8035HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8036{
8037 switch (aDepType)
8038 {
8039 case AnyStateDep:
8040 {
8041 break;
8042 }
8043 case MutableStateDep:
8044 {
8045 if ( mData->mRegistered
8046 && ( !i_isSessionMachine()
8047 || ( mData->mMachineState != MachineState_Aborted
8048 && mData->mMachineState != MachineState_Teleported
8049 && mData->mMachineState != MachineState_PoweredOff
8050 )
8051 )
8052 )
8053 return setError(VBOX_E_INVALID_VM_STATE,
8054 tr("The machine is not mutable (state is %s)"),
8055 Global::stringifyMachineState(mData->mMachineState));
8056 break;
8057 }
8058 case MutableOrSavedStateDep:
8059 {
8060 if ( mData->mRegistered
8061 && ( !i_isSessionMachine()
8062 || ( mData->mMachineState != MachineState_Aborted
8063 && mData->mMachineState != MachineState_Teleported
8064 && mData->mMachineState != MachineState_Saved
8065 && mData->mMachineState != MachineState_PoweredOff
8066 )
8067 )
8068 )
8069 return setError(VBOX_E_INVALID_VM_STATE,
8070 tr("The machine is not mutable or saved (state is %s)"),
8071 Global::stringifyMachineState(mData->mMachineState));
8072 break;
8073 }
8074 case MutableOrRunningStateDep:
8075 {
8076 if ( mData->mRegistered
8077 && ( !i_isSessionMachine()
8078 || ( mData->mMachineState != MachineState_Aborted
8079 && mData->mMachineState != MachineState_Teleported
8080 && mData->mMachineState != MachineState_PoweredOff
8081 && !Global::IsOnline(mData->mMachineState)
8082 )
8083 )
8084 )
8085 return setError(VBOX_E_INVALID_VM_STATE,
8086 tr("The machine is not mutable or running (state is %s)"),
8087 Global::stringifyMachineState(mData->mMachineState));
8088 break;
8089 }
8090 case MutableOrSavedOrRunningStateDep:
8091 {
8092 if ( mData->mRegistered
8093 && ( !i_isSessionMachine()
8094 || ( mData->mMachineState != MachineState_Aborted
8095 && mData->mMachineState != MachineState_Teleported
8096 && mData->mMachineState != MachineState_Saved
8097 && mData->mMachineState != MachineState_PoweredOff
8098 && !Global::IsOnline(mData->mMachineState)
8099 )
8100 )
8101 )
8102 return setError(VBOX_E_INVALID_VM_STATE,
8103 tr("The machine is not mutable, saved or running (state is %s)"),
8104 Global::stringifyMachineState(mData->mMachineState));
8105 break;
8106 }
8107 }
8108
8109 return S_OK;
8110}
8111
8112/**
8113 * Helper to initialize all associated child objects and allocate data
8114 * structures.
8115 *
8116 * This method must be called as a part of the object's initialization procedure
8117 * (usually done in the #init() method).
8118 *
8119 * @note Must be called only from #init() or from #i_registeredInit().
8120 */
8121HRESULT Machine::initDataAndChildObjects()
8122{
8123 AutoCaller autoCaller(this);
8124 AssertComRCReturnRC(autoCaller.rc());
8125 AssertReturn( getObjectState().getState() == ObjectState::InInit
8126 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8127
8128 AssertReturn(!mData->mAccessible, E_FAIL);
8129
8130 /* allocate data structures */
8131 mSSData.allocate();
8132 mUserData.allocate();
8133 mHWData.allocate();
8134 mMediumAttachments.allocate();
8135 mStorageControllers.allocate();
8136 mUSBControllers.allocate();
8137
8138 /* initialize mOSTypeId */
8139 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8140
8141/** @todo r=bird: init() methods never fails, right? Why don't we make them
8142 * return void then! */
8143
8144 /* create associated BIOS settings object */
8145 unconst(mBIOSSettings).createObject();
8146 mBIOSSettings->init(this);
8147
8148 /* create associated record settings object */
8149 unconst(mRecordingSettings).createObject();
8150 mRecordingSettings->init(this);
8151
8152 /* create the graphics adapter object (always present) */
8153 unconst(mGraphicsAdapter).createObject();
8154 mGraphicsAdapter->init(this);
8155
8156 /* create an associated VRDE object (default is disabled) */
8157 unconst(mVRDEServer).createObject();
8158 mVRDEServer->init(this);
8159
8160 /* create associated serial port objects */
8161 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8162 {
8163 unconst(mSerialPorts[slot]).createObject();
8164 mSerialPorts[slot]->init(this, slot);
8165 }
8166
8167 /* create associated parallel port objects */
8168 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8169 {
8170 unconst(mParallelPorts[slot]).createObject();
8171 mParallelPorts[slot]->init(this, slot);
8172 }
8173
8174 /* create the audio adapter object (always present, default is disabled) */
8175 unconst(mAudioAdapter).createObject();
8176 mAudioAdapter->init(this);
8177
8178 /* create the USB device filters object (always present) */
8179 unconst(mUSBDeviceFilters).createObject();
8180 mUSBDeviceFilters->init(this);
8181
8182 /* create associated network adapter objects */
8183 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8184 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8185 {
8186 unconst(mNetworkAdapters[slot]).createObject();
8187 mNetworkAdapters[slot]->init(this, slot);
8188 }
8189
8190 /* create the bandwidth control */
8191 unconst(mBandwidthControl).createObject();
8192 mBandwidthControl->init(this);
8193
8194 return S_OK;
8195}
8196
8197/**
8198 * Helper to uninitialize all associated child objects and to free all data
8199 * structures.
8200 *
8201 * This method must be called as a part of the object's uninitialization
8202 * procedure (usually done in the #uninit() method).
8203 *
8204 * @note Must be called only from #uninit() or from #i_registeredInit().
8205 */
8206void Machine::uninitDataAndChildObjects()
8207{
8208 AutoCaller autoCaller(this);
8209 AssertComRCReturnVoid(autoCaller.rc());
8210 AssertReturnVoid( getObjectState().getState() == ObjectState::InUninit
8211 || getObjectState().getState() == ObjectState::Limited);
8212
8213 /* tell all our other child objects we've been uninitialized */
8214 if (mBandwidthControl)
8215 {
8216 mBandwidthControl->uninit();
8217 unconst(mBandwidthControl).setNull();
8218 }
8219
8220 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8221 {
8222 if (mNetworkAdapters[slot])
8223 {
8224 mNetworkAdapters[slot]->uninit();
8225 unconst(mNetworkAdapters[slot]).setNull();
8226 }
8227 }
8228
8229 if (mUSBDeviceFilters)
8230 {
8231 mUSBDeviceFilters->uninit();
8232 unconst(mUSBDeviceFilters).setNull();
8233 }
8234
8235 if (mAudioAdapter)
8236 {
8237 mAudioAdapter->uninit();
8238 unconst(mAudioAdapter).setNull();
8239 }
8240
8241 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8242 {
8243 if (mParallelPorts[slot])
8244 {
8245 mParallelPorts[slot]->uninit();
8246 unconst(mParallelPorts[slot]).setNull();
8247 }
8248 }
8249
8250 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8251 {
8252 if (mSerialPorts[slot])
8253 {
8254 mSerialPorts[slot]->uninit();
8255 unconst(mSerialPorts[slot]).setNull();
8256 }
8257 }
8258
8259 if (mVRDEServer)
8260 {
8261 mVRDEServer->uninit();
8262 unconst(mVRDEServer).setNull();
8263 }
8264
8265 if (mGraphicsAdapter)
8266 {
8267 mGraphicsAdapter->uninit();
8268 unconst(mGraphicsAdapter).setNull();
8269 }
8270
8271 if (mBIOSSettings)
8272 {
8273 mBIOSSettings->uninit();
8274 unconst(mBIOSSettings).setNull();
8275 }
8276
8277 if (mRecordingSettings)
8278 {
8279 mRecordingSettings->uninit();
8280 unconst(mRecordingSettings).setNull();
8281 }
8282
8283 /* Deassociate media (only when a real Machine or a SnapshotMachine
8284 * instance is uninitialized; SessionMachine instances refer to real
8285 * Machine media). This is necessary for a clean re-initialization of
8286 * the VM after successfully re-checking the accessibility state. Note
8287 * that in case of normal Machine or SnapshotMachine uninitialization (as
8288 * a result of unregistering or deleting the snapshot), outdated media
8289 * attachments will already be uninitialized and deleted, so this
8290 * code will not affect them. */
8291 if ( !mMediumAttachments.isNull()
8292 && !i_isSessionMachine()
8293 )
8294 {
8295 for (MediumAttachmentList::const_iterator
8296 it = mMediumAttachments->begin();
8297 it != mMediumAttachments->end();
8298 ++it)
8299 {
8300 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8301 if (pMedium.isNull())
8302 continue;
8303 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8304 AssertComRC(rc);
8305 }
8306 }
8307
8308 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8309 {
8310 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8311 if (mData->mFirstSnapshot)
8312 {
8313 // snapshots tree is protected by machine write lock; strictly
8314 // this isn't necessary here since we're deleting the entire
8315 // machine, but otherwise we assert in Snapshot::uninit()
8316 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8317 mData->mFirstSnapshot->uninit();
8318 mData->mFirstSnapshot.setNull();
8319 }
8320
8321 mData->mCurrentSnapshot.setNull();
8322 }
8323
8324 /* free data structures (the essential mData structure is not freed here
8325 * since it may be still in use) */
8326 mMediumAttachments.free();
8327 mStorageControllers.free();
8328 mUSBControllers.free();
8329 mHWData.free();
8330 mUserData.free();
8331 mSSData.free();
8332}
8333
8334/**
8335 * Returns a pointer to the Machine object for this machine that acts like a
8336 * parent for complex machine data objects such as shared folders, etc.
8337 *
8338 * For primary Machine objects and for SnapshotMachine objects, returns this
8339 * object's pointer itself. For SessionMachine objects, returns the peer
8340 * (primary) machine pointer.
8341 */
8342Machine *Machine::i_getMachine()
8343{
8344 if (i_isSessionMachine())
8345 return (Machine*)mPeer;
8346 return this;
8347}
8348
8349/**
8350 * Makes sure that there are no machine state dependents. If necessary, waits
8351 * for the number of dependents to drop to zero.
8352 *
8353 * Make sure this method is called from under this object's write lock to
8354 * guarantee that no new dependents may be added when this method returns
8355 * control to the caller.
8356 *
8357 * @note Locks this object for writing. The lock will be released while waiting
8358 * (if necessary).
8359 *
8360 * @warning To be used only in methods that change the machine state!
8361 */
8362void Machine::i_ensureNoStateDependencies()
8363{
8364 AssertReturnVoid(isWriteLockOnCurrentThread());
8365
8366 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8367
8368 /* Wait for all state dependents if necessary */
8369 if (mData->mMachineStateDeps != 0)
8370 {
8371 /* lazy semaphore creation */
8372 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8373 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8374
8375 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8376 mData->mMachineStateDeps));
8377
8378 ++mData->mMachineStateChangePending;
8379
8380 /* reset the semaphore before waiting, the last dependent will signal
8381 * it */
8382 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8383
8384 alock.release();
8385
8386 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8387
8388 alock.acquire();
8389
8390 -- mData->mMachineStateChangePending;
8391 }
8392}
8393
8394/**
8395 * Changes the machine state and informs callbacks.
8396 *
8397 * This method is not intended to fail so it either returns S_OK or asserts (and
8398 * returns a failure).
8399 *
8400 * @note Locks this object for writing.
8401 */
8402HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8403{
8404 LogFlowThisFuncEnter();
8405 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8406 Assert(aMachineState != MachineState_Null);
8407
8408 AutoCaller autoCaller(this);
8409 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8410
8411 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8412
8413 /* wait for state dependents to drop to zero */
8414 i_ensureNoStateDependencies();
8415
8416 MachineState_T const enmOldState = mData->mMachineState;
8417 if (enmOldState != aMachineState)
8418 {
8419 mData->mMachineState = aMachineState;
8420 RTTimeNow(&mData->mLastStateChange);
8421
8422#ifdef VBOX_WITH_DTRACE_R3_MAIN
8423 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8424#endif
8425 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8426 }
8427
8428 LogFlowThisFuncLeave();
8429 return S_OK;
8430}
8431
8432/**
8433 * Searches for a shared folder with the given logical name
8434 * in the collection of shared folders.
8435 *
8436 * @param aName logical name of the shared folder
8437 * @param aSharedFolder where to return the found object
8438 * @param aSetError whether to set the error info if the folder is
8439 * not found
8440 * @return
8441 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8442 *
8443 * @note
8444 * must be called from under the object's lock!
8445 */
8446HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8447 ComObjPtr<SharedFolder> &aSharedFolder,
8448 bool aSetError /* = false */)
8449{
8450 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8451 for (HWData::SharedFolderList::const_iterator
8452 it = mHWData->mSharedFolders.begin();
8453 it != mHWData->mSharedFolders.end();
8454 ++it)
8455 {
8456 SharedFolder *pSF = *it;
8457 AutoCaller autoCaller(pSF);
8458 if (pSF->i_getName() == aName)
8459 {
8460 aSharedFolder = pSF;
8461 rc = S_OK;
8462 break;
8463 }
8464 }
8465
8466 if (aSetError && FAILED(rc))
8467 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8468
8469 return rc;
8470}
8471
8472/**
8473 * Initializes all machine instance data from the given settings structures
8474 * from XML. The exception is the machine UUID which needs special handling
8475 * depending on the caller's use case, so the caller needs to set that herself.
8476 *
8477 * This gets called in several contexts during machine initialization:
8478 *
8479 * -- When machine XML exists on disk already and needs to be loaded into memory,
8480 * for example, from #i_registeredInit() to load all registered machines on
8481 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8482 * attached to the machine should be part of some media registry already.
8483 *
8484 * -- During OVF import, when a machine config has been constructed from an
8485 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8486 * ensure that the media listed as attachments in the config (which have
8487 * been imported from the OVF) receive the correct registry ID.
8488 *
8489 * -- During VM cloning.
8490 *
8491 * @param config Machine settings from XML.
8492 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8493 * for each attached medium in the config.
8494 * @return
8495 */
8496HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8497 const Guid *puuidRegistry)
8498{
8499 // copy name, description, OS type, teleporter, UTC etc.
8500 mUserData->s = config.machineUserData;
8501
8502 // look up the object by Id to check it is valid
8503 ComObjPtr<GuestOSType> pGuestOSType;
8504 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8505 if (!pGuestOSType.isNull())
8506 mUserData->s.strOsType = pGuestOSType->i_id();
8507
8508 // stateFile (optional)
8509 if (config.strStateFile.isEmpty())
8510 mSSData->strStateFilePath.setNull();
8511 else
8512 {
8513 Utf8Str stateFilePathFull(config.strStateFile);
8514 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8515 if (RT_FAILURE(vrc))
8516 return setErrorBoth(E_FAIL, vrc,
8517 tr("Invalid saved state file path '%s' (%Rrc)"),
8518 config.strStateFile.c_str(),
8519 vrc);
8520 mSSData->strStateFilePath = stateFilePathFull;
8521 }
8522
8523 // snapshot folder needs special processing so set it again
8524 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8525 if (FAILED(rc)) return rc;
8526
8527 /* Copy the extra data items (config may or may not be the same as
8528 * mData->pMachineConfigFile) if necessary. When loading the XML files
8529 * from disk they are the same, but not for OVF import. */
8530 if (mData->pMachineConfigFile != &config)
8531 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8532
8533 /* currentStateModified (optional, default is true) */
8534 mData->mCurrentStateModified = config.fCurrentStateModified;
8535
8536 mData->mLastStateChange = config.timeLastStateChange;
8537
8538 /*
8539 * note: all mUserData members must be assigned prior this point because
8540 * we need to commit changes in order to let mUserData be shared by all
8541 * snapshot machine instances.
8542 */
8543 mUserData.commitCopy();
8544
8545 // machine registry, if present (must be loaded before snapshots)
8546 if (config.canHaveOwnMediaRegistry())
8547 {
8548 // determine machine folder
8549 Utf8Str strMachineFolder = i_getSettingsFileFull();
8550 strMachineFolder.stripFilename();
8551 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8552 config.mediaRegistry,
8553 strMachineFolder);
8554 if (FAILED(rc)) return rc;
8555 }
8556
8557 /* Snapshot node (optional) */
8558 size_t cRootSnapshots;
8559 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8560 {
8561 // there must be only one root snapshot
8562 Assert(cRootSnapshots == 1);
8563
8564 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8565
8566 rc = i_loadSnapshot(snap,
8567 config.uuidCurrentSnapshot,
8568 NULL); // no parent == first snapshot
8569 if (FAILED(rc)) return rc;
8570 }
8571
8572 // hardware data
8573 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8574 if (FAILED(rc)) return rc;
8575
8576 /*
8577 * NOTE: the assignment below must be the last thing to do,
8578 * otherwise it will be not possible to change the settings
8579 * somewhere in the code above because all setters will be
8580 * blocked by i_checkStateDependency(MutableStateDep).
8581 */
8582
8583 /* set the machine state to Aborted or Saved when appropriate */
8584 if (config.fAborted)
8585 {
8586 mSSData->strStateFilePath.setNull();
8587
8588 /* no need to use i_setMachineState() during init() */
8589 mData->mMachineState = MachineState_Aborted;
8590 }
8591 else if (!mSSData->strStateFilePath.isEmpty())
8592 {
8593 /* no need to use i_setMachineState() during init() */
8594 mData->mMachineState = MachineState_Saved;
8595 }
8596
8597 // after loading settings, we are no longer different from the XML on disk
8598 mData->flModifications = 0;
8599
8600 return S_OK;
8601}
8602
8603/**
8604 * Recursively loads all snapshots starting from the given.
8605 *
8606 * @param data snapshot settings.
8607 * @param aCurSnapshotId Current snapshot ID from the settings file.
8608 * @param aParentSnapshot Parent snapshot.
8609 */
8610HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8611 const Guid &aCurSnapshotId,
8612 Snapshot *aParentSnapshot)
8613{
8614 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8615 AssertReturn(!i_isSessionMachine(), E_FAIL);
8616
8617 HRESULT rc = S_OK;
8618
8619 Utf8Str strStateFile;
8620 if (!data.strStateFile.isEmpty())
8621 {
8622 /* optional */
8623 strStateFile = data.strStateFile;
8624 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8625 if (RT_FAILURE(vrc))
8626 return setErrorBoth(E_FAIL, vrc,
8627 tr("Invalid saved state file path '%s' (%Rrc)"),
8628 strStateFile.c_str(),
8629 vrc);
8630 }
8631
8632 /* create a snapshot machine object */
8633 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8634 pSnapshotMachine.createObject();
8635 rc = pSnapshotMachine->initFromSettings(this,
8636 data.hardware,
8637 &data.debugging,
8638 &data.autostart,
8639 data.uuid.ref(),
8640 strStateFile);
8641 if (FAILED(rc)) return rc;
8642
8643 /* create a snapshot object */
8644 ComObjPtr<Snapshot> pSnapshot;
8645 pSnapshot.createObject();
8646 /* initialize the snapshot */
8647 rc = pSnapshot->init(mParent, // VirtualBox object
8648 data.uuid,
8649 data.strName,
8650 data.strDescription,
8651 data.timestamp,
8652 pSnapshotMachine,
8653 aParentSnapshot);
8654 if (FAILED(rc)) return rc;
8655
8656 /* memorize the first snapshot if necessary */
8657 if (!mData->mFirstSnapshot)
8658 mData->mFirstSnapshot = pSnapshot;
8659
8660 /* memorize the current snapshot when appropriate */
8661 if ( !mData->mCurrentSnapshot
8662 && pSnapshot->i_getId() == aCurSnapshotId
8663 )
8664 mData->mCurrentSnapshot = pSnapshot;
8665
8666 // now create the children
8667 for (settings::SnapshotsList::const_iterator
8668 it = data.llChildSnapshots.begin();
8669 it != data.llChildSnapshots.end();
8670 ++it)
8671 {
8672 const settings::Snapshot &childData = *it;
8673 // recurse
8674 rc = i_loadSnapshot(childData,
8675 aCurSnapshotId,
8676 pSnapshot); // parent = the one we created above
8677 if (FAILED(rc)) return rc;
8678 }
8679
8680 return rc;
8681}
8682
8683/**
8684 * Loads settings into mHWData.
8685 *
8686 * @param puuidRegistry Registry ID.
8687 * @param puuidSnapshot Snapshot ID
8688 * @param data Reference to the hardware settings.
8689 * @param pDbg Pointer to the debugging settings.
8690 * @param pAutostart Pointer to the autostart settings.
8691 */
8692HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8693 const Guid *puuidSnapshot,
8694 const settings::Hardware &data,
8695 const settings::Debugging *pDbg,
8696 const settings::Autostart *pAutostart)
8697{
8698 AssertReturn(!i_isSessionMachine(), E_FAIL);
8699
8700 HRESULT rc = S_OK;
8701
8702 try
8703 {
8704 ComObjPtr<GuestOSType> pGuestOSType;
8705 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8706
8707 /* The hardware version attribute (optional). */
8708 mHWData->mHWVersion = data.strVersion;
8709 mHWData->mHardwareUUID = data.uuid;
8710
8711 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8712 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8713 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8714 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8715 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8716 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8717 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8718 mHWData->mHWVirtExVirtVmsaveVmload = data.fVirtVmsaveVmload;
8719 mHWData->mPAEEnabled = data.fPAE;
8720 mHWData->mLongMode = data.enmLongMode;
8721 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8722 mHWData->mAPIC = data.fAPIC;
8723 mHWData->mX2APIC = data.fX2APIC;
8724 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8725 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8726 mHWData->mSpecCtrl = data.fSpecCtrl;
8727 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8728 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8729 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8730 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8731 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8732 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8733 mHWData->mCPUCount = data.cCPUs;
8734 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8735 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8736 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8737 mHWData->mCpuProfile = data.strCpuProfile;
8738
8739 // cpu
8740 if (mHWData->mCPUHotPlugEnabled)
8741 {
8742 for (settings::CpuList::const_iterator
8743 it = data.llCpus.begin();
8744 it != data.llCpus.end();
8745 ++it)
8746 {
8747 const settings::Cpu &cpu = *it;
8748
8749 mHWData->mCPUAttached[cpu.ulId] = true;
8750 }
8751 }
8752
8753 // cpuid leafs
8754 for (settings::CpuIdLeafsList::const_iterator
8755 it = data.llCpuIdLeafs.begin();
8756 it != data.llCpuIdLeafs.end();
8757 ++it)
8758 {
8759 const settings::CpuIdLeaf &rLeaf= *it;
8760 if ( rLeaf.idx < UINT32_C(0x20)
8761 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8762 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8763 mHWData->mCpuIdLeafList.push_back(rLeaf);
8764 /* else: just ignore */
8765 }
8766
8767 mHWData->mMemorySize = data.ulMemorySizeMB;
8768 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8769
8770 // boot order
8771 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8772 {
8773 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8774 if (it == data.mapBootOrder.end())
8775 mHWData->mBootOrder[i] = DeviceType_Null;
8776 else
8777 mHWData->mBootOrder[i] = it->second;
8778 }
8779
8780 mHWData->mFirmwareType = data.firmwareType;
8781 mHWData->mPointingHIDType = data.pointingHIDType;
8782 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8783 mHWData->mChipsetType = data.chipsetType;
8784 mHWData->mIommuType = data.iommuType;
8785 mHWData->mParavirtProvider = data.paravirtProvider;
8786 mHWData->mParavirtDebug = data.strParavirtDebug;
8787 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8788 mHWData->mHPETEnabled = data.fHPETEnabled;
8789
8790 /* GraphicsAdapter */
8791 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8792 if (FAILED(rc)) return rc;
8793
8794 /* VRDEServer */
8795 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8796 if (FAILED(rc)) return rc;
8797
8798 /* BIOS */
8799 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8800 if (FAILED(rc)) return rc;
8801
8802 /* Recording settings */
8803 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8804 if (FAILED(rc)) return rc;
8805
8806 // Bandwidth control (must come before network adapters)
8807 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8808 if (FAILED(rc)) return rc;
8809
8810 /* USB controllers */
8811 for (settings::USBControllerList::const_iterator
8812 it = data.usbSettings.llUSBControllers.begin();
8813 it != data.usbSettings.llUSBControllers.end();
8814 ++it)
8815 {
8816 const settings::USBController &settingsCtrl = *it;
8817 ComObjPtr<USBController> newCtrl;
8818
8819 newCtrl.createObject();
8820 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8821 mUSBControllers->push_back(newCtrl);
8822 }
8823
8824 /* USB device filters */
8825 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8826 if (FAILED(rc)) return rc;
8827
8828 // network adapters (establish array size first and apply defaults, to
8829 // ensure reading the same settings as we saved, since the list skips
8830 // adapters having defaults)
8831 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8832 size_t oldCount = mNetworkAdapters.size();
8833 if (newCount > oldCount)
8834 {
8835 mNetworkAdapters.resize(newCount);
8836 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8837 {
8838 unconst(mNetworkAdapters[slot]).createObject();
8839 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8840 }
8841 }
8842 else if (newCount < oldCount)
8843 mNetworkAdapters.resize(newCount);
8844 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8845 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8846 for (settings::NetworkAdaptersList::const_iterator
8847 it = data.llNetworkAdapters.begin();
8848 it != data.llNetworkAdapters.end();
8849 ++it)
8850 {
8851 const settings::NetworkAdapter &nic = *it;
8852
8853 /* slot uniqueness is guaranteed by XML Schema */
8854 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8855 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8856 if (FAILED(rc)) return rc;
8857 }
8858
8859 // serial ports (establish defaults first, to ensure reading the same
8860 // settings as we saved, since the list skips ports having defaults)
8861 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8862 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8863 for (settings::SerialPortsList::const_iterator
8864 it = data.llSerialPorts.begin();
8865 it != data.llSerialPorts.end();
8866 ++it)
8867 {
8868 const settings::SerialPort &s = *it;
8869
8870 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8871 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8872 if (FAILED(rc)) return rc;
8873 }
8874
8875 // parallel ports (establish defaults first, to ensure reading the same
8876 // settings as we saved, since the list skips ports having defaults)
8877 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8878 mParallelPorts[i]->i_applyDefaults();
8879 for (settings::ParallelPortsList::const_iterator
8880 it = data.llParallelPorts.begin();
8881 it != data.llParallelPorts.end();
8882 ++it)
8883 {
8884 const settings::ParallelPort &p = *it;
8885
8886 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8887 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8888 if (FAILED(rc)) return rc;
8889 }
8890
8891 /* AudioAdapter */
8892 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8893 if (FAILED(rc)) return rc;
8894
8895 /* storage controllers */
8896 rc = i_loadStorageControllers(data.storage,
8897 puuidRegistry,
8898 puuidSnapshot);
8899 if (FAILED(rc)) return rc;
8900
8901 /* Shared folders */
8902 for (settings::SharedFoldersList::const_iterator
8903 it = data.llSharedFolders.begin();
8904 it != data.llSharedFolders.end();
8905 ++it)
8906 {
8907 const settings::SharedFolder &sf = *it;
8908
8909 ComObjPtr<SharedFolder> sharedFolder;
8910 /* Check for double entries. Not allowed! */
8911 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8912 if (SUCCEEDED(rc))
8913 return setError(VBOX_E_OBJECT_IN_USE,
8914 tr("Shared folder named '%s' already exists"),
8915 sf.strName.c_str());
8916
8917 /* Create the new shared folder. Don't break on error. This will be
8918 * reported when the machine starts. */
8919 sharedFolder.createObject();
8920 rc = sharedFolder->init(i_getMachine(),
8921 sf.strName,
8922 sf.strHostPath,
8923 RT_BOOL(sf.fWritable),
8924 RT_BOOL(sf.fAutoMount),
8925 sf.strAutoMountPoint,
8926 false /* fFailOnError */);
8927 if (FAILED(rc)) return rc;
8928 mHWData->mSharedFolders.push_back(sharedFolder);
8929 }
8930
8931 // Clipboard
8932 mHWData->mClipboardMode = data.clipboardMode;
8933 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8934
8935 // drag'n'drop
8936 mHWData->mDnDMode = data.dndMode;
8937
8938 // guest settings
8939 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8940
8941 // IO settings
8942 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8943 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8944
8945 // Host PCI devices
8946 for (settings::HostPCIDeviceAttachmentList::const_iterator
8947 it = data.pciAttachments.begin();
8948 it != data.pciAttachments.end();
8949 ++it)
8950 {
8951 const settings::HostPCIDeviceAttachment &hpda = *it;
8952 ComObjPtr<PCIDeviceAttachment> pda;
8953
8954 pda.createObject();
8955 pda->i_loadSettings(this, hpda);
8956 mHWData->mPCIDeviceAssignments.push_back(pda);
8957 }
8958
8959 /*
8960 * (The following isn't really real hardware, but it lives in HWData
8961 * for reasons of convenience.)
8962 */
8963
8964#ifdef VBOX_WITH_GUEST_PROPS
8965 /* Guest properties (optional) */
8966
8967 /* Only load transient guest properties for configs which have saved
8968 * state, because there shouldn't be any for powered off VMs. The same
8969 * logic applies for snapshots, as offline snapshots shouldn't have
8970 * any such properties. They confuse the code in various places.
8971 * Note: can't rely on the machine state, as it isn't set yet. */
8972 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8973 /* apologies for the hacky unconst() usage, but this needs hacking
8974 * actually inconsistent settings into consistency, otherwise there
8975 * will be some corner cases where the inconsistency survives
8976 * surprisingly long without getting fixed, especially for snapshots
8977 * as there are no config changes. */
8978 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8979 for (settings::GuestPropertiesList::iterator
8980 it = llGuestProperties.begin();
8981 it != llGuestProperties.end();
8982 /*nothing*/)
8983 {
8984 const settings::GuestProperty &prop = *it;
8985 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8986 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8987 if ( fSkipTransientGuestProperties
8988 && ( fFlags & GUEST_PROP_F_TRANSIENT
8989 || fFlags & GUEST_PROP_F_TRANSRESET))
8990 {
8991 it = llGuestProperties.erase(it);
8992 continue;
8993 }
8994 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8995 mHWData->mGuestProperties[prop.strName] = property;
8996 ++it;
8997 }
8998#endif /* VBOX_WITH_GUEST_PROPS defined */
8999
9000 rc = i_loadDebugging(pDbg);
9001 if (FAILED(rc))
9002 return rc;
9003
9004 mHWData->mAutostart = *pAutostart;
9005
9006 /* default frontend */
9007 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9008 }
9009 catch (std::bad_alloc &)
9010 {
9011 return E_OUTOFMEMORY;
9012 }
9013
9014 AssertComRC(rc);
9015 return rc;
9016}
9017
9018/**
9019 * Called from i_loadHardware() to load the debugging settings of the
9020 * machine.
9021 *
9022 * @param pDbg Pointer to the settings.
9023 */
9024HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9025{
9026 mHWData->mDebugging = *pDbg;
9027 /* no more processing currently required, this will probably change. */
9028 return S_OK;
9029}
9030
9031/**
9032 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9033 *
9034 * @param data storage settings.
9035 * @param puuidRegistry media registry ID to set media to or NULL;
9036 * see Machine::i_loadMachineDataFromSettings()
9037 * @param puuidSnapshot snapshot ID
9038 * @return
9039 */
9040HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9041 const Guid *puuidRegistry,
9042 const Guid *puuidSnapshot)
9043{
9044 AssertReturn(!i_isSessionMachine(), E_FAIL);
9045
9046 HRESULT rc = S_OK;
9047
9048 for (settings::StorageControllersList::const_iterator
9049 it = data.llStorageControllers.begin();
9050 it != data.llStorageControllers.end();
9051 ++it)
9052 {
9053 const settings::StorageController &ctlData = *it;
9054
9055 ComObjPtr<StorageController> pCtl;
9056 /* Try to find one with the name first. */
9057 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9058 if (SUCCEEDED(rc))
9059 return setError(VBOX_E_OBJECT_IN_USE,
9060 tr("Storage controller named '%s' already exists"),
9061 ctlData.strName.c_str());
9062
9063 pCtl.createObject();
9064 rc = pCtl->init(this,
9065 ctlData.strName,
9066 ctlData.storageBus,
9067 ctlData.ulInstance,
9068 ctlData.fBootable);
9069 if (FAILED(rc)) return rc;
9070
9071 mStorageControllers->push_back(pCtl);
9072
9073 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9074 if (FAILED(rc)) return rc;
9075
9076 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9077 if (FAILED(rc)) return rc;
9078
9079 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9080 if (FAILED(rc)) return rc;
9081
9082 /* Load the attached devices now. */
9083 rc = i_loadStorageDevices(pCtl,
9084 ctlData,
9085 puuidRegistry,
9086 puuidSnapshot);
9087 if (FAILED(rc)) return rc;
9088 }
9089
9090 return S_OK;
9091}
9092
9093/**
9094 * Called from i_loadStorageControllers for a controller's devices.
9095 *
9096 * @param aStorageController
9097 * @param data
9098 * @param puuidRegistry media registry ID to set media to or NULL; see
9099 * Machine::i_loadMachineDataFromSettings()
9100 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9101 * @return
9102 */
9103HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9104 const settings::StorageController &data,
9105 const Guid *puuidRegistry,
9106 const Guid *puuidSnapshot)
9107{
9108 HRESULT rc = S_OK;
9109
9110 /* paranoia: detect duplicate attachments */
9111 for (settings::AttachedDevicesList::const_iterator
9112 it = data.llAttachedDevices.begin();
9113 it != data.llAttachedDevices.end();
9114 ++it)
9115 {
9116 const settings::AttachedDevice &ad = *it;
9117
9118 for (settings::AttachedDevicesList::const_iterator it2 = it;
9119 it2 != data.llAttachedDevices.end();
9120 ++it2)
9121 {
9122 if (it == it2)
9123 continue;
9124
9125 const settings::AttachedDevice &ad2 = *it2;
9126
9127 if ( ad.lPort == ad2.lPort
9128 && ad.lDevice == ad2.lDevice)
9129 {
9130 return setError(E_FAIL,
9131 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9132 aStorageController->i_getName().c_str(),
9133 ad.lPort,
9134 ad.lDevice,
9135 mUserData->s.strName.c_str());
9136 }
9137 }
9138 }
9139
9140 for (settings::AttachedDevicesList::const_iterator
9141 it = data.llAttachedDevices.begin();
9142 it != data.llAttachedDevices.end();
9143 ++it)
9144 {
9145 const settings::AttachedDevice &dev = *it;
9146 ComObjPtr<Medium> medium;
9147
9148 switch (dev.deviceType)
9149 {
9150 case DeviceType_Floppy:
9151 case DeviceType_DVD:
9152 if (dev.strHostDriveSrc.isNotEmpty())
9153 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9154 false /* fRefresh */, medium);
9155 else
9156 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9157 dev.uuid,
9158 false /* fRefresh */,
9159 false /* aSetError */,
9160 medium);
9161 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9162 // This is not an error. The host drive or UUID might have vanished, so just go
9163 // ahead without this removeable medium attachment
9164 rc = S_OK;
9165 break;
9166
9167 case DeviceType_HardDisk:
9168 {
9169 /* find a hard disk by UUID */
9170 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9171 if (FAILED(rc))
9172 {
9173 if (i_isSnapshotMachine())
9174 {
9175 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9176 // so the user knows that the bad disk is in a snapshot somewhere
9177 com::ErrorInfo info;
9178 return setError(E_FAIL,
9179 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9180 puuidSnapshot->raw(),
9181 info.getText().raw());
9182 }
9183 else
9184 return rc;
9185 }
9186
9187 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9188
9189 if (medium->i_getType() == MediumType_Immutable)
9190 {
9191 if (i_isSnapshotMachine())
9192 return setError(E_FAIL,
9193 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9194 "of the virtual machine '%s' ('%s')"),
9195 medium->i_getLocationFull().c_str(),
9196 dev.uuid.raw(),
9197 puuidSnapshot->raw(),
9198 mUserData->s.strName.c_str(),
9199 mData->m_strConfigFileFull.c_str());
9200
9201 return setError(E_FAIL,
9202 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9203 medium->i_getLocationFull().c_str(),
9204 dev.uuid.raw(),
9205 mUserData->s.strName.c_str(),
9206 mData->m_strConfigFileFull.c_str());
9207 }
9208
9209 if (medium->i_getType() == MediumType_MultiAttach)
9210 {
9211 if (i_isSnapshotMachine())
9212 return setError(E_FAIL,
9213 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9214 "of the virtual machine '%s' ('%s')"),
9215 medium->i_getLocationFull().c_str(),
9216 dev.uuid.raw(),
9217 puuidSnapshot->raw(),
9218 mUserData->s.strName.c_str(),
9219 mData->m_strConfigFileFull.c_str());
9220
9221 return setError(E_FAIL,
9222 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9223 medium->i_getLocationFull().c_str(),
9224 dev.uuid.raw(),
9225 mUserData->s.strName.c_str(),
9226 mData->m_strConfigFileFull.c_str());
9227 }
9228
9229 if ( !i_isSnapshotMachine()
9230 && medium->i_getChildren().size() != 0
9231 )
9232 return setError(E_FAIL,
9233 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9234 "because it has %d differencing child hard disks"),
9235 medium->i_getLocationFull().c_str(),
9236 dev.uuid.raw(),
9237 mUserData->s.strName.c_str(),
9238 mData->m_strConfigFileFull.c_str(),
9239 medium->i_getChildren().size());
9240
9241 if (i_findAttachment(*mMediumAttachments.data(),
9242 medium))
9243 return setError(E_FAIL,
9244 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9245 medium->i_getLocationFull().c_str(),
9246 dev.uuid.raw(),
9247 mUserData->s.strName.c_str(),
9248 mData->m_strConfigFileFull.c_str());
9249
9250 break;
9251 }
9252
9253 default:
9254 return setError(E_FAIL,
9255 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9256 medium->i_getLocationFull().c_str(),
9257 mUserData->s.strName.c_str(),
9258 mData->m_strConfigFileFull.c_str());
9259 }
9260
9261 if (FAILED(rc))
9262 break;
9263
9264 /* Bandwidth groups are loaded at this point. */
9265 ComObjPtr<BandwidthGroup> pBwGroup;
9266
9267 if (!dev.strBwGroup.isEmpty())
9268 {
9269 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9270 if (FAILED(rc))
9271 return setError(E_FAIL,
9272 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9273 medium->i_getLocationFull().c_str(),
9274 dev.strBwGroup.c_str(),
9275 mUserData->s.strName.c_str(),
9276 mData->m_strConfigFileFull.c_str());
9277 pBwGroup->i_reference();
9278 }
9279
9280 const Utf8Str controllerName = aStorageController->i_getName();
9281 ComObjPtr<MediumAttachment> pAttachment;
9282 pAttachment.createObject();
9283 rc = pAttachment->init(this,
9284 medium,
9285 controllerName,
9286 dev.lPort,
9287 dev.lDevice,
9288 dev.deviceType,
9289 false,
9290 dev.fPassThrough,
9291 dev.fTempEject,
9292 dev.fNonRotational,
9293 dev.fDiscard,
9294 dev.fHotPluggable,
9295 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9296 if (FAILED(rc)) break;
9297
9298 /* associate the medium with this machine and snapshot */
9299 if (!medium.isNull())
9300 {
9301 AutoCaller medCaller(medium);
9302 if (FAILED(medCaller.rc())) return medCaller.rc();
9303 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9304
9305 if (i_isSnapshotMachine())
9306 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9307 else
9308 rc = medium->i_addBackReference(mData->mUuid);
9309 /* If the medium->addBackReference fails it sets an appropriate
9310 * error message, so no need to do any guesswork here. */
9311
9312 if (puuidRegistry)
9313 // caller wants registry ID to be set on all attached media (OVF import case)
9314 medium->i_addRegistry(*puuidRegistry);
9315 }
9316
9317 if (FAILED(rc))
9318 break;
9319
9320 /* back up mMediumAttachments to let registeredInit() properly rollback
9321 * on failure (= limited accessibility) */
9322 i_setModified(IsModified_Storage);
9323 mMediumAttachments.backup();
9324 mMediumAttachments->push_back(pAttachment);
9325 }
9326
9327 return rc;
9328}
9329
9330/**
9331 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9332 *
9333 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9334 * @param aSnapshot where to return the found snapshot
9335 * @param aSetError true to set extended error info on failure
9336 */
9337HRESULT Machine::i_findSnapshotById(const Guid &aId,
9338 ComObjPtr<Snapshot> &aSnapshot,
9339 bool aSetError /* = false */)
9340{
9341 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9342
9343 if (!mData->mFirstSnapshot)
9344 {
9345 if (aSetError)
9346 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9347 return E_FAIL;
9348 }
9349
9350 if (aId.isZero())
9351 aSnapshot = mData->mFirstSnapshot;
9352 else
9353 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9354
9355 if (!aSnapshot)
9356 {
9357 if (aSetError)
9358 return setError(E_FAIL,
9359 tr("Could not find a snapshot with UUID {%s}"),
9360 aId.toString().c_str());
9361 return E_FAIL;
9362 }
9363
9364 return S_OK;
9365}
9366
9367/**
9368 * Returns the snapshot with the given name or fails of no such snapshot.
9369 *
9370 * @param strName snapshot name to find
9371 * @param aSnapshot where to return the found snapshot
9372 * @param aSetError true to set extended error info on failure
9373 */
9374HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9375 ComObjPtr<Snapshot> &aSnapshot,
9376 bool aSetError /* = false */)
9377{
9378 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9379
9380 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9381
9382 if (!mData->mFirstSnapshot)
9383 {
9384 if (aSetError)
9385 return setError(VBOX_E_OBJECT_NOT_FOUND,
9386 tr("This machine does not have any snapshots"));
9387 return VBOX_E_OBJECT_NOT_FOUND;
9388 }
9389
9390 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9391
9392 if (!aSnapshot)
9393 {
9394 if (aSetError)
9395 return setError(VBOX_E_OBJECT_NOT_FOUND,
9396 tr("Could not find a snapshot named '%s'"), strName.c_str());
9397 return VBOX_E_OBJECT_NOT_FOUND;
9398 }
9399
9400 return S_OK;
9401}
9402
9403/**
9404 * Returns a storage controller object with the given name.
9405 *
9406 * @param aName storage controller name to find
9407 * @param aStorageController where to return the found storage controller
9408 * @param aSetError true to set extended error info on failure
9409 */
9410HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9411 ComObjPtr<StorageController> &aStorageController,
9412 bool aSetError /* = false */)
9413{
9414 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9415
9416 for (StorageControllerList::const_iterator
9417 it = mStorageControllers->begin();
9418 it != mStorageControllers->end();
9419 ++it)
9420 {
9421 if ((*it)->i_getName() == aName)
9422 {
9423 aStorageController = (*it);
9424 return S_OK;
9425 }
9426 }
9427
9428 if (aSetError)
9429 return setError(VBOX_E_OBJECT_NOT_FOUND,
9430 tr("Could not find a storage controller named '%s'"),
9431 aName.c_str());
9432 return VBOX_E_OBJECT_NOT_FOUND;
9433}
9434
9435/**
9436 * Returns a USB controller object with the given name.
9437 *
9438 * @param aName USB controller name to find
9439 * @param aUSBController where to return the found USB controller
9440 * @param aSetError true to set extended error info on failure
9441 */
9442HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9443 ComObjPtr<USBController> &aUSBController,
9444 bool aSetError /* = false */)
9445{
9446 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9447
9448 for (USBControllerList::const_iterator
9449 it = mUSBControllers->begin();
9450 it != mUSBControllers->end();
9451 ++it)
9452 {
9453 if ((*it)->i_getName() == aName)
9454 {
9455 aUSBController = (*it);
9456 return S_OK;
9457 }
9458 }
9459
9460 if (aSetError)
9461 return setError(VBOX_E_OBJECT_NOT_FOUND,
9462 tr("Could not find a storage controller named '%s'"),
9463 aName.c_str());
9464 return VBOX_E_OBJECT_NOT_FOUND;
9465}
9466
9467/**
9468 * Returns the number of USB controller instance of the given type.
9469 *
9470 * @param enmType USB controller type.
9471 */
9472ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9473{
9474 ULONG cCtrls = 0;
9475
9476 for (USBControllerList::const_iterator
9477 it = mUSBControllers->begin();
9478 it != mUSBControllers->end();
9479 ++it)
9480 {
9481 if ((*it)->i_getControllerType() == enmType)
9482 cCtrls++;
9483 }
9484
9485 return cCtrls;
9486}
9487
9488HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9489 MediumAttachmentList &atts)
9490{
9491 AutoCaller autoCaller(this);
9492 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9493
9494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9495
9496 for (MediumAttachmentList::const_iterator
9497 it = mMediumAttachments->begin();
9498 it != mMediumAttachments->end();
9499 ++it)
9500 {
9501 const ComObjPtr<MediumAttachment> &pAtt = *it;
9502 // should never happen, but deal with NULL pointers in the list.
9503 AssertContinue(!pAtt.isNull());
9504
9505 // getControllerName() needs caller+read lock
9506 AutoCaller autoAttCaller(pAtt);
9507 if (FAILED(autoAttCaller.rc()))
9508 {
9509 atts.clear();
9510 return autoAttCaller.rc();
9511 }
9512 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9513
9514 if (pAtt->i_getControllerName() == aName)
9515 atts.push_back(pAtt);
9516 }
9517
9518 return S_OK;
9519}
9520
9521
9522/**
9523 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9524 * file if the machine name was changed and about creating a new settings file
9525 * if this is a new machine.
9526 *
9527 * @note Must be never called directly but only from #saveSettings().
9528 */
9529HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9530{
9531 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9532
9533 HRESULT rc = S_OK;
9534
9535 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9536
9537 /// @todo need to handle primary group change, too
9538
9539 /* attempt to rename the settings file if machine name is changed */
9540 if ( mUserData->s.fNameSync
9541 && mUserData.isBackedUp()
9542 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9543 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9544 )
9545 {
9546 bool dirRenamed = false;
9547 bool fileRenamed = false;
9548
9549 Utf8Str configFile, newConfigFile;
9550 Utf8Str configFilePrev, newConfigFilePrev;
9551 Utf8Str NVRAMFile, newNVRAMFile;
9552 Utf8Str configDir, newConfigDir;
9553
9554 do
9555 {
9556 int vrc = VINF_SUCCESS;
9557
9558 Utf8Str name = mUserData.backedUpData()->s.strName;
9559 Utf8Str newName = mUserData->s.strName;
9560 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9561 if (group == "/")
9562 group.setNull();
9563 Utf8Str newGroup = mUserData->s.llGroups.front();
9564 if (newGroup == "/")
9565 newGroup.setNull();
9566
9567 configFile = mData->m_strConfigFileFull;
9568
9569 /* first, rename the directory if it matches the group and machine name */
9570 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9571 /** @todo hack, make somehow use of ComposeMachineFilename */
9572 if (mUserData->s.fDirectoryIncludesUUID)
9573 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9574 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9575 /** @todo hack, make somehow use of ComposeMachineFilename */
9576 if (mUserData->s.fDirectoryIncludesUUID)
9577 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9578 configDir = configFile;
9579 configDir.stripFilename();
9580 newConfigDir = configDir;
9581 if ( configDir.length() >= groupPlusName.length()
9582 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9583 groupPlusName.c_str()))
9584 {
9585 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9586 Utf8Str newConfigBaseDir(newConfigDir);
9587 newConfigDir.append(newGroupPlusName);
9588 /* consistency: use \ if appropriate on the platform */
9589 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9590 /* new dir and old dir cannot be equal here because of 'if'
9591 * above and because name != newName */
9592 Assert(configDir != newConfigDir);
9593 if (!fSettingsFileIsNew)
9594 {
9595 /* perform real rename only if the machine is not new */
9596 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9597 if ( vrc == VERR_FILE_NOT_FOUND
9598 || vrc == VERR_PATH_NOT_FOUND)
9599 {
9600 /* create the parent directory, then retry renaming */
9601 Utf8Str parent(newConfigDir);
9602 parent.stripFilename();
9603 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9604 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9605 }
9606 if (RT_FAILURE(vrc))
9607 {
9608 rc = setErrorBoth(E_FAIL, vrc,
9609 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9610 configDir.c_str(),
9611 newConfigDir.c_str(),
9612 vrc);
9613 break;
9614 }
9615 /* delete subdirectories which are no longer needed */
9616 Utf8Str dir(configDir);
9617 dir.stripFilename();
9618 while (dir != newConfigBaseDir && dir != ".")
9619 {
9620 vrc = RTDirRemove(dir.c_str());
9621 if (RT_FAILURE(vrc))
9622 break;
9623 dir.stripFilename();
9624 }
9625 dirRenamed = true;
9626 }
9627 }
9628
9629 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9630
9631 /* then try to rename the settings file itself */
9632 if (newConfigFile != configFile)
9633 {
9634 /* get the path to old settings file in renamed directory */
9635 Assert(mData->m_strConfigFileFull == configFile);
9636 configFile.printf("%s%c%s",
9637 newConfigDir.c_str(),
9638 RTPATH_DELIMITER,
9639 RTPathFilename(mData->m_strConfigFileFull.c_str()));
9640 if (!fSettingsFileIsNew)
9641 {
9642 /* perform real rename only if the machine is not new */
9643 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9644 if (RT_FAILURE(vrc))
9645 {
9646 rc = setErrorBoth(E_FAIL, vrc,
9647 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9648 configFile.c_str(),
9649 newConfigFile.c_str(),
9650 vrc);
9651 break;
9652 }
9653 fileRenamed = true;
9654 configFilePrev = configFile;
9655 configFilePrev += "-prev";
9656 newConfigFilePrev = newConfigFile;
9657 newConfigFilePrev += "-prev";
9658 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9659 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9660 if (NVRAMFile.isNotEmpty())
9661 {
9662 // in the NVRAM file path, replace the old directory with the new directory
9663 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9664 {
9665 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9666 NVRAMFile = newConfigDir + strNVRAMFile;
9667 }
9668 newNVRAMFile = newConfigFile;
9669 newNVRAMFile.stripSuffix();
9670 newNVRAMFile += ".nvram";
9671 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9672 }
9673 }
9674 }
9675
9676 // update m_strConfigFileFull amd mConfigFile
9677 mData->m_strConfigFileFull = newConfigFile;
9678 // compute the relative path too
9679 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9680
9681 // store the old and new so that VirtualBox::i_saveSettings() can update
9682 // the media registry
9683 if ( mData->mRegistered
9684 && (configDir != newConfigDir || configFile != newConfigFile))
9685 {
9686 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9687
9688 if (pfNeedsGlobalSaveSettings)
9689 *pfNeedsGlobalSaveSettings = true;
9690 }
9691
9692 // in the saved state file path, replace the old directory with the new directory
9693 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9694 {
9695 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9696 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9697 }
9698 if (newNVRAMFile.isNotEmpty())
9699 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9700
9701 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9702 if (mData->mFirstSnapshot)
9703 {
9704 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9705 newConfigDir.c_str());
9706 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9707 newConfigDir.c_str());
9708 }
9709 }
9710 while (0);
9711
9712 if (FAILED(rc))
9713 {
9714 /* silently try to rename everything back */
9715 if (fileRenamed)
9716 {
9717 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9718 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9719 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9720 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9721 }
9722 if (dirRenamed)
9723 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9724 }
9725
9726 if (FAILED(rc)) return rc;
9727 }
9728
9729 if (fSettingsFileIsNew)
9730 {
9731 /* create a virgin config file */
9732 int vrc = VINF_SUCCESS;
9733
9734 /* ensure the settings directory exists */
9735 Utf8Str path(mData->m_strConfigFileFull);
9736 path.stripFilename();
9737 if (!RTDirExists(path.c_str()))
9738 {
9739 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9740 if (RT_FAILURE(vrc))
9741 {
9742 return setErrorBoth(E_FAIL, vrc,
9743 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9744 path.c_str(),
9745 vrc);
9746 }
9747 }
9748
9749 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9750 path = Utf8Str(mData->m_strConfigFileFull);
9751 RTFILE f = NIL_RTFILE;
9752 vrc = RTFileOpen(&f, path.c_str(),
9753 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9754 if (RT_FAILURE(vrc))
9755 return setErrorBoth(E_FAIL, vrc,
9756 tr("Could not create the settings file '%s' (%Rrc)"),
9757 path.c_str(),
9758 vrc);
9759 RTFileClose(f);
9760 }
9761
9762 return rc;
9763}
9764
9765/**
9766 * Saves and commits machine data, user data and hardware data.
9767 *
9768 * Note that on failure, the data remains uncommitted.
9769 *
9770 * @a aFlags may combine the following flags:
9771 *
9772 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9773 * Used when saving settings after an operation that makes them 100%
9774 * correspond to the settings from the current snapshot.
9775 * - SaveS_Force: settings will be saved without doing a deep compare of the
9776 * settings structures. This is used when this is called because snapshots
9777 * have changed to avoid the overhead of the deep compare.
9778 *
9779 * @note Must be called from under this object's write lock. Locks children for
9780 * writing.
9781 *
9782 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9783 * initialized to false and that will be set to true by this function if
9784 * the caller must invoke VirtualBox::i_saveSettings() because the global
9785 * settings have changed. This will happen if a machine rename has been
9786 * saved and the global machine and media registries will therefore need
9787 * updating.
9788 * @param aFlags Flags.
9789 */
9790HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9791 int aFlags /*= 0*/)
9792{
9793 LogFlowThisFuncEnter();
9794
9795 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9796
9797 /* make sure child objects are unable to modify the settings while we are
9798 * saving them */
9799 i_ensureNoStateDependencies();
9800
9801 AssertReturn(!i_isSnapshotMachine(),
9802 E_FAIL);
9803
9804 if (!mData->mAccessible)
9805 return setError(VBOX_E_INVALID_VM_STATE,
9806 tr("The machine is not accessible, so cannot save settings"));
9807
9808 HRESULT rc = S_OK;
9809 bool fNeedsWrite = false;
9810
9811 /* First, prepare to save settings. It will care about renaming the
9812 * settings directory and file if the machine name was changed and about
9813 * creating a new settings file if this is a new machine. */
9814 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9815 if (FAILED(rc)) return rc;
9816
9817 // keep a pointer to the current settings structures
9818 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9819 settings::MachineConfigFile *pNewConfig = NULL;
9820
9821 try
9822 {
9823 // make a fresh one to have everyone write stuff into
9824 pNewConfig = new settings::MachineConfigFile(NULL);
9825 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9826
9827 // now go and copy all the settings data from COM to the settings structures
9828 // (this calls i_saveSettings() on all the COM objects in the machine)
9829 i_copyMachineDataToSettings(*pNewConfig);
9830
9831 if (aFlags & SaveS_ResetCurStateModified)
9832 {
9833 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9834 mData->mCurrentStateModified = FALSE;
9835 fNeedsWrite = true; // always, no need to compare
9836 }
9837 else if (aFlags & SaveS_Force)
9838 {
9839 fNeedsWrite = true; // always, no need to compare
9840 }
9841 else
9842 {
9843 if (!mData->mCurrentStateModified)
9844 {
9845 // do a deep compare of the settings that we just saved with the settings
9846 // previously stored in the config file; this invokes MachineConfigFile::operator==
9847 // which does a deep compare of all the settings, which is expensive but less expensive
9848 // than writing out XML in vain
9849 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9850
9851 // could still be modified if any settings changed
9852 mData->mCurrentStateModified = fAnySettingsChanged;
9853
9854 fNeedsWrite = fAnySettingsChanged;
9855 }
9856 else
9857 fNeedsWrite = true;
9858 }
9859
9860 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9861
9862 if (fNeedsWrite)
9863 // now spit it all out!
9864 pNewConfig->write(mData->m_strConfigFileFull);
9865
9866 mData->pMachineConfigFile = pNewConfig;
9867 delete pOldConfig;
9868 i_commit();
9869
9870 // after saving settings, we are no longer different from the XML on disk
9871 mData->flModifications = 0;
9872 }
9873 catch (HRESULT err)
9874 {
9875 // we assume that error info is set by the thrower
9876 rc = err;
9877
9878 // restore old config
9879 delete pNewConfig;
9880 mData->pMachineConfigFile = pOldConfig;
9881 }
9882 catch (...)
9883 {
9884 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9885 }
9886
9887 if (fNeedsWrite)
9888 {
9889 /* Fire the data change event, even on failure (since we've already
9890 * committed all data). This is done only for SessionMachines because
9891 * mutable Machine instances are always not registered (i.e. private
9892 * to the client process that creates them) and thus don't need to
9893 * inform callbacks. */
9894 if (i_isSessionMachine())
9895 mParent->i_onMachineDataChanged(mData->mUuid);
9896 }
9897
9898 LogFlowThisFunc(("rc=%08X\n", rc));
9899 LogFlowThisFuncLeave();
9900 return rc;
9901}
9902
9903/**
9904 * Implementation for saving the machine settings into the given
9905 * settings::MachineConfigFile instance. This copies machine extradata
9906 * from the previous machine config file in the instance data, if any.
9907 *
9908 * This gets called from two locations:
9909 *
9910 * -- Machine::i_saveSettings(), during the regular XML writing;
9911 *
9912 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9913 * exported to OVF and we write the VirtualBox proprietary XML
9914 * into a <vbox:Machine> tag.
9915 *
9916 * This routine fills all the fields in there, including snapshots, *except*
9917 * for the following:
9918 *
9919 * -- fCurrentStateModified. There is some special logic associated with that.
9920 *
9921 * The caller can then call MachineConfigFile::write() or do something else
9922 * with it.
9923 *
9924 * Caller must hold the machine lock!
9925 *
9926 * This throws XML errors and HRESULT, so the caller must have a catch block!
9927 */
9928void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9929{
9930 // deep copy extradata, being extra careful with self assignment (the STL
9931 // map assignment on Mac OS X clang based Xcode isn't checking)
9932 if (&config != mData->pMachineConfigFile)
9933 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9934
9935 config.uuid = mData->mUuid;
9936
9937 // copy name, description, OS type, teleport, UTC etc.
9938 config.machineUserData = mUserData->s;
9939
9940 if ( mData->mMachineState == MachineState_Saved
9941 || mData->mMachineState == MachineState_Restoring
9942 // when doing certain snapshot operations we may or may not have
9943 // a saved state in the current state, so keep everything as is
9944 || ( ( mData->mMachineState == MachineState_Snapshotting
9945 || mData->mMachineState == MachineState_DeletingSnapshot
9946 || mData->mMachineState == MachineState_RestoringSnapshot)
9947 && (!mSSData->strStateFilePath.isEmpty())
9948 )
9949 )
9950 {
9951 Assert(!mSSData->strStateFilePath.isEmpty());
9952 /* try to make the file name relative to the settings file dir */
9953 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9954 }
9955 else
9956 {
9957 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9958 config.strStateFile.setNull();
9959 }
9960
9961 if (mData->mCurrentSnapshot)
9962 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9963 else
9964 config.uuidCurrentSnapshot.clear();
9965
9966 config.timeLastStateChange = mData->mLastStateChange;
9967 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9968 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9969
9970 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9971 if (FAILED(rc)) throw rc;
9972
9973 // save machine's media registry if this is VirtualBox 4.0 or later
9974 if (config.canHaveOwnMediaRegistry())
9975 {
9976 // determine machine folder
9977 Utf8Str strMachineFolder = i_getSettingsFileFull();
9978 strMachineFolder.stripFilename();
9979 mParent->i_saveMediaRegistry(config.mediaRegistry,
9980 i_getId(), // only media with registry ID == machine UUID
9981 strMachineFolder);
9982 // this throws HRESULT
9983 }
9984
9985 // save snapshots
9986 rc = i_saveAllSnapshots(config);
9987 if (FAILED(rc)) throw rc;
9988}
9989
9990/**
9991 * Saves all snapshots of the machine into the given machine config file. Called
9992 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9993 * @param config
9994 * @return
9995 */
9996HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9997{
9998 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9999
10000 HRESULT rc = S_OK;
10001
10002 try
10003 {
10004 config.llFirstSnapshot.clear();
10005
10006 if (mData->mFirstSnapshot)
10007 {
10008 // the settings use a list for "the first snapshot"
10009 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10010
10011 // get reference to the snapshot on the list and work on that
10012 // element straight in the list to avoid excessive copying later
10013 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10014 if (FAILED(rc)) throw rc;
10015 }
10016
10017// if (mType == IsSessionMachine)
10018// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10019
10020 }
10021 catch (HRESULT err)
10022 {
10023 /* we assume that error info is set by the thrower */
10024 rc = err;
10025 }
10026 catch (...)
10027 {
10028 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10029 }
10030
10031 return rc;
10032}
10033
10034/**
10035 * Saves the VM hardware configuration. It is assumed that the
10036 * given node is empty.
10037 *
10038 * @param data Reference to the settings object for the hardware config.
10039 * @param pDbg Pointer to the settings object for the debugging config
10040 * which happens to live in mHWData.
10041 * @param pAutostart Pointer to the settings object for the autostart config
10042 * which happens to live in mHWData.
10043 */
10044HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10045 settings::Autostart *pAutostart)
10046{
10047 HRESULT rc = S_OK;
10048
10049 try
10050 {
10051 /* The hardware version attribute (optional).
10052 Automatically upgrade from 1 to current default hardware version
10053 when there is no saved state. (ugly!) */
10054 if ( mHWData->mHWVersion == "1"
10055 && mSSData->strStateFilePath.isEmpty()
10056 )
10057 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10058
10059 data.strVersion = mHWData->mHWVersion;
10060 data.uuid = mHWData->mHardwareUUID;
10061
10062 // CPU
10063 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10064 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10065 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10066 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10067 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10068 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10069 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10070 data.fVirtVmsaveVmload = !!mHWData->mHWVirtExVirtVmsaveVmload;
10071 data.fPAE = !!mHWData->mPAEEnabled;
10072 data.enmLongMode = mHWData->mLongMode;
10073 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10074 data.fAPIC = !!mHWData->mAPIC;
10075 data.fX2APIC = !!mHWData->mX2APIC;
10076 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10077 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10078 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10079 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10080 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10081 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10082 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10083 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10084 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10085 data.cCPUs = mHWData->mCPUCount;
10086 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10087 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10088 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10089 data.strCpuProfile = mHWData->mCpuProfile;
10090
10091 data.llCpus.clear();
10092 if (data.fCpuHotPlug)
10093 {
10094 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10095 {
10096 if (mHWData->mCPUAttached[idx])
10097 {
10098 settings::Cpu cpu;
10099 cpu.ulId = idx;
10100 data.llCpus.push_back(cpu);
10101 }
10102 }
10103 }
10104
10105 /* Standard and Extended CPUID leafs. */
10106 data.llCpuIdLeafs.clear();
10107 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10108
10109 // memory
10110 data.ulMemorySizeMB = mHWData->mMemorySize;
10111 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10112
10113 // firmware
10114 data.firmwareType = mHWData->mFirmwareType;
10115
10116 // HID
10117 data.pointingHIDType = mHWData->mPointingHIDType;
10118 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10119
10120 // chipset
10121 data.chipsetType = mHWData->mChipsetType;
10122
10123 // iommu
10124 data.iommuType = mHWData->mIommuType;
10125
10126 // paravirt
10127 data.paravirtProvider = mHWData->mParavirtProvider;
10128 data.strParavirtDebug = mHWData->mParavirtDebug;
10129
10130 // emulated USB card reader
10131 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10132
10133 // HPET
10134 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10135
10136 // boot order
10137 data.mapBootOrder.clear();
10138 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10139 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10140
10141 /* VRDEServer settings (optional) */
10142 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10143 if (FAILED(rc)) throw rc;
10144
10145 /* BIOS settings (required) */
10146 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10147 if (FAILED(rc)) throw rc;
10148
10149 /* Recording settings (required) */
10150 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10151 if (FAILED(rc)) throw rc;
10152
10153 /* GraphicsAdapter settings (required) */
10154 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10155 if (FAILED(rc)) throw rc;
10156
10157 /* USB Controller (required) */
10158 data.usbSettings.llUSBControllers.clear();
10159 for (USBControllerList::const_iterator
10160 it = mUSBControllers->begin();
10161 it != mUSBControllers->end();
10162 ++it)
10163 {
10164 ComObjPtr<USBController> ctrl = *it;
10165 settings::USBController settingsCtrl;
10166
10167 settingsCtrl.strName = ctrl->i_getName();
10168 settingsCtrl.enmType = ctrl->i_getControllerType();
10169
10170 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10171 }
10172
10173 /* USB device filters (required) */
10174 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10175 if (FAILED(rc)) throw rc;
10176
10177 /* Network adapters (required) */
10178 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10179 data.llNetworkAdapters.clear();
10180 /* Write out only the nominal number of network adapters for this
10181 * chipset type. Since Machine::commit() hasn't been called there
10182 * may be extra NIC settings in the vector. */
10183 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10184 {
10185 settings::NetworkAdapter nic;
10186 nic.ulSlot = (uint32_t)slot;
10187 /* paranoia check... must not be NULL, but must not crash either. */
10188 if (mNetworkAdapters[slot])
10189 {
10190 if (mNetworkAdapters[slot]->i_hasDefaults())
10191 continue;
10192
10193 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10194 if (FAILED(rc)) throw rc;
10195
10196 data.llNetworkAdapters.push_back(nic);
10197 }
10198 }
10199
10200 /* Serial ports */
10201 data.llSerialPorts.clear();
10202 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10203 {
10204 if (mSerialPorts[slot]->i_hasDefaults())
10205 continue;
10206
10207 settings::SerialPort s;
10208 s.ulSlot = slot;
10209 rc = mSerialPorts[slot]->i_saveSettings(s);
10210 if (FAILED(rc)) return rc;
10211
10212 data.llSerialPorts.push_back(s);
10213 }
10214
10215 /* Parallel ports */
10216 data.llParallelPorts.clear();
10217 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10218 {
10219 if (mParallelPorts[slot]->i_hasDefaults())
10220 continue;
10221
10222 settings::ParallelPort p;
10223 p.ulSlot = slot;
10224 rc = mParallelPorts[slot]->i_saveSettings(p);
10225 if (FAILED(rc)) return rc;
10226
10227 data.llParallelPorts.push_back(p);
10228 }
10229
10230 /* Audio adapter */
10231 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10232 if (FAILED(rc)) return rc;
10233
10234 rc = i_saveStorageControllers(data.storage);
10235 if (FAILED(rc)) return rc;
10236
10237 /* Shared folders */
10238 data.llSharedFolders.clear();
10239 for (HWData::SharedFolderList::const_iterator
10240 it = mHWData->mSharedFolders.begin();
10241 it != mHWData->mSharedFolders.end();
10242 ++it)
10243 {
10244 SharedFolder *pSF = *it;
10245 AutoCaller sfCaller(pSF);
10246 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10247 settings::SharedFolder sf;
10248 sf.strName = pSF->i_getName();
10249 sf.strHostPath = pSF->i_getHostPath();
10250 sf.fWritable = !!pSF->i_isWritable();
10251 sf.fAutoMount = !!pSF->i_isAutoMounted();
10252 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10253
10254 data.llSharedFolders.push_back(sf);
10255 }
10256
10257 // clipboard
10258 data.clipboardMode = mHWData->mClipboardMode;
10259 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10260
10261 // drag'n'drop
10262 data.dndMode = mHWData->mDnDMode;
10263
10264 /* Guest */
10265 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10266
10267 // IO settings
10268 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10269 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10270
10271 /* BandwidthControl (required) */
10272 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10273 if (FAILED(rc)) throw rc;
10274
10275 /* Host PCI devices */
10276 data.pciAttachments.clear();
10277 for (HWData::PCIDeviceAssignmentList::const_iterator
10278 it = mHWData->mPCIDeviceAssignments.begin();
10279 it != mHWData->mPCIDeviceAssignments.end();
10280 ++it)
10281 {
10282 ComObjPtr<PCIDeviceAttachment> pda = *it;
10283 settings::HostPCIDeviceAttachment hpda;
10284
10285 rc = pda->i_saveSettings(hpda);
10286 if (FAILED(rc)) throw rc;
10287
10288 data.pciAttachments.push_back(hpda);
10289 }
10290
10291 // guest properties
10292 data.llGuestProperties.clear();
10293#ifdef VBOX_WITH_GUEST_PROPS
10294 for (HWData::GuestPropertyMap::const_iterator
10295 it = mHWData->mGuestProperties.begin();
10296 it != mHWData->mGuestProperties.end();
10297 ++it)
10298 {
10299 HWData::GuestProperty property = it->second;
10300
10301 /* Remove transient guest properties at shutdown unless we
10302 * are saving state. Note that restoring snapshot intentionally
10303 * keeps them, they will be removed if appropriate once the final
10304 * machine state is set (as crashes etc. need to work). */
10305 if ( ( mData->mMachineState == MachineState_PoweredOff
10306 || mData->mMachineState == MachineState_Aborted
10307 || mData->mMachineState == MachineState_Teleported)
10308 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10309 continue;
10310 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10311 prop.strName = it->first;
10312 prop.strValue = property.strValue;
10313 prop.timestamp = (uint64_t)property.mTimestamp;
10314 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10315 GuestPropWriteFlags(property.mFlags, szFlags);
10316 prop.strFlags = szFlags;
10317
10318 data.llGuestProperties.push_back(prop);
10319 }
10320
10321 /* I presume this doesn't require a backup(). */
10322 mData->mGuestPropertiesModified = FALSE;
10323#endif /* VBOX_WITH_GUEST_PROPS defined */
10324
10325 *pDbg = mHWData->mDebugging;
10326 *pAutostart = mHWData->mAutostart;
10327
10328 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10329 }
10330 catch (std::bad_alloc &)
10331 {
10332 return E_OUTOFMEMORY;
10333 }
10334
10335 AssertComRC(rc);
10336 return rc;
10337}
10338
10339/**
10340 * Saves the storage controller configuration.
10341 *
10342 * @param data storage settings.
10343 */
10344HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10345{
10346 data.llStorageControllers.clear();
10347
10348 for (StorageControllerList::const_iterator
10349 it = mStorageControllers->begin();
10350 it != mStorageControllers->end();
10351 ++it)
10352 {
10353 HRESULT rc;
10354 ComObjPtr<StorageController> pCtl = *it;
10355
10356 settings::StorageController ctl;
10357 ctl.strName = pCtl->i_getName();
10358 ctl.controllerType = pCtl->i_getControllerType();
10359 ctl.storageBus = pCtl->i_getStorageBus();
10360 ctl.ulInstance = pCtl->i_getInstance();
10361 ctl.fBootable = pCtl->i_getBootable();
10362
10363 /* Save the port count. */
10364 ULONG portCount;
10365 rc = pCtl->COMGETTER(PortCount)(&portCount);
10366 ComAssertComRCRet(rc, rc);
10367 ctl.ulPortCount = portCount;
10368
10369 /* Save fUseHostIOCache */
10370 BOOL fUseHostIOCache;
10371 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10372 ComAssertComRCRet(rc, rc);
10373 ctl.fUseHostIOCache = !!fUseHostIOCache;
10374
10375 /* save the devices now. */
10376 rc = i_saveStorageDevices(pCtl, ctl);
10377 ComAssertComRCRet(rc, rc);
10378
10379 data.llStorageControllers.push_back(ctl);
10380 }
10381
10382 return S_OK;
10383}
10384
10385/**
10386 * Saves the hard disk configuration.
10387 */
10388HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10389 settings::StorageController &data)
10390{
10391 MediumAttachmentList atts;
10392
10393 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10394 if (FAILED(rc)) return rc;
10395
10396 data.llAttachedDevices.clear();
10397 for (MediumAttachmentList::const_iterator
10398 it = atts.begin();
10399 it != atts.end();
10400 ++it)
10401 {
10402 settings::AttachedDevice dev;
10403 IMediumAttachment *iA = *it;
10404 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10405 Medium *pMedium = pAttach->i_getMedium();
10406
10407 dev.deviceType = pAttach->i_getType();
10408 dev.lPort = pAttach->i_getPort();
10409 dev.lDevice = pAttach->i_getDevice();
10410 dev.fPassThrough = pAttach->i_getPassthrough();
10411 dev.fHotPluggable = pAttach->i_getHotPluggable();
10412 if (pMedium)
10413 {
10414 if (pMedium->i_isHostDrive())
10415 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10416 else
10417 dev.uuid = pMedium->i_getId();
10418 dev.fTempEject = pAttach->i_getTempEject();
10419 dev.fNonRotational = pAttach->i_getNonRotational();
10420 dev.fDiscard = pAttach->i_getDiscard();
10421 }
10422
10423 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10424
10425 data.llAttachedDevices.push_back(dev);
10426 }
10427
10428 return S_OK;
10429}
10430
10431/**
10432 * Saves machine state settings as defined by aFlags
10433 * (SaveSTS_* values).
10434 *
10435 * @param aFlags Combination of SaveSTS_* flags.
10436 *
10437 * @note Locks objects for writing.
10438 */
10439HRESULT Machine::i_saveStateSettings(int aFlags)
10440{
10441 if (aFlags == 0)
10442 return S_OK;
10443
10444 AutoCaller autoCaller(this);
10445 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10446
10447 /* This object's write lock is also necessary to serialize file access
10448 * (prevent concurrent reads and writes) */
10449 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10450
10451 HRESULT rc = S_OK;
10452
10453 Assert(mData->pMachineConfigFile);
10454
10455 try
10456 {
10457 if (aFlags & SaveSTS_CurStateModified)
10458 mData->pMachineConfigFile->fCurrentStateModified = true;
10459
10460 if (aFlags & SaveSTS_StateFilePath)
10461 {
10462 if (!mSSData->strStateFilePath.isEmpty())
10463 /* try to make the file name relative to the settings file dir */
10464 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10465 else
10466 mData->pMachineConfigFile->strStateFile.setNull();
10467 }
10468
10469 if (aFlags & SaveSTS_StateTimeStamp)
10470 {
10471 Assert( mData->mMachineState != MachineState_Aborted
10472 || mSSData->strStateFilePath.isEmpty());
10473
10474 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10475
10476 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10477/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10478 }
10479
10480 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10481 }
10482 catch (...)
10483 {
10484 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10485 }
10486
10487 return rc;
10488}
10489
10490/**
10491 * Ensures that the given medium is added to a media registry. If this machine
10492 * was created with 4.0 or later, then the machine registry is used. Otherwise
10493 * the global VirtualBox media registry is used.
10494 *
10495 * Caller must NOT hold machine lock, media tree or any medium locks!
10496 *
10497 * @param pMedium
10498 */
10499void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10500{
10501 /* Paranoia checks: do not hold machine or media tree locks. */
10502 AssertReturnVoid(!isWriteLockOnCurrentThread());
10503 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10504
10505 ComObjPtr<Medium> pBase;
10506 {
10507 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10508 pBase = pMedium->i_getBase();
10509 }
10510
10511 /* Paranoia checks: do not hold medium locks. */
10512 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10513 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10514
10515 // decide which medium registry to use now that the medium is attached:
10516 Guid uuid;
10517 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10518 if (fCanHaveOwnMediaRegistry)
10519 // machine XML is VirtualBox 4.0 or higher:
10520 uuid = i_getId(); // machine UUID
10521 else
10522 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10523
10524 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10525 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10526 if (pMedium->i_addRegistry(uuid))
10527 mParent->i_markRegistryModified(uuid);
10528
10529 /* For more complex hard disk structures it can happen that the base
10530 * medium isn't yet associated with any medium registry. Do that now. */
10531 if (pMedium != pBase)
10532 {
10533 /* Tree lock needed by Medium::addRegistry when recursing. */
10534 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10535 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10536 {
10537 treeLock.release();
10538 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10539 treeLock.acquire();
10540 }
10541 if (pBase->i_addRegistryRecursive(uuid))
10542 {
10543 treeLock.release();
10544 mParent->i_markRegistryModified(uuid);
10545 }
10546 }
10547}
10548
10549/**
10550 * Creates differencing hard disks for all normal hard disks attached to this
10551 * machine and a new set of attachments to refer to created disks.
10552 *
10553 * Used when taking a snapshot or when deleting the current state. Gets called
10554 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10555 *
10556 * This method assumes that mMediumAttachments contains the original hard disk
10557 * attachments it needs to create diffs for. On success, these attachments will
10558 * be replaced with the created diffs.
10559 *
10560 * Attachments with non-normal hard disks are left as is.
10561 *
10562 * If @a aOnline is @c false then the original hard disks that require implicit
10563 * diffs will be locked for reading. Otherwise it is assumed that they are
10564 * already locked for writing (when the VM was started). Note that in the latter
10565 * case it is responsibility of the caller to lock the newly created diffs for
10566 * writing if this method succeeds.
10567 *
10568 * @param aProgress Progress object to run (must contain at least as
10569 * many operations left as the number of hard disks
10570 * attached).
10571 * @param aWeight Weight of this operation.
10572 * @param aOnline Whether the VM was online prior to this operation.
10573 *
10574 * @note The progress object is not marked as completed, neither on success nor
10575 * on failure. This is a responsibility of the caller.
10576 *
10577 * @note Locks this object and the media tree for writing.
10578 */
10579HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10580 ULONG aWeight,
10581 bool aOnline)
10582{
10583 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10584
10585 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10586 AssertReturn(!!pProgressControl, E_INVALIDARG);
10587
10588 AutoCaller autoCaller(this);
10589 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10590
10591 AutoMultiWriteLock2 alock(this->lockHandle(),
10592 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10593
10594 /* must be in a protective state because we release the lock below */
10595 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10596 || mData->mMachineState == MachineState_OnlineSnapshotting
10597 || mData->mMachineState == MachineState_LiveSnapshotting
10598 || mData->mMachineState == MachineState_RestoringSnapshot
10599 || mData->mMachineState == MachineState_DeletingSnapshot
10600 , E_FAIL);
10601
10602 HRESULT rc = S_OK;
10603
10604 // use appropriate locked media map (online or offline)
10605 MediumLockListMap lockedMediaOffline;
10606 MediumLockListMap *lockedMediaMap;
10607 if (aOnline)
10608 lockedMediaMap = &mData->mSession.mLockedMedia;
10609 else
10610 lockedMediaMap = &lockedMediaOffline;
10611
10612 try
10613 {
10614 if (!aOnline)
10615 {
10616 /* lock all attached hard disks early to detect "in use"
10617 * situations before creating actual diffs */
10618 for (MediumAttachmentList::const_iterator
10619 it = mMediumAttachments->begin();
10620 it != mMediumAttachments->end();
10621 ++it)
10622 {
10623 MediumAttachment *pAtt = *it;
10624 if (pAtt->i_getType() == DeviceType_HardDisk)
10625 {
10626 Medium *pMedium = pAtt->i_getMedium();
10627 Assert(pMedium);
10628
10629 MediumLockList *pMediumLockList(new MediumLockList());
10630 alock.release();
10631 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10632 NULL /* pToLockWrite */,
10633 false /* fMediumLockWriteAll */,
10634 NULL,
10635 *pMediumLockList);
10636 alock.acquire();
10637 if (FAILED(rc))
10638 {
10639 delete pMediumLockList;
10640 throw rc;
10641 }
10642 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10643 if (FAILED(rc))
10644 {
10645 throw setError(rc,
10646 tr("Collecting locking information for all attached media failed"));
10647 }
10648 }
10649 }
10650
10651 /* Now lock all media. If this fails, nothing is locked. */
10652 alock.release();
10653 rc = lockedMediaMap->Lock();
10654 alock.acquire();
10655 if (FAILED(rc))
10656 {
10657 throw setError(rc,
10658 tr("Locking of attached media failed"));
10659 }
10660 }
10661
10662 /* remember the current list (note that we don't use backup() since
10663 * mMediumAttachments may be already backed up) */
10664 MediumAttachmentList atts = *mMediumAttachments.data();
10665
10666 /* start from scratch */
10667 mMediumAttachments->clear();
10668
10669 /* go through remembered attachments and create diffs for normal hard
10670 * disks and attach them */
10671 for (MediumAttachmentList::const_iterator
10672 it = atts.begin();
10673 it != atts.end();
10674 ++it)
10675 {
10676 MediumAttachment *pAtt = *it;
10677
10678 DeviceType_T devType = pAtt->i_getType();
10679 Medium *pMedium = pAtt->i_getMedium();
10680
10681 if ( devType != DeviceType_HardDisk
10682 || pMedium == NULL
10683 || pMedium->i_getType() != MediumType_Normal)
10684 {
10685 /* copy the attachment as is */
10686
10687 /** @todo the progress object created in SessionMachine::TakeSnaphot
10688 * only expects operations for hard disks. Later other
10689 * device types need to show up in the progress as well. */
10690 if (devType == DeviceType_HardDisk)
10691 {
10692 if (pMedium == NULL)
10693 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10694 aWeight); // weight
10695 else
10696 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10697 pMedium->i_getBase()->i_getName().c_str()).raw(),
10698 aWeight); // weight
10699 }
10700
10701 mMediumAttachments->push_back(pAtt);
10702 continue;
10703 }
10704
10705 /* need a diff */
10706 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10707 pMedium->i_getBase()->i_getName().c_str()).raw(),
10708 aWeight); // weight
10709
10710 Utf8Str strFullSnapshotFolder;
10711 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10712
10713 ComObjPtr<Medium> diff;
10714 diff.createObject();
10715 // store the diff in the same registry as the parent
10716 // (this cannot fail here because we can't create implicit diffs for
10717 // unregistered images)
10718 Guid uuidRegistryParent;
10719 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10720 Assert(fInRegistry); NOREF(fInRegistry);
10721 rc = diff->init(mParent,
10722 pMedium->i_getPreferredDiffFormat(),
10723 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10724 uuidRegistryParent,
10725 DeviceType_HardDisk);
10726 if (FAILED(rc)) throw rc;
10727
10728 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10729 * the push_back? Looks like we're going to release medium with the
10730 * wrong kind of lock (general issue with if we fail anywhere at all)
10731 * and an orphaned VDI in the snapshots folder. */
10732
10733 /* update the appropriate lock list */
10734 MediumLockList *pMediumLockList;
10735 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10736 AssertComRCThrowRC(rc);
10737 if (aOnline)
10738 {
10739 alock.release();
10740 /* The currently attached medium will be read-only, change
10741 * the lock type to read. */
10742 rc = pMediumLockList->Update(pMedium, false);
10743 alock.acquire();
10744 AssertComRCThrowRC(rc);
10745 }
10746
10747 /* release the locks before the potentially lengthy operation */
10748 alock.release();
10749 rc = pMedium->i_createDiffStorage(diff,
10750 pMedium->i_getPreferredDiffVariant(),
10751 pMediumLockList,
10752 NULL /* aProgress */,
10753 true /* aWait */,
10754 false /* aNotify */);
10755 alock.acquire();
10756 if (FAILED(rc)) throw rc;
10757
10758 /* actual lock list update is done in Machine::i_commitMedia */
10759
10760 rc = diff->i_addBackReference(mData->mUuid);
10761 AssertComRCThrowRC(rc);
10762
10763 /* add a new attachment */
10764 ComObjPtr<MediumAttachment> attachment;
10765 attachment.createObject();
10766 rc = attachment->init(this,
10767 diff,
10768 pAtt->i_getControllerName(),
10769 pAtt->i_getPort(),
10770 pAtt->i_getDevice(),
10771 DeviceType_HardDisk,
10772 true /* aImplicit */,
10773 false /* aPassthrough */,
10774 false /* aTempEject */,
10775 pAtt->i_getNonRotational(),
10776 pAtt->i_getDiscard(),
10777 pAtt->i_getHotPluggable(),
10778 pAtt->i_getBandwidthGroup());
10779 if (FAILED(rc)) throw rc;
10780
10781 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10782 AssertComRCThrowRC(rc);
10783 mMediumAttachments->push_back(attachment);
10784 }
10785 }
10786 catch (HRESULT aRC) { rc = aRC; }
10787
10788 /* unlock all hard disks we locked when there is no VM */
10789 if (!aOnline)
10790 {
10791 ErrorInfoKeeper eik;
10792
10793 HRESULT rc1 = lockedMediaMap->Clear();
10794 AssertComRC(rc1);
10795 }
10796
10797 return rc;
10798}
10799
10800/**
10801 * Deletes implicit differencing hard disks created either by
10802 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10803 * mMediumAttachments.
10804 *
10805 * Note that to delete hard disks created by #attachDevice() this method is
10806 * called from #i_rollbackMedia() when the changes are rolled back.
10807 *
10808 * @note Locks this object and the media tree for writing.
10809 */
10810HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10811{
10812 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10813
10814 AutoCaller autoCaller(this);
10815 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10816
10817 AutoMultiWriteLock2 alock(this->lockHandle(),
10818 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10819
10820 /* We absolutely must have backed up state. */
10821 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10822
10823 /* Check if there are any implicitly created diff images. */
10824 bool fImplicitDiffs = false;
10825 for (MediumAttachmentList::const_iterator
10826 it = mMediumAttachments->begin();
10827 it != mMediumAttachments->end();
10828 ++it)
10829 {
10830 const ComObjPtr<MediumAttachment> &pAtt = *it;
10831 if (pAtt->i_isImplicit())
10832 {
10833 fImplicitDiffs = true;
10834 break;
10835 }
10836 }
10837 /* If there is nothing to do, leave early. This saves lots of image locking
10838 * effort. It also avoids a MachineStateChanged event without real reason.
10839 * This is important e.g. when loading a VM config, because there should be
10840 * no events. Otherwise API clients can become thoroughly confused for
10841 * inaccessible VMs (the code for loading VM configs uses this method for
10842 * cleanup if the config makes no sense), as they take such events as an
10843 * indication that the VM is alive, and they would force the VM config to
10844 * be reread, leading to an endless loop. */
10845 if (!fImplicitDiffs)
10846 return S_OK;
10847
10848 HRESULT rc = S_OK;
10849 MachineState_T oldState = mData->mMachineState;
10850
10851 /* will release the lock before the potentially lengthy operation,
10852 * so protect with the special state (unless already protected) */
10853 if ( oldState != MachineState_Snapshotting
10854 && oldState != MachineState_OnlineSnapshotting
10855 && oldState != MachineState_LiveSnapshotting
10856 && oldState != MachineState_RestoringSnapshot
10857 && oldState != MachineState_DeletingSnapshot
10858 && oldState != MachineState_DeletingSnapshotOnline
10859 && oldState != MachineState_DeletingSnapshotPaused
10860 )
10861 i_setMachineState(MachineState_SettingUp);
10862
10863 // use appropriate locked media map (online or offline)
10864 MediumLockListMap lockedMediaOffline;
10865 MediumLockListMap *lockedMediaMap;
10866 if (aOnline)
10867 lockedMediaMap = &mData->mSession.mLockedMedia;
10868 else
10869 lockedMediaMap = &lockedMediaOffline;
10870
10871 try
10872 {
10873 if (!aOnline)
10874 {
10875 /* lock all attached hard disks early to detect "in use"
10876 * situations before deleting actual diffs */
10877 for (MediumAttachmentList::const_iterator
10878 it = mMediumAttachments->begin();
10879 it != mMediumAttachments->end();
10880 ++it)
10881 {
10882 MediumAttachment *pAtt = *it;
10883 if (pAtt->i_getType() == DeviceType_HardDisk)
10884 {
10885 Medium *pMedium = pAtt->i_getMedium();
10886 Assert(pMedium);
10887
10888 MediumLockList *pMediumLockList(new MediumLockList());
10889 alock.release();
10890 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10891 NULL /* pToLockWrite */,
10892 false /* fMediumLockWriteAll */,
10893 NULL,
10894 *pMediumLockList);
10895 alock.acquire();
10896
10897 if (FAILED(rc))
10898 {
10899 delete pMediumLockList;
10900 throw rc;
10901 }
10902
10903 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10904 if (FAILED(rc))
10905 throw rc;
10906 }
10907 }
10908
10909 if (FAILED(rc))
10910 throw rc;
10911 } // end of offline
10912
10913 /* Lock lists are now up to date and include implicitly created media */
10914
10915 /* Go through remembered attachments and delete all implicitly created
10916 * diffs and fix up the attachment information */
10917 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10918 MediumAttachmentList implicitAtts;
10919 for (MediumAttachmentList::const_iterator
10920 it = mMediumAttachments->begin();
10921 it != mMediumAttachments->end();
10922 ++it)
10923 {
10924 ComObjPtr<MediumAttachment> pAtt = *it;
10925 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10926 if (pMedium.isNull())
10927 continue;
10928
10929 // Implicit attachments go on the list for deletion and back references are removed.
10930 if (pAtt->i_isImplicit())
10931 {
10932 /* Deassociate and mark for deletion */
10933 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10934 rc = pMedium->i_removeBackReference(mData->mUuid);
10935 if (FAILED(rc))
10936 throw rc;
10937 implicitAtts.push_back(pAtt);
10938 continue;
10939 }
10940
10941 /* Was this medium attached before? */
10942 if (!i_findAttachment(oldAtts, pMedium))
10943 {
10944 /* no: de-associate */
10945 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10946 rc = pMedium->i_removeBackReference(mData->mUuid);
10947 if (FAILED(rc))
10948 throw rc;
10949 continue;
10950 }
10951 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10952 }
10953
10954 /* If there are implicit attachments to delete, throw away the lock
10955 * map contents (which will unlock all media) since the medium
10956 * attachments will be rolled back. Below we need to completely
10957 * recreate the lock map anyway since it is infinitely complex to
10958 * do this incrementally (would need reconstructing each attachment
10959 * change, which would be extremely hairy). */
10960 if (implicitAtts.size() != 0)
10961 {
10962 ErrorInfoKeeper eik;
10963
10964 HRESULT rc1 = lockedMediaMap->Clear();
10965 AssertComRC(rc1);
10966 }
10967
10968 /* rollback hard disk changes */
10969 mMediumAttachments.rollback();
10970
10971 MultiResult mrc(S_OK);
10972
10973 // Delete unused implicit diffs.
10974 if (implicitAtts.size() != 0)
10975 {
10976 alock.release();
10977
10978 for (MediumAttachmentList::const_iterator
10979 it = implicitAtts.begin();
10980 it != implicitAtts.end();
10981 ++it)
10982 {
10983 // Remove medium associated with this attachment.
10984 ComObjPtr<MediumAttachment> pAtt = *it;
10985 Assert(pAtt);
10986 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10987 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10988 Assert(pMedium);
10989
10990 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10991 // continue on delete failure, just collect error messages
10992 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10993 pMedium->i_getLocationFull().c_str() ));
10994 mrc = rc;
10995 }
10996 // Clear the list of deleted implicit attachments now, while not
10997 // holding the lock, as it will ultimately trigger Medium::uninit()
10998 // calls which assume that the media tree lock isn't held.
10999 implicitAtts.clear();
11000
11001 alock.acquire();
11002
11003 /* if there is a VM recreate media lock map as mentioned above,
11004 * otherwise it is a waste of time and we leave things unlocked */
11005 if (aOnline)
11006 {
11007 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11008 /* must never be NULL, but better safe than sorry */
11009 if (!pMachine.isNull())
11010 {
11011 alock.release();
11012 rc = mData->mSession.mMachine->i_lockMedia();
11013 alock.acquire();
11014 if (FAILED(rc))
11015 throw rc;
11016 }
11017 }
11018 }
11019 }
11020 catch (HRESULT aRC) {rc = aRC;}
11021
11022 if (mData->mMachineState == MachineState_SettingUp)
11023 i_setMachineState(oldState);
11024
11025 /* unlock all hard disks we locked when there is no VM */
11026 if (!aOnline)
11027 {
11028 ErrorInfoKeeper eik;
11029
11030 HRESULT rc1 = lockedMediaMap->Clear();
11031 AssertComRC(rc1);
11032 }
11033
11034 return rc;
11035}
11036
11037
11038/**
11039 * Looks through the given list of media attachments for one with the given parameters
11040 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11041 * can be searched as well if needed.
11042 *
11043 * @param ll
11044 * @param aControllerName
11045 * @param aControllerPort
11046 * @param aDevice
11047 * @return
11048 */
11049MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11050 const Utf8Str &aControllerName,
11051 LONG aControllerPort,
11052 LONG aDevice)
11053{
11054 for (MediumAttachmentList::const_iterator
11055 it = ll.begin();
11056 it != ll.end();
11057 ++it)
11058 {
11059 MediumAttachment *pAttach = *it;
11060 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11061 return pAttach;
11062 }
11063
11064 return NULL;
11065}
11066
11067/**
11068 * Looks through the given list of media attachments for one with the given parameters
11069 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11070 * can be searched as well if needed.
11071 *
11072 * @param ll
11073 * @param pMedium
11074 * @return
11075 */
11076MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11077 ComObjPtr<Medium> pMedium)
11078{
11079 for (MediumAttachmentList::const_iterator
11080 it = ll.begin();
11081 it != ll.end();
11082 ++it)
11083 {
11084 MediumAttachment *pAttach = *it;
11085 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11086 if (pMediumThis == pMedium)
11087 return pAttach;
11088 }
11089
11090 return NULL;
11091}
11092
11093/**
11094 * Looks through the given list of media attachments for one with the given parameters
11095 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11096 * can be searched as well if needed.
11097 *
11098 * @param ll
11099 * @param id
11100 * @return
11101 */
11102MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11103 Guid &id)
11104{
11105 for (MediumAttachmentList::const_iterator
11106 it = ll.begin();
11107 it != ll.end();
11108 ++it)
11109 {
11110 MediumAttachment *pAttach = *it;
11111 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11112 if (pMediumThis->i_getId() == id)
11113 return pAttach;
11114 }
11115
11116 return NULL;
11117}
11118
11119/**
11120 * Main implementation for Machine::DetachDevice. This also gets called
11121 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11122 *
11123 * @param pAttach Medium attachment to detach.
11124 * @param writeLock Machine write lock which the caller must have locked once.
11125 * This may be released temporarily in here.
11126 * @param pSnapshot If NULL, then the detachment is for the current machine.
11127 * Otherwise this is for a SnapshotMachine, and this must be
11128 * its snapshot.
11129 * @return
11130 */
11131HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11132 AutoWriteLock &writeLock,
11133 Snapshot *pSnapshot)
11134{
11135 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11136 DeviceType_T mediumType = pAttach->i_getType();
11137
11138 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11139
11140 if (pAttach->i_isImplicit())
11141 {
11142 /* attempt to implicitly delete the implicitly created diff */
11143
11144 /// @todo move the implicit flag from MediumAttachment to Medium
11145 /// and forbid any hard disk operation when it is implicit. Or maybe
11146 /// a special media state for it to make it even more simple.
11147
11148 Assert(mMediumAttachments.isBackedUp());
11149
11150 /* will release the lock before the potentially lengthy operation, so
11151 * protect with the special state */
11152 MachineState_T oldState = mData->mMachineState;
11153 i_setMachineState(MachineState_SettingUp);
11154
11155 writeLock.release();
11156
11157 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11158 true /*aWait*/,
11159 false /*aNotify*/);
11160
11161 writeLock.acquire();
11162
11163 i_setMachineState(oldState);
11164
11165 if (FAILED(rc)) return rc;
11166 }
11167
11168 i_setModified(IsModified_Storage);
11169 mMediumAttachments.backup();
11170 mMediumAttachments->remove(pAttach);
11171
11172 if (!oldmedium.isNull())
11173 {
11174 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11175 if (pSnapshot)
11176 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11177 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11178 else if (mediumType != DeviceType_HardDisk)
11179 oldmedium->i_removeBackReference(mData->mUuid);
11180 }
11181
11182 return S_OK;
11183}
11184
11185/**
11186 * Goes thru all media of the given list and
11187 *
11188 * 1) calls i_detachDevice() on each of them for this machine and
11189 * 2) adds all Medium objects found in the process to the given list,
11190 * depending on cleanupMode.
11191 *
11192 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11193 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11194 * media to the list.
11195 *
11196 * This gets called from Machine::Unregister, both for the actual Machine and
11197 * the SnapshotMachine objects that might be found in the snapshots.
11198 *
11199 * Requires caller and locking. The machine lock must be passed in because it
11200 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11201 *
11202 * @param writeLock Machine lock from top-level caller; this gets passed to
11203 * i_detachDevice.
11204 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11205 * object if called for a SnapshotMachine.
11206 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11207 * added to llMedia; if Full, then all media get added;
11208 * otherwise no media get added.
11209 * @param llMedia Caller's list to receive Medium objects which got detached so
11210 * caller can close() them, depending on cleanupMode.
11211 * @return
11212 */
11213HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11214 Snapshot *pSnapshot,
11215 CleanupMode_T cleanupMode,
11216 MediaList &llMedia)
11217{
11218 Assert(isWriteLockOnCurrentThread());
11219
11220 HRESULT rc;
11221
11222 // make a temporary list because i_detachDevice invalidates iterators into
11223 // mMediumAttachments
11224 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11225
11226 for (MediumAttachmentList::iterator
11227 it = llAttachments2.begin();
11228 it != llAttachments2.end();
11229 ++it)
11230 {
11231 ComObjPtr<MediumAttachment> &pAttach = *it;
11232 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11233
11234 if (!pMedium.isNull())
11235 {
11236 AutoCaller mac(pMedium);
11237 if (FAILED(mac.rc())) return mac.rc();
11238 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11239 DeviceType_T devType = pMedium->i_getDeviceType();
11240 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11241 && devType == DeviceType_HardDisk)
11242 || (cleanupMode == CleanupMode_Full)
11243 )
11244 {
11245 llMedia.push_back(pMedium);
11246 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11247 /* Not allowed to keep this lock as below we need the parent
11248 * medium lock, and the lock order is parent to child. */
11249 lock.release();
11250 /*
11251 * Search for medias which are not attached to any machine, but
11252 * in the chain to an attached disk. Mediums are only consided
11253 * if they are:
11254 * - have only one child
11255 * - no references to any machines
11256 * - are of normal medium type
11257 */
11258 while (!pParent.isNull())
11259 {
11260 AutoCaller mac1(pParent);
11261 if (FAILED(mac1.rc())) return mac1.rc();
11262 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11263 if (pParent->i_getChildren().size() == 1)
11264 {
11265 if ( pParent->i_getMachineBackRefCount() == 0
11266 && pParent->i_getType() == MediumType_Normal
11267 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11268 llMedia.push_back(pParent);
11269 }
11270 else
11271 break;
11272 pParent = pParent->i_getParent();
11273 }
11274 }
11275 }
11276
11277 // real machine: then we need to use the proper method
11278 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11279
11280 if (FAILED(rc))
11281 return rc;
11282 }
11283
11284 return S_OK;
11285}
11286
11287/**
11288 * Perform deferred hard disk detachments.
11289 *
11290 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11291 * changed (not backed up).
11292 *
11293 * If @a aOnline is @c true then this method will also unlock the old hard
11294 * disks for which the new implicit diffs were created and will lock these new
11295 * diffs for writing.
11296 *
11297 * @param aOnline Whether the VM was online prior to this operation.
11298 *
11299 * @note Locks this object for writing!
11300 */
11301void Machine::i_commitMedia(bool aOnline /*= false*/)
11302{
11303 AutoCaller autoCaller(this);
11304 AssertComRCReturnVoid(autoCaller.rc());
11305
11306 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11307
11308 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11309
11310 HRESULT rc = S_OK;
11311
11312 /* no attach/detach operations -- nothing to do */
11313 if (!mMediumAttachments.isBackedUp())
11314 return;
11315
11316 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11317 bool fMediaNeedsLocking = false;
11318
11319 /* enumerate new attachments */
11320 for (MediumAttachmentList::const_iterator
11321 it = mMediumAttachments->begin();
11322 it != mMediumAttachments->end();
11323 ++it)
11324 {
11325 MediumAttachment *pAttach = *it;
11326
11327 pAttach->i_commit();
11328
11329 Medium *pMedium = pAttach->i_getMedium();
11330 bool fImplicit = pAttach->i_isImplicit();
11331
11332 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11333 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11334 fImplicit));
11335
11336 /** @todo convert all this Machine-based voodoo to MediumAttachment
11337 * based commit logic. */
11338 if (fImplicit)
11339 {
11340 /* convert implicit attachment to normal */
11341 pAttach->i_setImplicit(false);
11342
11343 if ( aOnline
11344 && pMedium
11345 && pAttach->i_getType() == DeviceType_HardDisk
11346 )
11347 {
11348 /* update the appropriate lock list */
11349 MediumLockList *pMediumLockList;
11350 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11351 AssertComRC(rc);
11352 if (pMediumLockList)
11353 {
11354 /* unlock if there's a need to change the locking */
11355 if (!fMediaNeedsLocking)
11356 {
11357 rc = mData->mSession.mLockedMedia.Unlock();
11358 AssertComRC(rc);
11359 fMediaNeedsLocking = true;
11360 }
11361 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11362 AssertComRC(rc);
11363 rc = pMediumLockList->Append(pMedium, true);
11364 AssertComRC(rc);
11365 }
11366 }
11367
11368 continue;
11369 }
11370
11371 if (pMedium)
11372 {
11373 /* was this medium attached before? */
11374 for (MediumAttachmentList::iterator
11375 oldIt = oldAtts.begin();
11376 oldIt != oldAtts.end();
11377 ++oldIt)
11378 {
11379 MediumAttachment *pOldAttach = *oldIt;
11380 if (pOldAttach->i_getMedium() == pMedium)
11381 {
11382 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11383
11384 /* yes: remove from old to avoid de-association */
11385 oldAtts.erase(oldIt);
11386 break;
11387 }
11388 }
11389 }
11390 }
11391
11392 /* enumerate remaining old attachments and de-associate from the
11393 * current machine state */
11394 for (MediumAttachmentList::const_iterator
11395 it = oldAtts.begin();
11396 it != oldAtts.end();
11397 ++it)
11398 {
11399 MediumAttachment *pAttach = *it;
11400 Medium *pMedium = pAttach->i_getMedium();
11401
11402 /* Detach only hard disks, since DVD/floppy media is detached
11403 * instantly in MountMedium. */
11404 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11405 {
11406 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11407
11408 /* now de-associate from the current machine state */
11409 rc = pMedium->i_removeBackReference(mData->mUuid);
11410 AssertComRC(rc);
11411
11412 if (aOnline)
11413 {
11414 /* unlock since medium is not used anymore */
11415 MediumLockList *pMediumLockList;
11416 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11417 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11418 {
11419 /* this happens for online snapshots, there the attachment
11420 * is changing, but only to a diff image created under
11421 * the old one, so there is no separate lock list */
11422 Assert(!pMediumLockList);
11423 }
11424 else
11425 {
11426 AssertComRC(rc);
11427 if (pMediumLockList)
11428 {
11429 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11430 AssertComRC(rc);
11431 }
11432 }
11433 }
11434 }
11435 }
11436
11437 /* take media locks again so that the locking state is consistent */
11438 if (fMediaNeedsLocking)
11439 {
11440 Assert(aOnline);
11441 rc = mData->mSession.mLockedMedia.Lock();
11442 AssertComRC(rc);
11443 }
11444
11445 /* commit the hard disk changes */
11446 mMediumAttachments.commit();
11447
11448 if (i_isSessionMachine())
11449 {
11450 /*
11451 * Update the parent machine to point to the new owner.
11452 * This is necessary because the stored parent will point to the
11453 * session machine otherwise and cause crashes or errors later
11454 * when the session machine gets invalid.
11455 */
11456 /** @todo Change the MediumAttachment class to behave like any other
11457 * class in this regard by creating peer MediumAttachment
11458 * objects for session machines and share the data with the peer
11459 * machine.
11460 */
11461 for (MediumAttachmentList::const_iterator
11462 it = mMediumAttachments->begin();
11463 it != mMediumAttachments->end();
11464 ++it)
11465 (*it)->i_updateParentMachine(mPeer);
11466
11467 /* attach new data to the primary machine and reshare it */
11468 mPeer->mMediumAttachments.attach(mMediumAttachments);
11469 }
11470
11471 return;
11472}
11473
11474/**
11475 * Perform deferred deletion of implicitly created diffs.
11476 *
11477 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11478 * changed (not backed up).
11479 *
11480 * @note Locks this object for writing!
11481 */
11482void Machine::i_rollbackMedia()
11483{
11484 AutoCaller autoCaller(this);
11485 AssertComRCReturnVoid(autoCaller.rc());
11486
11487 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11488 LogFlowThisFunc(("Entering rollbackMedia\n"));
11489
11490 HRESULT rc = S_OK;
11491
11492 /* no attach/detach operations -- nothing to do */
11493 if (!mMediumAttachments.isBackedUp())
11494 return;
11495
11496 /* enumerate new attachments */
11497 for (MediumAttachmentList::const_iterator
11498 it = mMediumAttachments->begin();
11499 it != mMediumAttachments->end();
11500 ++it)
11501 {
11502 MediumAttachment *pAttach = *it;
11503 /* Fix up the backrefs for DVD/floppy media. */
11504 if (pAttach->i_getType() != DeviceType_HardDisk)
11505 {
11506 Medium *pMedium = pAttach->i_getMedium();
11507 if (pMedium)
11508 {
11509 rc = pMedium->i_removeBackReference(mData->mUuid);
11510 AssertComRC(rc);
11511 }
11512 }
11513
11514 (*it)->i_rollback();
11515
11516 pAttach = *it;
11517 /* Fix up the backrefs for DVD/floppy media. */
11518 if (pAttach->i_getType() != DeviceType_HardDisk)
11519 {
11520 Medium *pMedium = pAttach->i_getMedium();
11521 if (pMedium)
11522 {
11523 rc = pMedium->i_addBackReference(mData->mUuid);
11524 AssertComRC(rc);
11525 }
11526 }
11527 }
11528
11529 /** @todo convert all this Machine-based voodoo to MediumAttachment
11530 * based rollback logic. */
11531 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11532
11533 return;
11534}
11535
11536/**
11537 * Returns true if the settings file is located in the directory named exactly
11538 * as the machine; this means, among other things, that the machine directory
11539 * should be auto-renamed.
11540 *
11541 * @param aSettingsDir if not NULL, the full machine settings file directory
11542 * name will be assigned there.
11543 *
11544 * @note Doesn't lock anything.
11545 * @note Not thread safe (must be called from this object's lock).
11546 */
11547bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11548{
11549 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11550 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11551 if (aSettingsDir)
11552 *aSettingsDir = strMachineDirName;
11553 strMachineDirName.stripPath(); // vmname
11554 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11555 strConfigFileOnly.stripPath() // vmname.vbox
11556 .stripSuffix(); // vmname
11557 /** @todo hack, make somehow use of ComposeMachineFilename */
11558 if (mUserData->s.fDirectoryIncludesUUID)
11559 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11560
11561 AssertReturn(!strMachineDirName.isEmpty(), false);
11562 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11563
11564 return strMachineDirName == strConfigFileOnly;
11565}
11566
11567/**
11568 * Discards all changes to machine settings.
11569 *
11570 * @param aNotify Whether to notify the direct session about changes or not.
11571 *
11572 * @note Locks objects for writing!
11573 */
11574void Machine::i_rollback(bool aNotify)
11575{
11576 AutoCaller autoCaller(this);
11577 AssertComRCReturn(autoCaller.rc(), (void)0);
11578
11579 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11580
11581 if (!mStorageControllers.isNull())
11582 {
11583 if (mStorageControllers.isBackedUp())
11584 {
11585 /* unitialize all new devices (absent in the backed up list). */
11586 StorageControllerList *backedList = mStorageControllers.backedUpData();
11587 for (StorageControllerList::const_iterator
11588 it = mStorageControllers->begin();
11589 it != mStorageControllers->end();
11590 ++it)
11591 {
11592 if ( std::find(backedList->begin(), backedList->end(), *it)
11593 == backedList->end()
11594 )
11595 {
11596 (*it)->uninit();
11597 }
11598 }
11599
11600 /* restore the list */
11601 mStorageControllers.rollback();
11602 }
11603
11604 /* rollback any changes to devices after restoring the list */
11605 if (mData->flModifications & IsModified_Storage)
11606 {
11607 for (StorageControllerList::const_iterator
11608 it = mStorageControllers->begin();
11609 it != mStorageControllers->end();
11610 ++it)
11611 {
11612 (*it)->i_rollback();
11613 }
11614 }
11615 }
11616
11617 if (!mUSBControllers.isNull())
11618 {
11619 if (mUSBControllers.isBackedUp())
11620 {
11621 /* unitialize all new devices (absent in the backed up list). */
11622 USBControllerList *backedList = mUSBControllers.backedUpData();
11623 for (USBControllerList::const_iterator
11624 it = mUSBControllers->begin();
11625 it != mUSBControllers->end();
11626 ++it)
11627 {
11628 if ( std::find(backedList->begin(), backedList->end(), *it)
11629 == backedList->end()
11630 )
11631 {
11632 (*it)->uninit();
11633 }
11634 }
11635
11636 /* restore the list */
11637 mUSBControllers.rollback();
11638 }
11639
11640 /* rollback any changes to devices after restoring the list */
11641 if (mData->flModifications & IsModified_USB)
11642 {
11643 for (USBControllerList::const_iterator
11644 it = mUSBControllers->begin();
11645 it != mUSBControllers->end();
11646 ++it)
11647 {
11648 (*it)->i_rollback();
11649 }
11650 }
11651 }
11652
11653 mUserData.rollback();
11654
11655 mHWData.rollback();
11656
11657 if (mData->flModifications & IsModified_Storage)
11658 i_rollbackMedia();
11659
11660 if (mBIOSSettings)
11661 mBIOSSettings->i_rollback();
11662
11663 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11664 mRecordingSettings->i_rollback();
11665
11666 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11667 mGraphicsAdapter->i_rollback();
11668
11669 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11670 mVRDEServer->i_rollback();
11671
11672 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11673 mAudioAdapter->i_rollback();
11674
11675 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11676 mUSBDeviceFilters->i_rollback();
11677
11678 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11679 mBandwidthControl->i_rollback();
11680
11681 if (!mHWData.isNull())
11682 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11683 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11684 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11685 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11686
11687 if (mData->flModifications & IsModified_NetworkAdapters)
11688 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11689 if ( mNetworkAdapters[slot]
11690 && mNetworkAdapters[slot]->i_isModified())
11691 {
11692 mNetworkAdapters[slot]->i_rollback();
11693 networkAdapters[slot] = mNetworkAdapters[slot];
11694 }
11695
11696 if (mData->flModifications & IsModified_SerialPorts)
11697 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11698 if ( mSerialPorts[slot]
11699 && mSerialPorts[slot]->i_isModified())
11700 {
11701 mSerialPorts[slot]->i_rollback();
11702 serialPorts[slot] = mSerialPorts[slot];
11703 }
11704
11705 if (mData->flModifications & IsModified_ParallelPorts)
11706 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11707 if ( mParallelPorts[slot]
11708 && mParallelPorts[slot]->i_isModified())
11709 {
11710 mParallelPorts[slot]->i_rollback();
11711 parallelPorts[slot] = mParallelPorts[slot];
11712 }
11713
11714 if (aNotify)
11715 {
11716 /* inform the direct session about changes */
11717
11718 ComObjPtr<Machine> that = this;
11719 uint32_t flModifications = mData->flModifications;
11720 alock.release();
11721
11722 if (flModifications & IsModified_SharedFolders)
11723 that->i_onSharedFolderChange();
11724
11725 if (flModifications & IsModified_VRDEServer)
11726 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11727 if (flModifications & IsModified_USB)
11728 that->i_onUSBControllerChange();
11729
11730 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11731 if (networkAdapters[slot])
11732 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11733 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11734 if (serialPorts[slot])
11735 that->i_onSerialPortChange(serialPorts[slot]);
11736 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11737 if (parallelPorts[slot])
11738 that->i_onParallelPortChange(parallelPorts[slot]);
11739
11740 if (flModifications & IsModified_Storage)
11741 {
11742 for (StorageControllerList::const_iterator
11743 it = mStorageControllers->begin();
11744 it != mStorageControllers->end();
11745 ++it)
11746 {
11747 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11748 }
11749 }
11750
11751
11752#if 0
11753 if (flModifications & IsModified_BandwidthControl)
11754 that->onBandwidthControlChange();
11755#endif
11756 }
11757}
11758
11759/**
11760 * Commits all the changes to machine settings.
11761 *
11762 * Note that this operation is supposed to never fail.
11763 *
11764 * @note Locks this object and children for writing.
11765 */
11766void Machine::i_commit()
11767{
11768 AutoCaller autoCaller(this);
11769 AssertComRCReturnVoid(autoCaller.rc());
11770
11771 AutoCaller peerCaller(mPeer);
11772 AssertComRCReturnVoid(peerCaller.rc());
11773
11774 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11775
11776 /*
11777 * use safe commit to ensure Snapshot machines (that share mUserData)
11778 * will still refer to a valid memory location
11779 */
11780 mUserData.commitCopy();
11781
11782 mHWData.commit();
11783
11784 if (mMediumAttachments.isBackedUp())
11785 i_commitMedia(Global::IsOnline(mData->mMachineState));
11786
11787 mBIOSSettings->i_commit();
11788 mRecordingSettings->i_commit();
11789 mGraphicsAdapter->i_commit();
11790 mVRDEServer->i_commit();
11791 mAudioAdapter->i_commit();
11792 mUSBDeviceFilters->i_commit();
11793 mBandwidthControl->i_commit();
11794
11795 /* Since mNetworkAdapters is a list which might have been changed (resized)
11796 * without using the Backupable<> template we need to handle the copying
11797 * of the list entries manually, including the creation of peers for the
11798 * new objects. */
11799 bool commitNetworkAdapters = false;
11800 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11801 if (mPeer)
11802 {
11803 /* commit everything, even the ones which will go away */
11804 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11805 mNetworkAdapters[slot]->i_commit();
11806 /* copy over the new entries, creating a peer and uninit the original */
11807 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11808 for (size_t slot = 0; slot < newSize; slot++)
11809 {
11810 /* look if this adapter has a peer device */
11811 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11812 if (!peer)
11813 {
11814 /* no peer means the adapter is a newly created one;
11815 * create a peer owning data this data share it with */
11816 peer.createObject();
11817 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11818 }
11819 mPeer->mNetworkAdapters[slot] = peer;
11820 }
11821 /* uninit any no longer needed network adapters */
11822 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11823 mNetworkAdapters[slot]->uninit();
11824 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11825 {
11826 if (mPeer->mNetworkAdapters[slot])
11827 mPeer->mNetworkAdapters[slot]->uninit();
11828 }
11829 /* Keep the original network adapter count until this point, so that
11830 * discarding a chipset type change will not lose settings. */
11831 mNetworkAdapters.resize(newSize);
11832 mPeer->mNetworkAdapters.resize(newSize);
11833 }
11834 else
11835 {
11836 /* we have no peer (our parent is the newly created machine);
11837 * just commit changes to the network adapters */
11838 commitNetworkAdapters = true;
11839 }
11840 if (commitNetworkAdapters)
11841 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11842 mNetworkAdapters[slot]->i_commit();
11843
11844 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11845 mSerialPorts[slot]->i_commit();
11846 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11847 mParallelPorts[slot]->i_commit();
11848
11849 bool commitStorageControllers = false;
11850
11851 if (mStorageControllers.isBackedUp())
11852 {
11853 mStorageControllers.commit();
11854
11855 if (mPeer)
11856 {
11857 /* Commit all changes to new controllers (this will reshare data with
11858 * peers for those who have peers) */
11859 StorageControllerList *newList = new StorageControllerList();
11860 for (StorageControllerList::const_iterator
11861 it = mStorageControllers->begin();
11862 it != mStorageControllers->end();
11863 ++it)
11864 {
11865 (*it)->i_commit();
11866
11867 /* look if this controller has a peer device */
11868 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11869 if (!peer)
11870 {
11871 /* no peer means the device is a newly created one;
11872 * create a peer owning data this device share it with */
11873 peer.createObject();
11874 peer->init(mPeer, *it, true /* aReshare */);
11875 }
11876 else
11877 {
11878 /* remove peer from the old list */
11879 mPeer->mStorageControllers->remove(peer);
11880 }
11881 /* and add it to the new list */
11882 newList->push_back(peer);
11883 }
11884
11885 /* uninit old peer's controllers that are left */
11886 for (StorageControllerList::const_iterator
11887 it = mPeer->mStorageControllers->begin();
11888 it != mPeer->mStorageControllers->end();
11889 ++it)
11890 {
11891 (*it)->uninit();
11892 }
11893
11894 /* attach new list of controllers to our peer */
11895 mPeer->mStorageControllers.attach(newList);
11896 }
11897 else
11898 {
11899 /* we have no peer (our parent is the newly created machine);
11900 * just commit changes to devices */
11901 commitStorageControllers = true;
11902 }
11903 }
11904 else
11905 {
11906 /* the list of controllers itself is not changed,
11907 * just commit changes to controllers themselves */
11908 commitStorageControllers = true;
11909 }
11910
11911 if (commitStorageControllers)
11912 {
11913 for (StorageControllerList::const_iterator
11914 it = mStorageControllers->begin();
11915 it != mStorageControllers->end();
11916 ++it)
11917 {
11918 (*it)->i_commit();
11919 }
11920 }
11921
11922 bool commitUSBControllers = false;
11923
11924 if (mUSBControllers.isBackedUp())
11925 {
11926 mUSBControllers.commit();
11927
11928 if (mPeer)
11929 {
11930 /* Commit all changes to new controllers (this will reshare data with
11931 * peers for those who have peers) */
11932 USBControllerList *newList = new USBControllerList();
11933 for (USBControllerList::const_iterator
11934 it = mUSBControllers->begin();
11935 it != mUSBControllers->end();
11936 ++it)
11937 {
11938 (*it)->i_commit();
11939
11940 /* look if this controller has a peer device */
11941 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11942 if (!peer)
11943 {
11944 /* no peer means the device is a newly created one;
11945 * create a peer owning data this device share it with */
11946 peer.createObject();
11947 peer->init(mPeer, *it, true /* aReshare */);
11948 }
11949 else
11950 {
11951 /* remove peer from the old list */
11952 mPeer->mUSBControllers->remove(peer);
11953 }
11954 /* and add it to the new list */
11955 newList->push_back(peer);
11956 }
11957
11958 /* uninit old peer's controllers that are left */
11959 for (USBControllerList::const_iterator
11960 it = mPeer->mUSBControllers->begin();
11961 it != mPeer->mUSBControllers->end();
11962 ++it)
11963 {
11964 (*it)->uninit();
11965 }
11966
11967 /* attach new list of controllers to our peer */
11968 mPeer->mUSBControllers.attach(newList);
11969 }
11970 else
11971 {
11972 /* we have no peer (our parent is the newly created machine);
11973 * just commit changes to devices */
11974 commitUSBControllers = true;
11975 }
11976 }
11977 else
11978 {
11979 /* the list of controllers itself is not changed,
11980 * just commit changes to controllers themselves */
11981 commitUSBControllers = true;
11982 }
11983
11984 if (commitUSBControllers)
11985 {
11986 for (USBControllerList::const_iterator
11987 it = mUSBControllers->begin();
11988 it != mUSBControllers->end();
11989 ++it)
11990 {
11991 (*it)->i_commit();
11992 }
11993 }
11994
11995 if (i_isSessionMachine())
11996 {
11997 /* attach new data to the primary machine and reshare it */
11998 mPeer->mUserData.attach(mUserData);
11999 mPeer->mHWData.attach(mHWData);
12000 /* mmMediumAttachments is reshared by fixupMedia */
12001 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12002 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12003 }
12004}
12005
12006/**
12007 * Copies all the hardware data from the given machine.
12008 *
12009 * Currently, only called when the VM is being restored from a snapshot. In
12010 * particular, this implies that the VM is not running during this method's
12011 * call.
12012 *
12013 * @note This method must be called from under this object's lock.
12014 *
12015 * @note This method doesn't call #i_commit(), so all data remains backed up and
12016 * unsaved.
12017 */
12018void Machine::i_copyFrom(Machine *aThat)
12019{
12020 AssertReturnVoid(!i_isSnapshotMachine());
12021 AssertReturnVoid(aThat->i_isSnapshotMachine());
12022
12023 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12024
12025 mHWData.assignCopy(aThat->mHWData);
12026
12027 // create copies of all shared folders (mHWData after attaching a copy
12028 // contains just references to original objects)
12029 for (HWData::SharedFolderList::iterator
12030 it = mHWData->mSharedFolders.begin();
12031 it != mHWData->mSharedFolders.end();
12032 ++it)
12033 {
12034 ComObjPtr<SharedFolder> folder;
12035 folder.createObject();
12036 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12037 AssertComRC(rc);
12038 *it = folder;
12039 }
12040
12041 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12042 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12043 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
12044 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12045 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12046 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12047 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12048
12049 /* create private copies of all controllers */
12050 mStorageControllers.backup();
12051 mStorageControllers->clear();
12052 for (StorageControllerList::const_iterator
12053 it = aThat->mStorageControllers->begin();
12054 it != aThat->mStorageControllers->end();
12055 ++it)
12056 {
12057 ComObjPtr<StorageController> ctrl;
12058 ctrl.createObject();
12059 ctrl->initCopy(this, *it);
12060 mStorageControllers->push_back(ctrl);
12061 }
12062
12063 /* create private copies of all USB controllers */
12064 mUSBControllers.backup();
12065 mUSBControllers->clear();
12066 for (USBControllerList::const_iterator
12067 it = aThat->mUSBControllers->begin();
12068 it != aThat->mUSBControllers->end();
12069 ++it)
12070 {
12071 ComObjPtr<USBController> ctrl;
12072 ctrl.createObject();
12073 ctrl->initCopy(this, *it);
12074 mUSBControllers->push_back(ctrl);
12075 }
12076
12077 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12078 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12079 {
12080 if (mNetworkAdapters[slot].isNotNull())
12081 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12082 else
12083 {
12084 unconst(mNetworkAdapters[slot]).createObject();
12085 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12086 }
12087 }
12088 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12089 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12090 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12091 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12092}
12093
12094/**
12095 * Returns whether the given storage controller is hotplug capable.
12096 *
12097 * @returns true if the controller supports hotplugging
12098 * false otherwise.
12099 * @param enmCtrlType The controller type to check for.
12100 */
12101bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12102{
12103 ComPtr<ISystemProperties> systemProperties;
12104 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12105 if (FAILED(rc))
12106 return false;
12107
12108 BOOL aHotplugCapable = FALSE;
12109 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12110
12111 return RT_BOOL(aHotplugCapable);
12112}
12113
12114#ifdef VBOX_WITH_RESOURCE_USAGE_API
12115
12116void Machine::i_getDiskList(MediaList &list)
12117{
12118 for (MediumAttachmentList::const_iterator
12119 it = mMediumAttachments->begin();
12120 it != mMediumAttachments->end();
12121 ++it)
12122 {
12123 MediumAttachment *pAttach = *it;
12124 /* just in case */
12125 AssertContinue(pAttach);
12126
12127 AutoCaller localAutoCallerA(pAttach);
12128 if (FAILED(localAutoCallerA.rc())) continue;
12129
12130 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12131
12132 if (pAttach->i_getType() == DeviceType_HardDisk)
12133 list.push_back(pAttach->i_getMedium());
12134 }
12135}
12136
12137void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12138{
12139 AssertReturnVoid(isWriteLockOnCurrentThread());
12140 AssertPtrReturnVoid(aCollector);
12141
12142 pm::CollectorHAL *hal = aCollector->getHAL();
12143 /* Create sub metrics */
12144 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12145 "Percentage of processor time spent in user mode by the VM process.");
12146 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12147 "Percentage of processor time spent in kernel mode by the VM process.");
12148 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12149 "Size of resident portion of VM process in memory.");
12150 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12151 "Actual size of all VM disks combined.");
12152 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12153 "Network receive rate.");
12154 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12155 "Network transmit rate.");
12156 /* Create and register base metrics */
12157 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12158 cpuLoadUser, cpuLoadKernel);
12159 aCollector->registerBaseMetric(cpuLoad);
12160 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12161 ramUsageUsed);
12162 aCollector->registerBaseMetric(ramUsage);
12163 MediaList disks;
12164 i_getDiskList(disks);
12165 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12166 diskUsageUsed);
12167 aCollector->registerBaseMetric(diskUsage);
12168
12169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12170 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12171 new pm::AggregateAvg()));
12172 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12173 new pm::AggregateMin()));
12174 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12175 new pm::AggregateMax()));
12176 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12177 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12178 new pm::AggregateAvg()));
12179 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12180 new pm::AggregateMin()));
12181 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12182 new pm::AggregateMax()));
12183
12184 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12185 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12186 new pm::AggregateAvg()));
12187 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12188 new pm::AggregateMin()));
12189 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12190 new pm::AggregateMax()));
12191
12192 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12193 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12194 new pm::AggregateAvg()));
12195 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12196 new pm::AggregateMin()));
12197 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12198 new pm::AggregateMax()));
12199
12200
12201 /* Guest metrics collector */
12202 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12203 aCollector->registerGuest(mCollectorGuest);
12204 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12205
12206 /* Create sub metrics */
12207 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12208 "Percentage of processor time spent in user mode as seen by the guest.");
12209 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12210 "Percentage of processor time spent in kernel mode as seen by the guest.");
12211 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12212 "Percentage of processor time spent idling as seen by the guest.");
12213
12214 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12215 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12216 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12217 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12218 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12219 pm::SubMetric *guestMemCache = new pm::SubMetric(
12220 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12221
12222 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12223 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12224
12225 /* Create and register base metrics */
12226 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12227 machineNetRx, machineNetTx);
12228 aCollector->registerBaseMetric(machineNetRate);
12229
12230 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12231 guestLoadUser, guestLoadKernel, guestLoadIdle);
12232 aCollector->registerBaseMetric(guestCpuLoad);
12233
12234 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12235 guestMemTotal, guestMemFree,
12236 guestMemBalloon, guestMemShared,
12237 guestMemCache, guestPagedTotal);
12238 aCollector->registerBaseMetric(guestCpuMem);
12239
12240 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12241 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12242 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12243 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12244
12245 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12246 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12247 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12248 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12249
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12253 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12254
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12258 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12259
12260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12263 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12264
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12269
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12274
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12279
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12284
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12289
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12294}
12295
12296void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12297{
12298 AssertReturnVoid(isWriteLockOnCurrentThread());
12299
12300 if (aCollector)
12301 {
12302 aCollector->unregisterMetricsFor(aMachine);
12303 aCollector->unregisterBaseMetricsFor(aMachine);
12304 }
12305}
12306
12307#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12308
12309
12310////////////////////////////////////////////////////////////////////////////////
12311
12312DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12313
12314HRESULT SessionMachine::FinalConstruct()
12315{
12316 LogFlowThisFunc(("\n"));
12317
12318 mClientToken = NULL;
12319
12320 return BaseFinalConstruct();
12321}
12322
12323void SessionMachine::FinalRelease()
12324{
12325 LogFlowThisFunc(("\n"));
12326
12327 Assert(!mClientToken);
12328 /* paranoia, should not hang around any more */
12329 if (mClientToken)
12330 {
12331 delete mClientToken;
12332 mClientToken = NULL;
12333 }
12334
12335 uninit(Uninit::Unexpected);
12336
12337 BaseFinalRelease();
12338}
12339
12340/**
12341 * @note Must be called only by Machine::LockMachine() from its own write lock.
12342 */
12343HRESULT SessionMachine::init(Machine *aMachine)
12344{
12345 LogFlowThisFuncEnter();
12346 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12347
12348 AssertReturn(aMachine, E_INVALIDARG);
12349
12350 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12351
12352 /* Enclose the state transition NotReady->InInit->Ready */
12353 AutoInitSpan autoInitSpan(this);
12354 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12355
12356 HRESULT rc = S_OK;
12357
12358 RT_ZERO(mAuthLibCtx);
12359
12360 /* create the machine client token */
12361 try
12362 {
12363 mClientToken = new ClientToken(aMachine, this);
12364 if (!mClientToken->isReady())
12365 {
12366 delete mClientToken;
12367 mClientToken = NULL;
12368 rc = E_FAIL;
12369 }
12370 }
12371 catch (std::bad_alloc &)
12372 {
12373 rc = E_OUTOFMEMORY;
12374 }
12375 if (FAILED(rc))
12376 return rc;
12377
12378 /* memorize the peer Machine */
12379 unconst(mPeer) = aMachine;
12380 /* share the parent pointer */
12381 unconst(mParent) = aMachine->mParent;
12382
12383 /* take the pointers to data to share */
12384 mData.share(aMachine->mData);
12385 mSSData.share(aMachine->mSSData);
12386
12387 mUserData.share(aMachine->mUserData);
12388 mHWData.share(aMachine->mHWData);
12389 mMediumAttachments.share(aMachine->mMediumAttachments);
12390
12391 mStorageControllers.allocate();
12392 for (StorageControllerList::const_iterator
12393 it = aMachine->mStorageControllers->begin();
12394 it != aMachine->mStorageControllers->end();
12395 ++it)
12396 {
12397 ComObjPtr<StorageController> ctl;
12398 ctl.createObject();
12399 ctl->init(this, *it);
12400 mStorageControllers->push_back(ctl);
12401 }
12402
12403 mUSBControllers.allocate();
12404 for (USBControllerList::const_iterator
12405 it = aMachine->mUSBControllers->begin();
12406 it != aMachine->mUSBControllers->end();
12407 ++it)
12408 {
12409 ComObjPtr<USBController> ctl;
12410 ctl.createObject();
12411 ctl->init(this, *it);
12412 mUSBControllers->push_back(ctl);
12413 }
12414
12415 unconst(mBIOSSettings).createObject();
12416 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12417 unconst(mRecordingSettings).createObject();
12418 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12419 /* create another GraphicsAdapter object that will be mutable */
12420 unconst(mGraphicsAdapter).createObject();
12421 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12422 /* create another VRDEServer object that will be mutable */
12423 unconst(mVRDEServer).createObject();
12424 mVRDEServer->init(this, aMachine->mVRDEServer);
12425 /* create another audio adapter object that will be mutable */
12426 unconst(mAudioAdapter).createObject();
12427 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12428 /* create a list of serial ports that will be mutable */
12429 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12430 {
12431 unconst(mSerialPorts[slot]).createObject();
12432 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12433 }
12434 /* create a list of parallel ports that will be mutable */
12435 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12436 {
12437 unconst(mParallelPorts[slot]).createObject();
12438 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12439 }
12440
12441 /* create another USB device filters object that will be mutable */
12442 unconst(mUSBDeviceFilters).createObject();
12443 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12444
12445 /* create a list of network adapters that will be mutable */
12446 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12447 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12448 {
12449 unconst(mNetworkAdapters[slot]).createObject();
12450 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12451 }
12452
12453 /* create another bandwidth control object that will be mutable */
12454 unconst(mBandwidthControl).createObject();
12455 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12456
12457 /* default is to delete saved state on Saved -> PoweredOff transition */
12458 mRemoveSavedState = true;
12459
12460 /* Confirm a successful initialization when it's the case */
12461 autoInitSpan.setSucceeded();
12462
12463 miNATNetworksStarted = 0;
12464
12465 LogFlowThisFuncLeave();
12466 return rc;
12467}
12468
12469/**
12470 * Uninitializes this session object. If the reason is other than
12471 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12472 * or the client watcher code.
12473 *
12474 * @param aReason uninitialization reason
12475 *
12476 * @note Locks mParent + this object for writing.
12477 */
12478void SessionMachine::uninit(Uninit::Reason aReason)
12479{
12480 LogFlowThisFuncEnter();
12481 LogFlowThisFunc(("reason=%d\n", aReason));
12482
12483 /*
12484 * Strongly reference ourselves to prevent this object deletion after
12485 * mData->mSession.mMachine.setNull() below (which can release the last
12486 * reference and call the destructor). Important: this must be done before
12487 * accessing any members (and before AutoUninitSpan that does it as well).
12488 * This self reference will be released as the very last step on return.
12489 */
12490 ComObjPtr<SessionMachine> selfRef;
12491 if (aReason != Uninit::Unexpected)
12492 selfRef = this;
12493
12494 /* Enclose the state transition Ready->InUninit->NotReady */
12495 AutoUninitSpan autoUninitSpan(this);
12496 if (autoUninitSpan.uninitDone())
12497 {
12498 LogFlowThisFunc(("Already uninitialized\n"));
12499 LogFlowThisFuncLeave();
12500 return;
12501 }
12502
12503 if (autoUninitSpan.initFailed())
12504 {
12505 /* We've been called by init() because it's failed. It's not really
12506 * necessary (nor it's safe) to perform the regular uninit sequence
12507 * below, the following is enough.
12508 */
12509 LogFlowThisFunc(("Initialization failed.\n"));
12510 /* destroy the machine client token */
12511 if (mClientToken)
12512 {
12513 delete mClientToken;
12514 mClientToken = NULL;
12515 }
12516 uninitDataAndChildObjects();
12517 mData.free();
12518 unconst(mParent) = NULL;
12519 unconst(mPeer) = NULL;
12520 LogFlowThisFuncLeave();
12521 return;
12522 }
12523
12524 MachineState_T lastState;
12525 {
12526 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12527 lastState = mData->mMachineState;
12528 }
12529 NOREF(lastState);
12530
12531#ifdef VBOX_WITH_USB
12532 // release all captured USB devices, but do this before requesting the locks below
12533 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12534 {
12535 /* Console::captureUSBDevices() is called in the VM process only after
12536 * setting the machine state to Starting or Restoring.
12537 * Console::detachAllUSBDevices() will be called upon successful
12538 * termination. So, we need to release USB devices only if there was
12539 * an abnormal termination of a running VM.
12540 *
12541 * This is identical to SessionMachine::DetachAllUSBDevices except
12542 * for the aAbnormal argument. */
12543 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12544 AssertComRC(rc);
12545 NOREF(rc);
12546
12547 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12548 if (service)
12549 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12550 }
12551#endif /* VBOX_WITH_USB */
12552
12553 // we need to lock this object in uninit() because the lock is shared
12554 // with mPeer (as well as data we modify below). mParent lock is needed
12555 // by several calls to it.
12556 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12557
12558#ifdef VBOX_WITH_RESOURCE_USAGE_API
12559 /*
12560 * It is safe to call Machine::i_unregisterMetrics() here because
12561 * PerformanceCollector::samplerCallback no longer accesses guest methods
12562 * holding the lock.
12563 */
12564 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12565 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12566 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12567 if (mCollectorGuest)
12568 {
12569 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12570 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12571 mCollectorGuest = NULL;
12572 }
12573#endif
12574
12575 if (aReason == Uninit::Abnormal)
12576 {
12577 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12578
12579 /* reset the state to Aborted */
12580 if (mData->mMachineState != MachineState_Aborted)
12581 i_setMachineState(MachineState_Aborted);
12582 }
12583
12584 // any machine settings modified?
12585 if (mData->flModifications)
12586 {
12587 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12588 i_rollback(false /* aNotify */);
12589 }
12590
12591 mData->mSession.mPID = NIL_RTPROCESS;
12592
12593 if (aReason == Uninit::Unexpected)
12594 {
12595 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12596 * client watcher thread to update the set of machines that have open
12597 * sessions. */
12598 mParent->i_updateClientWatcher();
12599 }
12600
12601 /* uninitialize all remote controls */
12602 if (mData->mSession.mRemoteControls.size())
12603 {
12604 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12605 mData->mSession.mRemoteControls.size()));
12606
12607 /* Always restart a the beginning, since the iterator is invalidated
12608 * by using erase(). */
12609 for (Data::Session::RemoteControlList::iterator
12610 it = mData->mSession.mRemoteControls.begin();
12611 it != mData->mSession.mRemoteControls.end();
12612 it = mData->mSession.mRemoteControls.begin())
12613 {
12614 ComPtr<IInternalSessionControl> pControl = *it;
12615 mData->mSession.mRemoteControls.erase(it);
12616 multilock.release();
12617 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12618 HRESULT rc = pControl->Uninitialize();
12619 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12620 if (FAILED(rc))
12621 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12622 multilock.acquire();
12623 }
12624 mData->mSession.mRemoteControls.clear();
12625 }
12626
12627 /* Remove all references to the NAT network service. The service will stop
12628 * if all references (also from other VMs) are removed. */
12629 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12630 {
12631 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12632 {
12633 BOOL enabled;
12634 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12635 if ( FAILED(hrc)
12636 || !enabled)
12637 continue;
12638
12639 NetworkAttachmentType_T type;
12640 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12641 if ( SUCCEEDED(hrc)
12642 && type == NetworkAttachmentType_NATNetwork)
12643 {
12644 Bstr name;
12645 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12646 if (SUCCEEDED(hrc))
12647 {
12648 multilock.release();
12649 Utf8Str strName(name);
12650 LogRel(("VM '%s' stops using NAT network '%s'\n",
12651 mUserData->s.strName.c_str(), strName.c_str()));
12652 mParent->i_natNetworkRefDec(strName);
12653 multilock.acquire();
12654 }
12655 }
12656 }
12657 }
12658
12659 /*
12660 * An expected uninitialization can come only from #i_checkForDeath().
12661 * Otherwise it means that something's gone really wrong (for example,
12662 * the Session implementation has released the VirtualBox reference
12663 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12664 * etc). However, it's also possible, that the client releases the IPC
12665 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12666 * but the VirtualBox release event comes first to the server process.
12667 * This case is practically possible, so we should not assert on an
12668 * unexpected uninit, just log a warning.
12669 */
12670
12671 if (aReason == Uninit::Unexpected)
12672 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12673
12674 if (aReason != Uninit::Normal)
12675 {
12676 mData->mSession.mDirectControl.setNull();
12677 }
12678 else
12679 {
12680 /* this must be null here (see #OnSessionEnd()) */
12681 Assert(mData->mSession.mDirectControl.isNull());
12682 Assert(mData->mSession.mState == SessionState_Unlocking);
12683 Assert(!mData->mSession.mProgress.isNull());
12684 }
12685 if (mData->mSession.mProgress)
12686 {
12687 if (aReason == Uninit::Normal)
12688 mData->mSession.mProgress->i_notifyComplete(S_OK);
12689 else
12690 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12691 COM_IIDOF(ISession),
12692 getComponentName(),
12693 tr("The VM session was aborted"));
12694 mData->mSession.mProgress.setNull();
12695 }
12696
12697 if (mConsoleTaskData.mProgress)
12698 {
12699 Assert(aReason == Uninit::Abnormal);
12700 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12701 COM_IIDOF(ISession),
12702 getComponentName(),
12703 tr("The VM session was aborted"));
12704 mConsoleTaskData.mProgress.setNull();
12705 }
12706
12707 /* remove the association between the peer machine and this session machine */
12708 Assert( (SessionMachine*)mData->mSession.mMachine == this
12709 || aReason == Uninit::Unexpected);
12710
12711 /* reset the rest of session data */
12712 mData->mSession.mLockType = LockType_Null;
12713 mData->mSession.mMachine.setNull();
12714 mData->mSession.mState = SessionState_Unlocked;
12715 mData->mSession.mName.setNull();
12716
12717 /* destroy the machine client token before leaving the exclusive lock */
12718 if (mClientToken)
12719 {
12720 delete mClientToken;
12721 mClientToken = NULL;
12722 }
12723
12724 /* fire an event */
12725 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12726
12727 uninitDataAndChildObjects();
12728
12729 /* free the essential data structure last */
12730 mData.free();
12731
12732 /* release the exclusive lock before setting the below two to NULL */
12733 multilock.release();
12734
12735 unconst(mParent) = NULL;
12736 unconst(mPeer) = NULL;
12737
12738 AuthLibUnload(&mAuthLibCtx);
12739
12740 LogFlowThisFuncLeave();
12741}
12742
12743// util::Lockable interface
12744////////////////////////////////////////////////////////////////////////////////
12745
12746/**
12747 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12748 * with the primary Machine instance (mPeer).
12749 */
12750RWLockHandle *SessionMachine::lockHandle() const
12751{
12752 AssertReturn(mPeer != NULL, NULL);
12753 return mPeer->lockHandle();
12754}
12755
12756// IInternalMachineControl methods
12757////////////////////////////////////////////////////////////////////////////////
12758
12759/**
12760 * Passes collected guest statistics to performance collector object
12761 */
12762HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12763 ULONG aCpuKernel, ULONG aCpuIdle,
12764 ULONG aMemTotal, ULONG aMemFree,
12765 ULONG aMemBalloon, ULONG aMemShared,
12766 ULONG aMemCache, ULONG aPageTotal,
12767 ULONG aAllocVMM, ULONG aFreeVMM,
12768 ULONG aBalloonedVMM, ULONG aSharedVMM,
12769 ULONG aVmNetRx, ULONG aVmNetTx)
12770{
12771#ifdef VBOX_WITH_RESOURCE_USAGE_API
12772 if (mCollectorGuest)
12773 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12774 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12775 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12776 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12777
12778 return S_OK;
12779#else
12780 NOREF(aValidStats);
12781 NOREF(aCpuUser);
12782 NOREF(aCpuKernel);
12783 NOREF(aCpuIdle);
12784 NOREF(aMemTotal);
12785 NOREF(aMemFree);
12786 NOREF(aMemBalloon);
12787 NOREF(aMemShared);
12788 NOREF(aMemCache);
12789 NOREF(aPageTotal);
12790 NOREF(aAllocVMM);
12791 NOREF(aFreeVMM);
12792 NOREF(aBalloonedVMM);
12793 NOREF(aSharedVMM);
12794 NOREF(aVmNetRx);
12795 NOREF(aVmNetTx);
12796 return E_NOTIMPL;
12797#endif
12798}
12799
12800////////////////////////////////////////////////////////////////////////////////
12801//
12802// SessionMachine task records
12803//
12804////////////////////////////////////////////////////////////////////////////////
12805
12806/**
12807 * Task record for saving the machine state.
12808 */
12809class SessionMachine::SaveStateTask
12810 : public Machine::Task
12811{
12812public:
12813 SaveStateTask(SessionMachine *m,
12814 Progress *p,
12815 const Utf8Str &t,
12816 Reason_T enmReason,
12817 const Utf8Str &strStateFilePath)
12818 : Task(m, p, t),
12819 m_enmReason(enmReason),
12820 m_strStateFilePath(strStateFilePath)
12821 {}
12822
12823private:
12824 void handler()
12825 {
12826 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12827 }
12828
12829 Reason_T m_enmReason;
12830 Utf8Str m_strStateFilePath;
12831
12832 friend class SessionMachine;
12833};
12834
12835/**
12836 * Task thread implementation for SessionMachine::SaveState(), called from
12837 * SessionMachine::taskHandler().
12838 *
12839 * @note Locks this object for writing.
12840 *
12841 * @param task
12842 * @return
12843 */
12844void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12845{
12846 LogFlowThisFuncEnter();
12847
12848 AutoCaller autoCaller(this);
12849 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12850 if (FAILED(autoCaller.rc()))
12851 {
12852 /* we might have been uninitialized because the session was accidentally
12853 * closed by the client, so don't assert */
12854 HRESULT rc = setError(E_FAIL,
12855 tr("The session has been accidentally closed"));
12856 task.m_pProgress->i_notifyComplete(rc);
12857 LogFlowThisFuncLeave();
12858 return;
12859 }
12860
12861 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12862
12863 HRESULT rc = S_OK;
12864
12865 try
12866 {
12867 ComPtr<IInternalSessionControl> directControl;
12868 if (mData->mSession.mLockType == LockType_VM)
12869 directControl = mData->mSession.mDirectControl;
12870 if (directControl.isNull())
12871 throw setError(VBOX_E_INVALID_VM_STATE,
12872 tr("Trying to save state without a running VM"));
12873 alock.release();
12874 BOOL fSuspendedBySave;
12875 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12876 Assert(!fSuspendedBySave);
12877 alock.acquire();
12878
12879 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12880 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12881 throw E_FAIL);
12882
12883 if (SUCCEEDED(rc))
12884 {
12885 mSSData->strStateFilePath = task.m_strStateFilePath;
12886
12887 /* save all VM settings */
12888 rc = i_saveSettings(NULL);
12889 // no need to check whether VirtualBox.xml needs saving also since
12890 // we can't have a name change pending at this point
12891 }
12892 else
12893 {
12894 // On failure, set the state to the state we had at the beginning.
12895 i_setMachineState(task.m_machineStateBackup);
12896 i_updateMachineStateOnClient();
12897
12898 // Delete the saved state file (might have been already created).
12899 // No need to check whether this is shared with a snapshot here
12900 // because we certainly created a fresh saved state file here.
12901 RTFileDelete(task.m_strStateFilePath.c_str());
12902 }
12903 }
12904 catch (HRESULT aRC) { rc = aRC; }
12905
12906 task.m_pProgress->i_notifyComplete(rc);
12907
12908 LogFlowThisFuncLeave();
12909}
12910
12911/**
12912 * @note Locks this object for writing.
12913 */
12914HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12915{
12916 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12917}
12918
12919HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12920{
12921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12922
12923 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12924 if (FAILED(rc)) return rc;
12925
12926 if ( mData->mMachineState != MachineState_Running
12927 && mData->mMachineState != MachineState_Paused
12928 )
12929 return setError(VBOX_E_INVALID_VM_STATE,
12930 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12931 Global::stringifyMachineState(mData->mMachineState));
12932
12933 ComObjPtr<Progress> pProgress;
12934 pProgress.createObject();
12935 rc = pProgress->init(i_getVirtualBox(),
12936 static_cast<IMachine *>(this) /* aInitiator */,
12937 tr("Saving the execution state of the virtual machine"),
12938 FALSE /* aCancelable */);
12939 if (FAILED(rc))
12940 return rc;
12941
12942 Utf8Str strStateFilePath;
12943 i_composeSavedStateFilename(strStateFilePath);
12944
12945 /* create and start the task on a separate thread (note that it will not
12946 * start working until we release alock) */
12947 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12948 rc = pTask->createThread();
12949 if (FAILED(rc))
12950 return rc;
12951
12952 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12953 i_setMachineState(MachineState_Saving);
12954 i_updateMachineStateOnClient();
12955
12956 pProgress.queryInterfaceTo(aProgress.asOutParam());
12957
12958 return S_OK;
12959}
12960
12961/**
12962 * @note Locks this object for writing.
12963 */
12964HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12965{
12966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12967
12968 HRESULT rc = i_checkStateDependency(MutableStateDep);
12969 if (FAILED(rc)) return rc;
12970
12971 if ( mData->mMachineState != MachineState_PoweredOff
12972 && mData->mMachineState != MachineState_Teleported
12973 && mData->mMachineState != MachineState_Aborted
12974 )
12975 return setError(VBOX_E_INVALID_VM_STATE,
12976 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12977 Global::stringifyMachineState(mData->mMachineState));
12978
12979 com::Utf8Str stateFilePathFull;
12980 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12981 if (RT_FAILURE(vrc))
12982 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12983 tr("Invalid saved state file path '%s' (%Rrc)"),
12984 aSavedStateFile.c_str(),
12985 vrc);
12986
12987 mSSData->strStateFilePath = stateFilePathFull;
12988
12989 /* The below i_setMachineState() will detect the state transition and will
12990 * update the settings file */
12991
12992 return i_setMachineState(MachineState_Saved);
12993}
12994
12995/**
12996 * @note Locks this object for writing.
12997 */
12998HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12999{
13000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13001
13002 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13003 if (FAILED(rc)) return rc;
13004
13005 if (mData->mMachineState != MachineState_Saved)
13006 return setError(VBOX_E_INVALID_VM_STATE,
13007 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13008 Global::stringifyMachineState(mData->mMachineState));
13009
13010 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13011
13012 /*
13013 * Saved -> PoweredOff transition will be detected in the SessionMachine
13014 * and properly handled.
13015 */
13016 rc = i_setMachineState(MachineState_PoweredOff);
13017 return rc;
13018}
13019
13020
13021/**
13022 * @note Locks the same as #i_setMachineState() does.
13023 */
13024HRESULT SessionMachine::updateState(MachineState_T aState)
13025{
13026 return i_setMachineState(aState);
13027}
13028
13029/**
13030 * @note Locks this object for writing.
13031 */
13032HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13033{
13034 IProgress *pProgress(aProgress);
13035
13036 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13037
13038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13039
13040 if (mData->mSession.mState != SessionState_Locked)
13041 return VBOX_E_INVALID_OBJECT_STATE;
13042
13043 if (!mData->mSession.mProgress.isNull())
13044 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13045
13046 /* If we didn't reference the NAT network service yet, add a reference to
13047 * force a start */
13048 if (miNATNetworksStarted < 1)
13049 {
13050 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13051 {
13052 BOOL enabled;
13053 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13054 if ( FAILED(hrc)
13055 || !enabled)
13056 continue;
13057
13058 NetworkAttachmentType_T type;
13059 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13060 if ( SUCCEEDED(hrc)
13061 && type == NetworkAttachmentType_NATNetwork)
13062 {
13063 Bstr name;
13064 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13065 if (SUCCEEDED(hrc))
13066 {
13067 Utf8Str strName(name);
13068 LogRel(("VM '%s' starts using NAT network '%s'\n",
13069 mUserData->s.strName.c_str(), strName.c_str()));
13070 mPeer->lockHandle()->unlockWrite();
13071 mParent->i_natNetworkRefInc(strName);
13072#ifdef RT_LOCK_STRICT
13073 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13074#else
13075 mPeer->lockHandle()->lockWrite();
13076#endif
13077 }
13078 }
13079 }
13080 miNATNetworksStarted++;
13081 }
13082
13083 LogFlowThisFunc(("returns S_OK.\n"));
13084 return S_OK;
13085}
13086
13087/**
13088 * @note Locks this object for writing.
13089 */
13090HRESULT SessionMachine::endPowerUp(LONG aResult)
13091{
13092 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13093
13094 if (mData->mSession.mState != SessionState_Locked)
13095 return VBOX_E_INVALID_OBJECT_STATE;
13096
13097 /* Finalize the LaunchVMProcess progress object. */
13098 if (mData->mSession.mProgress)
13099 {
13100 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13101 mData->mSession.mProgress.setNull();
13102 }
13103
13104 if (SUCCEEDED((HRESULT)aResult))
13105 {
13106#ifdef VBOX_WITH_RESOURCE_USAGE_API
13107 /* The VM has been powered up successfully, so it makes sense
13108 * now to offer the performance metrics for a running machine
13109 * object. Doing it earlier wouldn't be safe. */
13110 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13111 mData->mSession.mPID);
13112#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13113 }
13114
13115 return S_OK;
13116}
13117
13118/**
13119 * @note Locks this object for writing.
13120 */
13121HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13122{
13123 LogFlowThisFuncEnter();
13124
13125 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13126
13127 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13128 E_FAIL);
13129
13130 /* create a progress object to track operation completion */
13131 ComObjPtr<Progress> pProgress;
13132 pProgress.createObject();
13133 pProgress->init(i_getVirtualBox(),
13134 static_cast<IMachine *>(this) /* aInitiator */,
13135 tr("Stopping the virtual machine"),
13136 FALSE /* aCancelable */);
13137
13138 /* fill in the console task data */
13139 mConsoleTaskData.mLastState = mData->mMachineState;
13140 mConsoleTaskData.mProgress = pProgress;
13141
13142 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13143 i_setMachineState(MachineState_Stopping);
13144
13145 pProgress.queryInterfaceTo(aProgress.asOutParam());
13146
13147 return S_OK;
13148}
13149
13150/**
13151 * @note Locks this object for writing.
13152 */
13153HRESULT SessionMachine::endPoweringDown(LONG aResult,
13154 const com::Utf8Str &aErrMsg)
13155{
13156 HRESULT const hrcResult = (HRESULT)aResult;
13157 LogFlowThisFuncEnter();
13158
13159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13160
13161 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13162 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13163 && mConsoleTaskData.mLastState != MachineState_Null,
13164 E_FAIL);
13165
13166 /*
13167 * On failure, set the state to the state we had when BeginPoweringDown()
13168 * was called (this is expected by Console::PowerDown() and the associated
13169 * task). On success the VM process already changed the state to
13170 * MachineState_PoweredOff, so no need to do anything.
13171 */
13172 if (FAILED(hrcResult))
13173 i_setMachineState(mConsoleTaskData.mLastState);
13174
13175 /* notify the progress object about operation completion */
13176 Assert(mConsoleTaskData.mProgress);
13177 if (SUCCEEDED(hrcResult))
13178 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13179 else
13180 {
13181 if (aErrMsg.length())
13182 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13183 COM_IIDOF(ISession),
13184 getComponentName(),
13185 aErrMsg.c_str());
13186 else
13187 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13188 }
13189
13190 /* clear out the temporary saved state data */
13191 mConsoleTaskData.mLastState = MachineState_Null;
13192 mConsoleTaskData.mProgress.setNull();
13193
13194 LogFlowThisFuncLeave();
13195 return S_OK;
13196}
13197
13198
13199/**
13200 * Goes through the USB filters of the given machine to see if the given
13201 * device matches any filter or not.
13202 *
13203 * @note Locks the same as USBController::hasMatchingFilter() does.
13204 */
13205HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13206 BOOL *aMatched,
13207 ULONG *aMaskedInterfaces)
13208{
13209 LogFlowThisFunc(("\n"));
13210
13211#ifdef VBOX_WITH_USB
13212 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13213#else
13214 NOREF(aDevice);
13215 NOREF(aMaskedInterfaces);
13216 *aMatched = FALSE;
13217#endif
13218
13219 return S_OK;
13220}
13221
13222/**
13223 * @note Locks the same as Host::captureUSBDevice() does.
13224 */
13225HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13226{
13227 LogFlowThisFunc(("\n"));
13228
13229#ifdef VBOX_WITH_USB
13230 /* if captureDeviceForVM() fails, it must have set extended error info */
13231 clearError();
13232 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13233 if (FAILED(rc)) return rc;
13234
13235 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13236 AssertReturn(service, E_FAIL);
13237 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13238#else
13239 RT_NOREF(aId, aCaptureFilename);
13240 return E_NOTIMPL;
13241#endif
13242}
13243
13244/**
13245 * @note Locks the same as Host::detachUSBDevice() does.
13246 */
13247HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13248 BOOL aDone)
13249{
13250 LogFlowThisFunc(("\n"));
13251
13252#ifdef VBOX_WITH_USB
13253 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13254 AssertReturn(service, E_FAIL);
13255 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13256#else
13257 NOREF(aId);
13258 NOREF(aDone);
13259 return E_NOTIMPL;
13260#endif
13261}
13262
13263/**
13264 * Inserts all machine filters to the USB proxy service and then calls
13265 * Host::autoCaptureUSBDevices().
13266 *
13267 * Called by Console from the VM process upon VM startup.
13268 *
13269 * @note Locks what called methods lock.
13270 */
13271HRESULT SessionMachine::autoCaptureUSBDevices()
13272{
13273 LogFlowThisFunc(("\n"));
13274
13275#ifdef VBOX_WITH_USB
13276 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13277 AssertComRC(rc);
13278 NOREF(rc);
13279
13280 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13281 AssertReturn(service, E_FAIL);
13282 return service->autoCaptureDevicesForVM(this);
13283#else
13284 return S_OK;
13285#endif
13286}
13287
13288/**
13289 * Removes all machine filters from the USB proxy service and then calls
13290 * Host::detachAllUSBDevices().
13291 *
13292 * Called by Console from the VM process upon normal VM termination or by
13293 * SessionMachine::uninit() upon abnormal VM termination (from under the
13294 * Machine/SessionMachine lock).
13295 *
13296 * @note Locks what called methods lock.
13297 */
13298HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13299{
13300 LogFlowThisFunc(("\n"));
13301
13302#ifdef VBOX_WITH_USB
13303 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13304 AssertComRC(rc);
13305 NOREF(rc);
13306
13307 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13308 AssertReturn(service, E_FAIL);
13309 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13310#else
13311 NOREF(aDone);
13312 return S_OK;
13313#endif
13314}
13315
13316/**
13317 * @note Locks this object for writing.
13318 */
13319HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13320 ComPtr<IProgress> &aProgress)
13321{
13322 LogFlowThisFuncEnter();
13323
13324 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13325 /*
13326 * We don't assert below because it might happen that a non-direct session
13327 * informs us it is closed right after we've been uninitialized -- it's ok.
13328 */
13329
13330 /* get IInternalSessionControl interface */
13331 ComPtr<IInternalSessionControl> control(aSession);
13332
13333 ComAssertRet(!control.isNull(), E_INVALIDARG);
13334
13335 /* Creating a Progress object requires the VirtualBox lock, and
13336 * thus locking it here is required by the lock order rules. */
13337 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13338
13339 if (control == mData->mSession.mDirectControl)
13340 {
13341 /* The direct session is being normally closed by the client process
13342 * ----------------------------------------------------------------- */
13343
13344 /* go to the closing state (essential for all open*Session() calls and
13345 * for #i_checkForDeath()) */
13346 Assert(mData->mSession.mState == SessionState_Locked);
13347 mData->mSession.mState = SessionState_Unlocking;
13348
13349 /* set direct control to NULL to release the remote instance */
13350 mData->mSession.mDirectControl.setNull();
13351 LogFlowThisFunc(("Direct control is set to NULL\n"));
13352
13353 if (mData->mSession.mProgress)
13354 {
13355 /* finalize the progress, someone might wait if a frontend
13356 * closes the session before powering on the VM. */
13357 mData->mSession.mProgress->notifyComplete(E_FAIL,
13358 COM_IIDOF(ISession),
13359 getComponentName(),
13360 tr("The VM session was closed before any attempt to power it on"));
13361 mData->mSession.mProgress.setNull();
13362 }
13363
13364 /* Create the progress object the client will use to wait until
13365 * #i_checkForDeath() is called to uninitialize this session object after
13366 * it releases the IPC semaphore.
13367 * Note! Because we're "reusing" mProgress here, this must be a proxy
13368 * object just like for LaunchVMProcess. */
13369 Assert(mData->mSession.mProgress.isNull());
13370 ComObjPtr<ProgressProxy> progress;
13371 progress.createObject();
13372 ComPtr<IUnknown> pPeer(mPeer);
13373 progress->init(mParent, pPeer,
13374 Bstr(tr("Closing session")).raw(),
13375 FALSE /* aCancelable */);
13376 progress.queryInterfaceTo(aProgress.asOutParam());
13377 mData->mSession.mProgress = progress;
13378 }
13379 else
13380 {
13381 /* the remote session is being normally closed */
13382 bool found = false;
13383 for (Data::Session::RemoteControlList::iterator
13384 it = mData->mSession.mRemoteControls.begin();
13385 it != mData->mSession.mRemoteControls.end();
13386 ++it)
13387 {
13388 if (control == *it)
13389 {
13390 found = true;
13391 // This MUST be erase(it), not remove(*it) as the latter
13392 // triggers a very nasty use after free due to the place where
13393 // the value "lives".
13394 mData->mSession.mRemoteControls.erase(it);
13395 break;
13396 }
13397 }
13398 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13399 E_INVALIDARG);
13400 }
13401
13402 /* signal the client watcher thread, because the client is going away */
13403 mParent->i_updateClientWatcher();
13404
13405 LogFlowThisFuncLeave();
13406 return S_OK;
13407}
13408
13409HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13410 std::vector<com::Utf8Str> &aValues,
13411 std::vector<LONG64> &aTimestamps,
13412 std::vector<com::Utf8Str> &aFlags)
13413{
13414 LogFlowThisFunc(("\n"));
13415
13416#ifdef VBOX_WITH_GUEST_PROPS
13417 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13418
13419 size_t cEntries = mHWData->mGuestProperties.size();
13420 aNames.resize(cEntries);
13421 aValues.resize(cEntries);
13422 aTimestamps.resize(cEntries);
13423 aFlags.resize(cEntries);
13424
13425 size_t i = 0;
13426 for (HWData::GuestPropertyMap::const_iterator
13427 it = mHWData->mGuestProperties.begin();
13428 it != mHWData->mGuestProperties.end();
13429 ++it, ++i)
13430 {
13431 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13432 aNames[i] = it->first;
13433 aValues[i] = it->second.strValue;
13434 aTimestamps[i] = it->second.mTimestamp;
13435
13436 /* If it is NULL, keep it NULL. */
13437 if (it->second.mFlags)
13438 {
13439 GuestPropWriteFlags(it->second.mFlags, szFlags);
13440 aFlags[i] = szFlags;
13441 }
13442 else
13443 aFlags[i] = "";
13444 }
13445 return S_OK;
13446#else
13447 ReturnComNotImplemented();
13448#endif
13449}
13450
13451HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13452 const com::Utf8Str &aValue,
13453 LONG64 aTimestamp,
13454 const com::Utf8Str &aFlags)
13455{
13456 LogFlowThisFunc(("\n"));
13457
13458#ifdef VBOX_WITH_GUEST_PROPS
13459 try
13460 {
13461 /*
13462 * Convert input up front.
13463 */
13464 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13465 if (aFlags.length())
13466 {
13467 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13468 AssertRCReturn(vrc, E_INVALIDARG);
13469 }
13470
13471 /*
13472 * Now grab the object lock, validate the state and do the update.
13473 */
13474
13475 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13476
13477 if (!Global::IsOnline(mData->mMachineState))
13478 {
13479 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13480 VBOX_E_INVALID_VM_STATE);
13481 }
13482
13483 i_setModified(IsModified_MachineData);
13484 mHWData.backup();
13485
13486 bool fDelete = !aValue.length();
13487 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13488 if (it != mHWData->mGuestProperties.end())
13489 {
13490 if (!fDelete)
13491 {
13492 it->second.strValue = aValue;
13493 it->second.mTimestamp = aTimestamp;
13494 it->second.mFlags = fFlags;
13495 }
13496 else
13497 mHWData->mGuestProperties.erase(it);
13498
13499 mData->mGuestPropertiesModified = TRUE;
13500 }
13501 else if (!fDelete)
13502 {
13503 HWData::GuestProperty prop;
13504 prop.strValue = aValue;
13505 prop.mTimestamp = aTimestamp;
13506 prop.mFlags = fFlags;
13507
13508 mHWData->mGuestProperties[aName] = prop;
13509 mData->mGuestPropertiesModified = TRUE;
13510 }
13511
13512 alock.release();
13513
13514 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13515 }
13516 catch (...)
13517 {
13518 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13519 }
13520 return S_OK;
13521#else
13522 ReturnComNotImplemented();
13523#endif
13524}
13525
13526
13527HRESULT SessionMachine::lockMedia()
13528{
13529 AutoMultiWriteLock2 alock(this->lockHandle(),
13530 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13531
13532 AssertReturn( mData->mMachineState == MachineState_Starting
13533 || mData->mMachineState == MachineState_Restoring
13534 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13535
13536 clearError();
13537 alock.release();
13538 return i_lockMedia();
13539}
13540
13541HRESULT SessionMachine::unlockMedia()
13542{
13543 HRESULT hrc = i_unlockMedia();
13544 return hrc;
13545}
13546
13547HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13548 ComPtr<IMediumAttachment> &aNewAttachment)
13549{
13550 // request the host lock first, since might be calling Host methods for getting host drives;
13551 // next, protect the media tree all the while we're in here, as well as our member variables
13552 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13553 this->lockHandle(),
13554 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13555
13556 IMediumAttachment *iAttach = aAttachment;
13557 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13558
13559 Utf8Str ctrlName;
13560 LONG lPort;
13561 LONG lDevice;
13562 bool fTempEject;
13563 {
13564 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13565
13566 /* Need to query the details first, as the IMediumAttachment reference
13567 * might be to the original settings, which we are going to change. */
13568 ctrlName = pAttach->i_getControllerName();
13569 lPort = pAttach->i_getPort();
13570 lDevice = pAttach->i_getDevice();
13571 fTempEject = pAttach->i_getTempEject();
13572 }
13573
13574 if (!fTempEject)
13575 {
13576 /* Remember previously mounted medium. The medium before taking the
13577 * backup is not necessarily the same thing. */
13578 ComObjPtr<Medium> oldmedium;
13579 oldmedium = pAttach->i_getMedium();
13580
13581 i_setModified(IsModified_Storage);
13582 mMediumAttachments.backup();
13583
13584 // The backup operation makes the pAttach reference point to the
13585 // old settings. Re-get the correct reference.
13586 pAttach = i_findAttachment(*mMediumAttachments.data(),
13587 ctrlName,
13588 lPort,
13589 lDevice);
13590
13591 {
13592 AutoCaller autoAttachCaller(this);
13593 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13594
13595 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13596 if (!oldmedium.isNull())
13597 oldmedium->i_removeBackReference(mData->mUuid);
13598
13599 pAttach->i_updateMedium(NULL);
13600 pAttach->i_updateEjected();
13601 }
13602
13603 i_setModified(IsModified_Storage);
13604 }
13605 else
13606 {
13607 {
13608 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13609 pAttach->i_updateEjected();
13610 }
13611 }
13612
13613 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13614
13615 return S_OK;
13616}
13617
13618HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13619 com::Utf8Str &aResult)
13620{
13621 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13622
13623 HRESULT hr = S_OK;
13624
13625 if (!mAuthLibCtx.hAuthLibrary)
13626 {
13627 /* Load the external authentication library. */
13628 Bstr authLibrary;
13629 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13630
13631 Utf8Str filename = authLibrary;
13632
13633 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13634 if (RT_FAILURE(vrc))
13635 hr = setErrorBoth(E_FAIL, vrc,
13636 tr("Could not load the external authentication library '%s' (%Rrc)"),
13637 filename.c_str(), vrc);
13638 }
13639
13640 /* The auth library might need the machine lock. */
13641 alock.release();
13642
13643 if (FAILED(hr))
13644 return hr;
13645
13646 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13647 {
13648 enum VRDEAuthParams
13649 {
13650 parmUuid = 1,
13651 parmGuestJudgement,
13652 parmUser,
13653 parmPassword,
13654 parmDomain,
13655 parmClientId
13656 };
13657
13658 AuthResult result = AuthResultAccessDenied;
13659
13660 Guid uuid(aAuthParams[parmUuid]);
13661 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13662 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13663
13664 result = AuthLibAuthenticate(&mAuthLibCtx,
13665 uuid.raw(), guestJudgement,
13666 aAuthParams[parmUser].c_str(),
13667 aAuthParams[parmPassword].c_str(),
13668 aAuthParams[parmDomain].c_str(),
13669 u32ClientId);
13670
13671 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13672 size_t cbPassword = aAuthParams[parmPassword].length();
13673 if (cbPassword)
13674 {
13675 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13676 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13677 }
13678
13679 if (result == AuthResultAccessGranted)
13680 aResult = "granted";
13681 else
13682 aResult = "denied";
13683
13684 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13685 aAuthParams[parmUser].c_str(), aResult.c_str()));
13686 }
13687 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13688 {
13689 enum VRDEAuthDisconnectParams
13690 {
13691 parmUuid = 1,
13692 parmClientId
13693 };
13694
13695 Guid uuid(aAuthParams[parmUuid]);
13696 uint32_t u32ClientId = 0;
13697 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13698 }
13699 else
13700 {
13701 hr = E_INVALIDARG;
13702 }
13703
13704 return hr;
13705}
13706
13707// public methods only for internal purposes
13708/////////////////////////////////////////////////////////////////////////////
13709
13710#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13711/**
13712 * Called from the client watcher thread to check for expected or unexpected
13713 * death of the client process that has a direct session to this machine.
13714 *
13715 * On Win32 and on OS/2, this method is called only when we've got the
13716 * mutex (i.e. the client has either died or terminated normally) so it always
13717 * returns @c true (the client is terminated, the session machine is
13718 * uninitialized).
13719 *
13720 * On other platforms, the method returns @c true if the client process has
13721 * terminated normally or abnormally and the session machine was uninitialized,
13722 * and @c false if the client process is still alive.
13723 *
13724 * @note Locks this object for writing.
13725 */
13726bool SessionMachine::i_checkForDeath()
13727{
13728 Uninit::Reason reason;
13729 bool terminated = false;
13730
13731 /* Enclose autoCaller with a block because calling uninit() from under it
13732 * will deadlock. */
13733 {
13734 AutoCaller autoCaller(this);
13735 if (!autoCaller.isOk())
13736 {
13737 /* return true if not ready, to cause the client watcher to exclude
13738 * the corresponding session from watching */
13739 LogFlowThisFunc(("Already uninitialized!\n"));
13740 return true;
13741 }
13742
13743 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13744
13745 /* Determine the reason of death: if the session state is Closing here,
13746 * everything is fine. Otherwise it means that the client did not call
13747 * OnSessionEnd() before it released the IPC semaphore. This may happen
13748 * either because the client process has abnormally terminated, or
13749 * because it simply forgot to call ISession::Close() before exiting. We
13750 * threat the latter also as an abnormal termination (see
13751 * Session::uninit() for details). */
13752 reason = mData->mSession.mState == SessionState_Unlocking ?
13753 Uninit::Normal :
13754 Uninit::Abnormal;
13755
13756 if (mClientToken)
13757 terminated = mClientToken->release();
13758 } /* AutoCaller block */
13759
13760 if (terminated)
13761 uninit(reason);
13762
13763 return terminated;
13764}
13765
13766void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13767{
13768 LogFlowThisFunc(("\n"));
13769
13770 strTokenId.setNull();
13771
13772 AutoCaller autoCaller(this);
13773 AssertComRCReturnVoid(autoCaller.rc());
13774
13775 Assert(mClientToken);
13776 if (mClientToken)
13777 mClientToken->getId(strTokenId);
13778}
13779#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13780IToken *SessionMachine::i_getToken()
13781{
13782 LogFlowThisFunc(("\n"));
13783
13784 AutoCaller autoCaller(this);
13785 AssertComRCReturn(autoCaller.rc(), NULL);
13786
13787 Assert(mClientToken);
13788 if (mClientToken)
13789 return mClientToken->getToken();
13790 else
13791 return NULL;
13792}
13793#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13794
13795Machine::ClientToken *SessionMachine::i_getClientToken()
13796{
13797 LogFlowThisFunc(("\n"));
13798
13799 AutoCaller autoCaller(this);
13800 AssertComRCReturn(autoCaller.rc(), NULL);
13801
13802 return mClientToken;
13803}
13804
13805
13806/**
13807 * @note Locks this object for reading.
13808 */
13809HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13810{
13811 LogFlowThisFunc(("\n"));
13812
13813 AutoCaller autoCaller(this);
13814 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13815
13816 ComPtr<IInternalSessionControl> directControl;
13817 {
13818 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13819 if (mData->mSession.mLockType == LockType_VM)
13820 directControl = mData->mSession.mDirectControl;
13821 }
13822
13823 /* ignore notifications sent after #OnSessionEnd() is called */
13824 if (!directControl)
13825 return S_OK;
13826
13827 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13828}
13829
13830/**
13831 * @note Locks this object for reading.
13832 */
13833HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13834 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13835 const Utf8Str &aGuestIp, LONG aGuestPort)
13836{
13837 LogFlowThisFunc(("\n"));
13838
13839 AutoCaller autoCaller(this);
13840 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13841
13842 ComPtr<IInternalSessionControl> directControl;
13843 {
13844 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13845 if (mData->mSession.mLockType == LockType_VM)
13846 directControl = mData->mSession.mDirectControl;
13847 }
13848
13849 /* ignore notifications sent after #OnSessionEnd() is called */
13850 if (!directControl)
13851 return S_OK;
13852 /*
13853 * instead acting like callback we ask IVirtualBox deliver corresponding event
13854 */
13855
13856 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13857 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13858 return S_OK;
13859}
13860
13861/**
13862 * @note Locks this object for reading.
13863 */
13864HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 AutoCaller autoCaller(this);
13869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13870
13871 ComPtr<IInternalSessionControl> directControl;
13872 {
13873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13874 if (mData->mSession.mLockType == LockType_VM)
13875 directControl = mData->mSession.mDirectControl;
13876 }
13877
13878 /* ignore notifications sent after #OnSessionEnd() is called */
13879 if (!directControl)
13880 return S_OK;
13881
13882 return directControl->OnAudioAdapterChange(audioAdapter);
13883}
13884
13885/**
13886 * @note Locks this object for reading.
13887 */
13888HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13889{
13890 LogFlowThisFunc(("\n"));
13891
13892 AutoCaller autoCaller(this);
13893 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13894
13895 ComPtr<IInternalSessionControl> directControl;
13896 {
13897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13898 if (mData->mSession.mLockType == LockType_VM)
13899 directControl = mData->mSession.mDirectControl;
13900 }
13901
13902 /* ignore notifications sent after #OnSessionEnd() is called */
13903 if (!directControl)
13904 return S_OK;
13905
13906 return directControl->OnSerialPortChange(serialPort);
13907}
13908
13909/**
13910 * @note Locks this object for reading.
13911 */
13912HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13913{
13914 LogFlowThisFunc(("\n"));
13915
13916 AutoCaller autoCaller(this);
13917 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13918
13919 ComPtr<IInternalSessionControl> directControl;
13920 {
13921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13922 if (mData->mSession.mLockType == LockType_VM)
13923 directControl = mData->mSession.mDirectControl;
13924 }
13925
13926 /* ignore notifications sent after #OnSessionEnd() is called */
13927 if (!directControl)
13928 return S_OK;
13929
13930 return directControl->OnParallelPortChange(parallelPort);
13931}
13932
13933/**
13934 * @note Locks this object for reading.
13935 */
13936HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13937{
13938 LogFlowThisFunc(("\n"));
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13942
13943 ComPtr<IInternalSessionControl> directControl;
13944 {
13945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13946 if (mData->mSession.mLockType == LockType_VM)
13947 directControl = mData->mSession.mDirectControl;
13948 }
13949
13950 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13951
13952 /* ignore notifications sent after #OnSessionEnd() is called */
13953 if (!directControl)
13954 return S_OK;
13955
13956 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13957}
13958
13959/**
13960 * @note Locks this object for reading.
13961 */
13962HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13963{
13964 LogFlowThisFunc(("\n"));
13965
13966 AutoCaller autoCaller(this);
13967 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13968
13969 ComPtr<IInternalSessionControl> directControl;
13970 {
13971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13972 if (mData->mSession.mLockType == LockType_VM)
13973 directControl = mData->mSession.mDirectControl;
13974 }
13975
13976 mParent->i_onMediumChanged(aAttachment);
13977
13978 /* ignore notifications sent after #OnSessionEnd() is called */
13979 if (!directControl)
13980 return S_OK;
13981
13982 return directControl->OnMediumChange(aAttachment, aForce);
13983}
13984
13985HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
13986{
13987 LogFlowThisFunc(("\n"));
13988
13989 AutoCaller autoCaller(this);
13990 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13991
13992 ComPtr<IInternalSessionControl> directControl;
13993 {
13994 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13995 if (mData->mSession.mLockType == LockType_VM)
13996 directControl = mData->mSession.mDirectControl;
13997 }
13998
13999 /* ignore notifications sent after #OnSessionEnd() is called */
14000 if (!directControl)
14001 return S_OK;
14002
14003 return directControl->OnVMProcessPriorityChange(aPriority);
14004}
14005
14006/**
14007 * @note Locks this object for reading.
14008 */
14009HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14010{
14011 LogFlowThisFunc(("\n"));
14012
14013 AutoCaller autoCaller(this);
14014 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14015
14016 ComPtr<IInternalSessionControl> directControl;
14017 {
14018 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14019 if (mData->mSession.mLockType == LockType_VM)
14020 directControl = mData->mSession.mDirectControl;
14021 }
14022
14023 /* ignore notifications sent after #OnSessionEnd() is called */
14024 if (!directControl)
14025 return S_OK;
14026
14027 return directControl->OnCPUChange(aCPU, aRemove);
14028}
14029
14030HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14031{
14032 LogFlowThisFunc(("\n"));
14033
14034 AutoCaller autoCaller(this);
14035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14036
14037 ComPtr<IInternalSessionControl> directControl;
14038 {
14039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14040 if (mData->mSession.mLockType == LockType_VM)
14041 directControl = mData->mSession.mDirectControl;
14042 }
14043
14044 /* ignore notifications sent after #OnSessionEnd() is called */
14045 if (!directControl)
14046 return S_OK;
14047
14048 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14049}
14050
14051/**
14052 * @note Locks this object for reading.
14053 */
14054HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14055{
14056 LogFlowThisFunc(("\n"));
14057
14058 AutoCaller autoCaller(this);
14059 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14060
14061 ComPtr<IInternalSessionControl> directControl;
14062 {
14063 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14064 if (mData->mSession.mLockType == LockType_VM)
14065 directControl = mData->mSession.mDirectControl;
14066 }
14067
14068 /* ignore notifications sent after #OnSessionEnd() is called */
14069 if (!directControl)
14070 return S_OK;
14071
14072 return directControl->OnVRDEServerChange(aRestart);
14073}
14074
14075/**
14076 * @note Locks this object for reading.
14077 */
14078HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14079{
14080 LogFlowThisFunc(("\n"));
14081
14082 AutoCaller autoCaller(this);
14083 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14084
14085 ComPtr<IInternalSessionControl> directControl;
14086 {
14087 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14088 if (mData->mSession.mLockType == LockType_VM)
14089 directControl = mData->mSession.mDirectControl;
14090 }
14091
14092 /* ignore notifications sent after #OnSessionEnd() is called */
14093 if (!directControl)
14094 return S_OK;
14095
14096 return directControl->OnRecordingChange(aEnable);
14097}
14098
14099/**
14100 * @note Locks this object for reading.
14101 */
14102HRESULT SessionMachine::i_onUSBControllerChange()
14103{
14104 LogFlowThisFunc(("\n"));
14105
14106 AutoCaller autoCaller(this);
14107 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14108
14109 ComPtr<IInternalSessionControl> directControl;
14110 {
14111 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14112 if (mData->mSession.mLockType == LockType_VM)
14113 directControl = mData->mSession.mDirectControl;
14114 }
14115
14116 /* ignore notifications sent after #OnSessionEnd() is called */
14117 if (!directControl)
14118 return S_OK;
14119
14120 return directControl->OnUSBControllerChange();
14121}
14122
14123/**
14124 * @note Locks this object for reading.
14125 */
14126HRESULT SessionMachine::i_onSharedFolderChange()
14127{
14128 LogFlowThisFunc(("\n"));
14129
14130 AutoCaller autoCaller(this);
14131 AssertComRCReturnRC(autoCaller.rc());
14132
14133 ComPtr<IInternalSessionControl> directControl;
14134 {
14135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14136 if (mData->mSession.mLockType == LockType_VM)
14137 directControl = mData->mSession.mDirectControl;
14138 }
14139
14140 /* ignore notifications sent after #OnSessionEnd() is called */
14141 if (!directControl)
14142 return S_OK;
14143
14144 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14145}
14146
14147/**
14148 * @note Locks this object for reading.
14149 */
14150HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14151{
14152 LogFlowThisFunc(("\n"));
14153
14154 AutoCaller autoCaller(this);
14155 AssertComRCReturnRC(autoCaller.rc());
14156
14157 ComPtr<IInternalSessionControl> directControl;
14158 {
14159 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14160 if (mData->mSession.mLockType == LockType_VM)
14161 directControl = mData->mSession.mDirectControl;
14162 }
14163
14164 /* ignore notifications sent after #OnSessionEnd() is called */
14165 if (!directControl)
14166 return S_OK;
14167
14168 return directControl->OnClipboardModeChange(aClipboardMode);
14169}
14170
14171/**
14172 * @note Locks this object for reading.
14173 */
14174HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14175{
14176 LogFlowThisFunc(("\n"));
14177
14178 AutoCaller autoCaller(this);
14179 AssertComRCReturnRC(autoCaller.rc());
14180
14181 ComPtr<IInternalSessionControl> directControl;
14182 {
14183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14184 if (mData->mSession.mLockType == LockType_VM)
14185 directControl = mData->mSession.mDirectControl;
14186 }
14187
14188 /* ignore notifications sent after #OnSessionEnd() is called */
14189 if (!directControl)
14190 return S_OK;
14191
14192 return directControl->OnClipboardFileTransferModeChange(aEnable);
14193}
14194
14195/**
14196 * @note Locks this object for reading.
14197 */
14198HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14199{
14200 LogFlowThisFunc(("\n"));
14201
14202 AutoCaller autoCaller(this);
14203 AssertComRCReturnRC(autoCaller.rc());
14204
14205 ComPtr<IInternalSessionControl> directControl;
14206 {
14207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14208 if (mData->mSession.mLockType == LockType_VM)
14209 directControl = mData->mSession.mDirectControl;
14210 }
14211
14212 /* ignore notifications sent after #OnSessionEnd() is called */
14213 if (!directControl)
14214 return S_OK;
14215
14216 return directControl->OnDnDModeChange(aDnDMode);
14217}
14218
14219/**
14220 * @note Locks this object for reading.
14221 */
14222HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14223{
14224 LogFlowThisFunc(("\n"));
14225
14226 AutoCaller autoCaller(this);
14227 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14228
14229 ComPtr<IInternalSessionControl> directControl;
14230 {
14231 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14232 if (mData->mSession.mLockType == LockType_VM)
14233 directControl = mData->mSession.mDirectControl;
14234 }
14235
14236 /* ignore notifications sent after #OnSessionEnd() is called */
14237 if (!directControl)
14238 return S_OK;
14239
14240 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14241}
14242
14243/**
14244 * @note Locks this object for reading.
14245 */
14246HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14247{
14248 LogFlowThisFunc(("\n"));
14249
14250 AutoCaller autoCaller(this);
14251 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14252
14253 ComPtr<IInternalSessionControl> directControl;
14254 {
14255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14256 if (mData->mSession.mLockType == LockType_VM)
14257 directControl = mData->mSession.mDirectControl;
14258 }
14259
14260 /* ignore notifications sent after #OnSessionEnd() is called */
14261 if (!directControl)
14262 return S_OK;
14263
14264 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14265}
14266
14267/**
14268 * Returns @c true if this machine's USB controller reports it has a matching
14269 * filter for the given USB device and @c false otherwise.
14270 *
14271 * @note locks this object for reading.
14272 */
14273bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14274{
14275 AutoCaller autoCaller(this);
14276 /* silently return if not ready -- this method may be called after the
14277 * direct machine session has been called */
14278 if (!autoCaller.isOk())
14279 return false;
14280
14281#ifdef VBOX_WITH_USB
14282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14283
14284 switch (mData->mMachineState)
14285 {
14286 case MachineState_Starting:
14287 case MachineState_Restoring:
14288 case MachineState_TeleportingIn:
14289 case MachineState_Paused:
14290 case MachineState_Running:
14291 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14292 * elsewhere... */
14293 alock.release();
14294 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14295 default: break;
14296 }
14297#else
14298 NOREF(aDevice);
14299 NOREF(aMaskedIfs);
14300#endif
14301 return false;
14302}
14303
14304/**
14305 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14306 */
14307HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14308 IVirtualBoxErrorInfo *aError,
14309 ULONG aMaskedIfs,
14310 const com::Utf8Str &aCaptureFilename)
14311{
14312 LogFlowThisFunc(("\n"));
14313
14314 AutoCaller autoCaller(this);
14315
14316 /* This notification may happen after the machine object has been
14317 * uninitialized (the session was closed), so don't assert. */
14318 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14319
14320 ComPtr<IInternalSessionControl> directControl;
14321 {
14322 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14323 if (mData->mSession.mLockType == LockType_VM)
14324 directControl = mData->mSession.mDirectControl;
14325 }
14326
14327 /* fail on notifications sent after #OnSessionEnd() is called, it is
14328 * expected by the caller */
14329 if (!directControl)
14330 return E_FAIL;
14331
14332 /* No locks should be held at this point. */
14333 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14334 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14335
14336 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14337}
14338
14339/**
14340 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14341 */
14342HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14343 IVirtualBoxErrorInfo *aError)
14344{
14345 LogFlowThisFunc(("\n"));
14346
14347 AutoCaller autoCaller(this);
14348
14349 /* This notification may happen after the machine object has been
14350 * uninitialized (the session was closed), so don't assert. */
14351 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14352
14353 ComPtr<IInternalSessionControl> directControl;
14354 {
14355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14356 if (mData->mSession.mLockType == LockType_VM)
14357 directControl = mData->mSession.mDirectControl;
14358 }
14359
14360 /* fail on notifications sent after #OnSessionEnd() is called, it is
14361 * expected by the caller */
14362 if (!directControl)
14363 return E_FAIL;
14364
14365 /* No locks should be held at this point. */
14366 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14367 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14368
14369 return directControl->OnUSBDeviceDetach(aId, aError);
14370}
14371
14372// protected methods
14373/////////////////////////////////////////////////////////////////////////////
14374
14375/**
14376 * Deletes the given file if it is no longer in use by either the current machine state
14377 * (if the machine is "saved") or any of the machine's snapshots.
14378 *
14379 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14380 * but is different for each SnapshotMachine. When calling this, the order of calling this
14381 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14382 * is therefore critical. I know, it's all rather messy.
14383 *
14384 * @param strStateFile
14385 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14386 * the test for whether the saved state file is in use.
14387 */
14388void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14389 Snapshot *pSnapshotToIgnore)
14390{
14391 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14392 if ( (strStateFile.isNotEmpty())
14393 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14394 )
14395 // ... and it must also not be shared with other snapshots
14396 if ( !mData->mFirstSnapshot
14397 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14398 // this checks the SnapshotMachine's state file paths
14399 )
14400 RTFileDelete(strStateFile.c_str());
14401}
14402
14403/**
14404 * Locks the attached media.
14405 *
14406 * All attached hard disks are locked for writing and DVD/floppy are locked for
14407 * reading. Parents of attached hard disks (if any) are locked for reading.
14408 *
14409 * This method also performs accessibility check of all media it locks: if some
14410 * media is inaccessible, the method will return a failure and a bunch of
14411 * extended error info objects per each inaccessible medium.
14412 *
14413 * Note that this method is atomic: if it returns a success, all media are
14414 * locked as described above; on failure no media is locked at all (all
14415 * succeeded individual locks will be undone).
14416 *
14417 * The caller is responsible for doing the necessary state sanity checks.
14418 *
14419 * The locks made by this method must be undone by calling #unlockMedia() when
14420 * no more needed.
14421 */
14422HRESULT SessionMachine::i_lockMedia()
14423{
14424 AutoCaller autoCaller(this);
14425 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14426
14427 AutoMultiWriteLock2 alock(this->lockHandle(),
14428 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14429
14430 /* bail out if trying to lock things with already set up locking */
14431 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14432
14433 MultiResult mrc(S_OK);
14434
14435 /* Collect locking information for all medium objects attached to the VM. */
14436 for (MediumAttachmentList::const_iterator
14437 it = mMediumAttachments->begin();
14438 it != mMediumAttachments->end();
14439 ++it)
14440 {
14441 MediumAttachment *pAtt = *it;
14442 DeviceType_T devType = pAtt->i_getType();
14443 Medium *pMedium = pAtt->i_getMedium();
14444
14445 MediumLockList *pMediumLockList(new MediumLockList());
14446 // There can be attachments without a medium (floppy/dvd), and thus
14447 // it's impossible to create a medium lock list. It still makes sense
14448 // to have the empty medium lock list in the map in case a medium is
14449 // attached later.
14450 if (pMedium != NULL)
14451 {
14452 MediumType_T mediumType = pMedium->i_getType();
14453 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14454 || mediumType == MediumType_Shareable;
14455 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14456
14457 alock.release();
14458 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14459 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14460 false /* fMediumLockWriteAll */,
14461 NULL,
14462 *pMediumLockList);
14463 alock.acquire();
14464 if (FAILED(mrc))
14465 {
14466 delete pMediumLockList;
14467 mData->mSession.mLockedMedia.Clear();
14468 break;
14469 }
14470 }
14471
14472 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14473 if (FAILED(rc))
14474 {
14475 mData->mSession.mLockedMedia.Clear();
14476 mrc = setError(rc,
14477 tr("Collecting locking information for all attached media failed"));
14478 break;
14479 }
14480 }
14481
14482 if (SUCCEEDED(mrc))
14483 {
14484 /* Now lock all media. If this fails, nothing is locked. */
14485 alock.release();
14486 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14487 alock.acquire();
14488 if (FAILED(rc))
14489 {
14490 mrc = setError(rc,
14491 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14492 }
14493 }
14494
14495 return mrc;
14496}
14497
14498/**
14499 * Undoes the locks made by by #lockMedia().
14500 */
14501HRESULT SessionMachine::i_unlockMedia()
14502{
14503 AutoCaller autoCaller(this);
14504 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14505
14506 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14507
14508 /* we may be holding important error info on the current thread;
14509 * preserve it */
14510 ErrorInfoKeeper eik;
14511
14512 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14513 AssertComRC(rc);
14514 return rc;
14515}
14516
14517/**
14518 * Helper to change the machine state (reimplementation).
14519 *
14520 * @note Locks this object for writing.
14521 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14522 * it can cause crashes in random places due to unexpectedly committing
14523 * the current settings. The caller is responsible for that. The call
14524 * to saveStateSettings is fine, because this method does not commit.
14525 */
14526HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14527{
14528 LogFlowThisFuncEnter();
14529 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14530
14531 AutoCaller autoCaller(this);
14532 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14533
14534 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14535
14536 MachineState_T oldMachineState = mData->mMachineState;
14537
14538 AssertMsgReturn(oldMachineState != aMachineState,
14539 ("oldMachineState=%s, aMachineState=%s\n",
14540 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14541 E_FAIL);
14542
14543 HRESULT rc = S_OK;
14544
14545 int stsFlags = 0;
14546 bool deleteSavedState = false;
14547
14548 /* detect some state transitions */
14549
14550 if ( ( oldMachineState == MachineState_Saved
14551 && aMachineState == MachineState_Restoring)
14552 || ( ( oldMachineState == MachineState_PoweredOff
14553 || oldMachineState == MachineState_Teleported
14554 || oldMachineState == MachineState_Aborted
14555 )
14556 && ( aMachineState == MachineState_TeleportingIn
14557 || aMachineState == MachineState_Starting
14558 )
14559 )
14560 )
14561 {
14562 /* The EMT thread is about to start */
14563
14564 /* Nothing to do here for now... */
14565
14566 /// @todo NEWMEDIA don't let mDVDDrive and other children
14567 /// change anything when in the Starting/Restoring state
14568 }
14569 else if ( ( oldMachineState == MachineState_Running
14570 || oldMachineState == MachineState_Paused
14571 || oldMachineState == MachineState_Teleporting
14572 || oldMachineState == MachineState_OnlineSnapshotting
14573 || oldMachineState == MachineState_LiveSnapshotting
14574 || oldMachineState == MachineState_Stuck
14575 || oldMachineState == MachineState_Starting
14576 || oldMachineState == MachineState_Stopping
14577 || oldMachineState == MachineState_Saving
14578 || oldMachineState == MachineState_Restoring
14579 || oldMachineState == MachineState_TeleportingPausedVM
14580 || oldMachineState == MachineState_TeleportingIn
14581 )
14582 && ( aMachineState == MachineState_PoweredOff
14583 || aMachineState == MachineState_Saved
14584 || aMachineState == MachineState_Teleported
14585 || aMachineState == MachineState_Aborted
14586 )
14587 )
14588 {
14589 /* The EMT thread has just stopped, unlock attached media. Note that as
14590 * opposed to locking that is done from Console, we do unlocking here
14591 * because the VM process may have aborted before having a chance to
14592 * properly unlock all media it locked. */
14593
14594 unlockMedia();
14595 }
14596
14597 if (oldMachineState == MachineState_Restoring)
14598 {
14599 if (aMachineState != MachineState_Saved)
14600 {
14601 /*
14602 * delete the saved state file once the machine has finished
14603 * restoring from it (note that Console sets the state from
14604 * Restoring to Saved if the VM couldn't restore successfully,
14605 * to give the user an ability to fix an error and retry --
14606 * we keep the saved state file in this case)
14607 */
14608 deleteSavedState = true;
14609 }
14610 }
14611 else if ( oldMachineState == MachineState_Saved
14612 && ( aMachineState == MachineState_PoweredOff
14613 || aMachineState == MachineState_Aborted
14614 || aMachineState == MachineState_Teleported
14615 )
14616 )
14617 {
14618 /*
14619 * delete the saved state after SessionMachine::ForgetSavedState() is called
14620 * or if the VM process (owning a direct VM session) crashed while the
14621 * VM was Saved
14622 */
14623
14624 /// @todo (dmik)
14625 // Not sure that deleting the saved state file just because of the
14626 // client death before it attempted to restore the VM is a good
14627 // thing. But when it crashes we need to go to the Aborted state
14628 // which cannot have the saved state file associated... The only
14629 // way to fix this is to make the Aborted condition not a VM state
14630 // but a bool flag: i.e., when a crash occurs, set it to true and
14631 // change the state to PoweredOff or Saved depending on the
14632 // saved state presence.
14633
14634 deleteSavedState = true;
14635 mData->mCurrentStateModified = TRUE;
14636 stsFlags |= SaveSTS_CurStateModified;
14637 }
14638
14639 if ( aMachineState == MachineState_Starting
14640 || aMachineState == MachineState_Restoring
14641 || aMachineState == MachineState_TeleportingIn
14642 )
14643 {
14644 /* set the current state modified flag to indicate that the current
14645 * state is no more identical to the state in the
14646 * current snapshot */
14647 if (!mData->mCurrentSnapshot.isNull())
14648 {
14649 mData->mCurrentStateModified = TRUE;
14650 stsFlags |= SaveSTS_CurStateModified;
14651 }
14652 }
14653
14654 if (deleteSavedState)
14655 {
14656 if (mRemoveSavedState)
14657 {
14658 Assert(!mSSData->strStateFilePath.isEmpty());
14659
14660 // it is safe to delete the saved state file if ...
14661 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14662 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14663 // ... none of the snapshots share the saved state file
14664 )
14665 RTFileDelete(mSSData->strStateFilePath.c_str());
14666 }
14667
14668 mSSData->strStateFilePath.setNull();
14669 stsFlags |= SaveSTS_StateFilePath;
14670 }
14671
14672 /* redirect to the underlying peer machine */
14673 mPeer->i_setMachineState(aMachineState);
14674
14675 if ( oldMachineState != MachineState_RestoringSnapshot
14676 && ( aMachineState == MachineState_PoweredOff
14677 || aMachineState == MachineState_Teleported
14678 || aMachineState == MachineState_Aborted
14679 || aMachineState == MachineState_Saved))
14680 {
14681 /* the machine has stopped execution
14682 * (or the saved state file was adopted) */
14683 stsFlags |= SaveSTS_StateTimeStamp;
14684 }
14685
14686 if ( ( oldMachineState == MachineState_PoweredOff
14687 || oldMachineState == MachineState_Aborted
14688 || oldMachineState == MachineState_Teleported
14689 )
14690 && aMachineState == MachineState_Saved)
14691 {
14692 /* the saved state file was adopted */
14693 Assert(!mSSData->strStateFilePath.isEmpty());
14694 stsFlags |= SaveSTS_StateFilePath;
14695 }
14696
14697#ifdef VBOX_WITH_GUEST_PROPS
14698 if ( aMachineState == MachineState_PoweredOff
14699 || aMachineState == MachineState_Aborted
14700 || aMachineState == MachineState_Teleported)
14701 {
14702 /* Make sure any transient guest properties get removed from the
14703 * property store on shutdown. */
14704 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14705
14706 /* remove it from the settings representation */
14707 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14708 for (settings::GuestPropertiesList::iterator
14709 it = llGuestProperties.begin();
14710 it != llGuestProperties.end();
14711 /*nothing*/)
14712 {
14713 const settings::GuestProperty &prop = *it;
14714 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14715 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14716 {
14717 it = llGuestProperties.erase(it);
14718 fNeedsSaving = true;
14719 }
14720 else
14721 {
14722 ++it;
14723 }
14724 }
14725
14726 /* Additionally remove it from the HWData representation. Required to
14727 * keep everything in sync, as this is what the API keeps using. */
14728 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14729 for (HWData::GuestPropertyMap::iterator
14730 it = llHWGuestProperties.begin();
14731 it != llHWGuestProperties.end();
14732 /*nothing*/)
14733 {
14734 uint32_t fFlags = it->second.mFlags;
14735 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14736 {
14737 /* iterator where we need to continue after the erase call
14738 * (C++03 is a fact still, and it doesn't return the iterator
14739 * which would allow continuing) */
14740 HWData::GuestPropertyMap::iterator it2 = it;
14741 ++it2;
14742 llHWGuestProperties.erase(it);
14743 it = it2;
14744 fNeedsSaving = true;
14745 }
14746 else
14747 {
14748 ++it;
14749 }
14750 }
14751
14752 if (fNeedsSaving)
14753 {
14754 mData->mCurrentStateModified = TRUE;
14755 stsFlags |= SaveSTS_CurStateModified;
14756 }
14757 }
14758#endif /* VBOX_WITH_GUEST_PROPS */
14759
14760 rc = i_saveStateSettings(stsFlags);
14761
14762 if ( ( oldMachineState != MachineState_PoweredOff
14763 && oldMachineState != MachineState_Aborted
14764 && oldMachineState != MachineState_Teleported
14765 )
14766 && ( aMachineState == MachineState_PoweredOff
14767 || aMachineState == MachineState_Aborted
14768 || aMachineState == MachineState_Teleported
14769 )
14770 )
14771 {
14772 /* we've been shut down for any reason */
14773 /* no special action so far */
14774 }
14775
14776 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14777 LogFlowThisFuncLeave();
14778 return rc;
14779}
14780
14781/**
14782 * Sends the current machine state value to the VM process.
14783 *
14784 * @note Locks this object for reading, then calls a client process.
14785 */
14786HRESULT SessionMachine::i_updateMachineStateOnClient()
14787{
14788 AutoCaller autoCaller(this);
14789 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14790
14791 ComPtr<IInternalSessionControl> directControl;
14792 {
14793 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14794 AssertReturn(!!mData, E_FAIL);
14795 if (mData->mSession.mLockType == LockType_VM)
14796 directControl = mData->mSession.mDirectControl;
14797
14798 /* directControl may be already set to NULL here in #OnSessionEnd()
14799 * called too early by the direct session process while there is still
14800 * some operation (like deleting the snapshot) in progress. The client
14801 * process in this case is waiting inside Session::close() for the
14802 * "end session" process object to complete, while #uninit() called by
14803 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14804 * operation to complete. For now, we accept this inconsistent behavior
14805 * and simply do nothing here. */
14806
14807 if (mData->mSession.mState == SessionState_Unlocking)
14808 return S_OK;
14809 }
14810
14811 /* ignore notifications sent after #OnSessionEnd() is called */
14812 if (!directControl)
14813 return S_OK;
14814
14815 return directControl->UpdateMachineState(mData->mMachineState);
14816}
14817
14818
14819/*static*/
14820HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14821{
14822 va_list args;
14823 va_start(args, pcszMsg);
14824 HRESULT rc = setErrorInternal(aResultCode,
14825 getStaticClassIID(),
14826 getStaticComponentName(),
14827 Utf8Str(pcszMsg, args),
14828 false /* aWarning */,
14829 true /* aLogIt */);
14830 va_end(args);
14831 return rc;
14832}
14833
14834
14835HRESULT Machine::updateState(MachineState_T aState)
14836{
14837 NOREF(aState);
14838 ReturnComNotImplemented();
14839}
14840
14841HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14842{
14843 NOREF(aProgress);
14844 ReturnComNotImplemented();
14845}
14846
14847HRESULT Machine::endPowerUp(LONG aResult)
14848{
14849 NOREF(aResult);
14850 ReturnComNotImplemented();
14851}
14852
14853HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14854{
14855 NOREF(aProgress);
14856 ReturnComNotImplemented();
14857}
14858
14859HRESULT Machine::endPoweringDown(LONG aResult,
14860 const com::Utf8Str &aErrMsg)
14861{
14862 NOREF(aResult);
14863 NOREF(aErrMsg);
14864 ReturnComNotImplemented();
14865}
14866
14867HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14868 BOOL *aMatched,
14869 ULONG *aMaskedInterfaces)
14870{
14871 NOREF(aDevice);
14872 NOREF(aMatched);
14873 NOREF(aMaskedInterfaces);
14874 ReturnComNotImplemented();
14875
14876}
14877
14878HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14879{
14880 NOREF(aId); NOREF(aCaptureFilename);
14881 ReturnComNotImplemented();
14882}
14883
14884HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14885 BOOL aDone)
14886{
14887 NOREF(aId);
14888 NOREF(aDone);
14889 ReturnComNotImplemented();
14890}
14891
14892HRESULT Machine::autoCaptureUSBDevices()
14893{
14894 ReturnComNotImplemented();
14895}
14896
14897HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14898{
14899 NOREF(aDone);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14904 ComPtr<IProgress> &aProgress)
14905{
14906 NOREF(aSession);
14907 NOREF(aProgress);
14908 ReturnComNotImplemented();
14909}
14910
14911HRESULT Machine::finishOnlineMergeMedium()
14912{
14913 ReturnComNotImplemented();
14914}
14915
14916HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14917 std::vector<com::Utf8Str> &aValues,
14918 std::vector<LONG64> &aTimestamps,
14919 std::vector<com::Utf8Str> &aFlags)
14920{
14921 NOREF(aNames);
14922 NOREF(aValues);
14923 NOREF(aTimestamps);
14924 NOREF(aFlags);
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14929 const com::Utf8Str &aValue,
14930 LONG64 aTimestamp,
14931 const com::Utf8Str &aFlags)
14932{
14933 NOREF(aName);
14934 NOREF(aValue);
14935 NOREF(aTimestamp);
14936 NOREF(aFlags);
14937 ReturnComNotImplemented();
14938}
14939
14940HRESULT Machine::lockMedia()
14941{
14942 ReturnComNotImplemented();
14943}
14944
14945HRESULT Machine::unlockMedia()
14946{
14947 ReturnComNotImplemented();
14948}
14949
14950HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14951 ComPtr<IMediumAttachment> &aNewAttachment)
14952{
14953 NOREF(aAttachment);
14954 NOREF(aNewAttachment);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14959 ULONG aCpuUser,
14960 ULONG aCpuKernel,
14961 ULONG aCpuIdle,
14962 ULONG aMemTotal,
14963 ULONG aMemFree,
14964 ULONG aMemBalloon,
14965 ULONG aMemShared,
14966 ULONG aMemCache,
14967 ULONG aPagedTotal,
14968 ULONG aMemAllocTotal,
14969 ULONG aMemFreeTotal,
14970 ULONG aMemBalloonTotal,
14971 ULONG aMemSharedTotal,
14972 ULONG aVmNetRx,
14973 ULONG aVmNetTx)
14974{
14975 NOREF(aValidStats);
14976 NOREF(aCpuUser);
14977 NOREF(aCpuKernel);
14978 NOREF(aCpuIdle);
14979 NOREF(aMemTotal);
14980 NOREF(aMemFree);
14981 NOREF(aMemBalloon);
14982 NOREF(aMemShared);
14983 NOREF(aMemCache);
14984 NOREF(aPagedTotal);
14985 NOREF(aMemAllocTotal);
14986 NOREF(aMemFreeTotal);
14987 NOREF(aMemBalloonTotal);
14988 NOREF(aMemSharedTotal);
14989 NOREF(aVmNetRx);
14990 NOREF(aVmNetTx);
14991 ReturnComNotImplemented();
14992}
14993
14994HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
14995 com::Utf8Str &aResult)
14996{
14997 NOREF(aAuthParams);
14998 NOREF(aResult);
14999 ReturnComNotImplemented();
15000}
15001
15002com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15003{
15004 com::Utf8Str strControllerName = "Unknown";
15005 switch (aBusType)
15006 {
15007 case StorageBus_IDE:
15008 {
15009 strControllerName = "IDE";
15010 break;
15011 }
15012 case StorageBus_SATA:
15013 {
15014 strControllerName = "SATA";
15015 break;
15016 }
15017 case StorageBus_SCSI:
15018 {
15019 strControllerName = "SCSI";
15020 break;
15021 }
15022 case StorageBus_Floppy:
15023 {
15024 strControllerName = "Floppy";
15025 break;
15026 }
15027 case StorageBus_SAS:
15028 {
15029 strControllerName = "SAS";
15030 break;
15031 }
15032 case StorageBus_USB:
15033 {
15034 strControllerName = "USB";
15035 break;
15036 }
15037 default:
15038 break;
15039 }
15040 return strControllerName;
15041}
15042
15043HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15044{
15045 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15046
15047 AutoCaller autoCaller(this);
15048 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15049
15050 HRESULT rc = S_OK;
15051
15052 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15053 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15054 rc = getUSBDeviceFilters(usbDeviceFilters);
15055 if (FAILED(rc)) return rc;
15056
15057 NOREF(aFlags);
15058 com::Utf8Str osTypeId;
15059 ComObjPtr<GuestOSType> osType = NULL;
15060
15061 /* Get the guest os type as a string from the VB. */
15062 rc = getOSTypeId(osTypeId);
15063 if (FAILED(rc)) return rc;
15064
15065 /* Get the os type obj that coresponds, can be used to get
15066 * the defaults for this guest OS. */
15067 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15068 if (FAILED(rc)) return rc;
15069
15070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15071
15072 /* Let the OS type select 64-bit ness. */
15073 mHWData->mLongMode = osType->i_is64Bit()
15074 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15075
15076 /* Let the OS type enable the X2APIC */
15077 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15078
15079 /* This one covers IOAPICEnabled. */
15080 mBIOSSettings->i_applyDefaults(osType);
15081
15082 /* Initialize default record settings. */
15083 mRecordingSettings->i_applyDefaults();
15084
15085 /* Initialize default BIOS settings here */
15086 /* Hardware virtualization must be ON by default */
15087 mHWData->mAPIC = true;
15088 mHWData->mHWVirtExEnabled = true;
15089
15090 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15091 if (FAILED(rc)) return rc;
15092
15093 /* Graphics stuff. */
15094 GraphicsControllerType_T graphicsController;
15095 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15096 if (FAILED(rc)) return rc;
15097
15098 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15099 if (FAILED(rc)) return rc;
15100
15101 ULONG vramSize;
15102 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15103 if (FAILED(rc)) return rc;
15104
15105 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15106 if (FAILED(rc)) return rc;
15107
15108 BOOL fAccelerate2DVideoEnabled;
15109 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15110 if (FAILED(rc)) return rc;
15111
15112 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15113 if (FAILED(rc)) return rc;
15114
15115 BOOL fAccelerate3DEnabled;
15116 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15117 if (FAILED(rc)) return rc;
15118
15119 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15120 if (FAILED(rc)) return rc;
15121
15122 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15123 if (FAILED(rc)) return rc;
15124
15125 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15126 if (FAILED(rc)) return rc;
15127
15128 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15129 if (FAILED(rc)) return rc;
15130
15131 BOOL mRTCUseUTC;
15132 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15133 if (FAILED(rc)) return rc;
15134
15135 setRTCUseUTC(mRTCUseUTC);
15136 if (FAILED(rc)) return rc;
15137
15138 /* the setter does more than just the assignment, so use it */
15139 ChipsetType_T enmChipsetType;
15140 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15141 if (FAILED(rc)) return rc;
15142
15143 rc = COMSETTER(ChipsetType)(enmChipsetType);
15144 if (FAILED(rc)) return rc;
15145
15146 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15147 if (FAILED(rc)) return rc;
15148
15149 /* Apply IOMMU defaults. */
15150 IommuType_T enmIommuType;
15151 rc = osType->COMGETTER(RecommendedIommuType)(&enmIommuType);
15152 if (FAILED(rc)) return rc;
15153
15154 rc = COMSETTER(IommuType)(enmIommuType);
15155 if (FAILED(rc)) return rc;
15156
15157 /* Apply network adapters defaults */
15158 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15159 mNetworkAdapters[slot]->i_applyDefaults(osType);
15160
15161 /* Apply serial port defaults */
15162 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15163 mSerialPorts[slot]->i_applyDefaults(osType);
15164
15165 /* Apply parallel port defaults - not OS dependent*/
15166 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15167 mParallelPorts[slot]->i_applyDefaults();
15168
15169 /* Audio stuff. */
15170 AudioControllerType_T audioController;
15171 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15172 if (FAILED(rc)) return rc;
15173
15174 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15175 if (FAILED(rc)) return rc;
15176
15177 AudioCodecType_T audioCodec;
15178 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15179 if (FAILED(rc)) return rc;
15180
15181 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15182 if (FAILED(rc)) return rc;
15183
15184 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15188 if (FAILED(rc)) return rc;
15189
15190 /* Storage Controllers */
15191 StorageControllerType_T hdStorageControllerType;
15192 StorageBus_T hdStorageBusType;
15193 StorageControllerType_T dvdStorageControllerType;
15194 StorageBus_T dvdStorageBusType;
15195 BOOL recommendedFloppy;
15196 ComPtr<IStorageController> floppyController;
15197 ComPtr<IStorageController> hdController;
15198 ComPtr<IStorageController> dvdController;
15199 Utf8Str strFloppyName, strDVDName, strHDName;
15200
15201 /* GUI auto generates controller names using bus type. Do the same*/
15202 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15203
15204 /* Floppy recommended? add one. */
15205 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15206 if (FAILED(rc)) return rc;
15207 if (recommendedFloppy)
15208 {
15209 rc = addStorageController(strFloppyName,
15210 StorageBus_Floppy,
15211 floppyController);
15212 if (FAILED(rc)) return rc;
15213 }
15214
15215 /* Setup one DVD storage controller. */
15216 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15217 if (FAILED(rc)) return rc;
15218
15219 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15220 if (FAILED(rc)) return rc;
15221
15222 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15223
15224 rc = addStorageController(strDVDName,
15225 dvdStorageBusType,
15226 dvdController);
15227 if (FAILED(rc)) return rc;
15228
15229 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15230 if (FAILED(rc)) return rc;
15231
15232 /* Setup one HDD storage controller. */
15233 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15237 if (FAILED(rc)) return rc;
15238
15239 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15240
15241 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15242 {
15243 rc = addStorageController(strHDName,
15244 hdStorageBusType,
15245 hdController);
15246 if (FAILED(rc)) return rc;
15247
15248 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15249 if (FAILED(rc)) return rc;
15250 }
15251 else
15252 {
15253 /* The HD controller is the same as DVD: */
15254 hdController = dvdController;
15255 }
15256
15257 /* Limit the AHCI port count if it's used because windows has trouble with
15258 * too many ports and other guest (OS X in particular) may take extra long
15259 * boot: */
15260
15261 // pParent = static_cast<Medium*>(aP)
15262 IStorageController *temp = hdController;
15263 ComObjPtr<StorageController> storageController;
15264 storageController = static_cast<StorageController *>(temp);
15265
15266 // tempHDController = aHDController;
15267 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15268 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15269 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15270 storageController->COMSETTER(PortCount)(1);
15271
15272 /* USB stuff */
15273
15274 bool ohciEnabled = false;
15275
15276 ComPtr<IUSBController> usbController;
15277 BOOL recommendedUSB3;
15278 BOOL recommendedUSB;
15279 BOOL usbProxyAvailable;
15280
15281 getUSBProxyAvailable(&usbProxyAvailable);
15282 if (FAILED(rc)) return rc;
15283
15284 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15285 if (FAILED(rc)) return rc;
15286 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15287 if (FAILED(rc)) return rc;
15288
15289 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15290 {
15291#ifdef VBOX_WITH_EXTPACK
15292 /* USB 3.0 is only available if the proper ExtPack is installed. */
15293 ExtPackManager *aManager = mParent->i_getExtPackManager();
15294 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15295 {
15296 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15297 if (FAILED(rc)) return rc;
15298
15299 /* xHci includes OHCI */
15300 ohciEnabled = true;
15301 }
15302#endif
15303 }
15304 if ( !ohciEnabled
15305 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15306 {
15307 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15308 if (FAILED(rc)) return rc;
15309 ohciEnabled = true;
15310
15311#ifdef VBOX_WITH_EXTPACK
15312 /* USB 2.0 is only available if the proper ExtPack is installed.
15313 * Note. Configuring EHCI here and providing messages about
15314 * the missing extpack isn't exactly clean, but it is a
15315 * necessary evil to patch over legacy compatability issues
15316 * introduced by the new distribution model. */
15317 ExtPackManager *manager = mParent->i_getExtPackManager();
15318 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15319 {
15320 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15321 if (FAILED(rc)) return rc;
15322 }
15323#endif
15324 }
15325
15326 /* Set recommended human interface device types: */
15327 BOOL recommendedUSBHID;
15328 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15329 if (FAILED(rc)) return rc;
15330
15331 if (recommendedUSBHID)
15332 {
15333 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15334 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15335 if (!ohciEnabled && !usbDeviceFilters.isNull())
15336 {
15337 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15338 if (FAILED(rc)) return rc;
15339 }
15340 }
15341
15342 BOOL recommendedUSBTablet;
15343 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15344 if (FAILED(rc)) return rc;
15345
15346 if (recommendedUSBTablet)
15347 {
15348 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15349 if (!ohciEnabled && !usbDeviceFilters.isNull())
15350 {
15351 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15352 if (FAILED(rc)) return rc;
15353 }
15354 }
15355 return S_OK;
15356}
15357
15358/* This isn't handled entirely by the wrapper generator yet. */
15359#ifdef VBOX_WITH_XPCOM
15360NS_DECL_CLASSINFO(SessionMachine)
15361NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15362
15363NS_DECL_CLASSINFO(SnapshotMachine)
15364NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15365#endif
Note: See TracBrowser for help on using the repository browser.

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