VirtualBox

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

Last change on this file since 80612 was 80586, checked in by vboxsync, 6 years ago

Runtime: Windows build fix for r133105

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 535.4 KB
Line 
1/* $Id: MachineImpl.cpp 80586 2019-09-04 14:14:03Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#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#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#ifdef VBOX_WITH_DTRACE_R3_MAIN
95# include "dtrace/VBoxAPI.h"
96#endif
97
98#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
99# define HOSTSUFF_EXE ".exe"
100#else /* !RT_OS_WINDOWS */
101# define HOSTSUFF_EXE ""
102#endif /* !RT_OS_WINDOWS */
103
104// defines / prototypes
105/////////////////////////////////////////////////////////////////////////////
106
107/////////////////////////////////////////////////////////////////////////////
108// Machine::Data structure
109/////////////////////////////////////////////////////////////////////////////
110
111Machine::Data::Data()
112{
113 mRegistered = FALSE;
114 pMachineConfigFile = NULL;
115 /* Contains hints on what has changed when the user is using the VM (config
116 * changes, running the VM, ...). This is used to decide if a config needs
117 * to be written to disk. */
118 flModifications = 0;
119 /* VM modification usually also trigger setting the current state to
120 * "Modified". Although this is not always the case. An e.g. is the VM
121 * initialization phase or when snapshot related data is changed. The
122 * actually behavior is controlled by the following flag. */
123 m_fAllowStateModification = false;
124 mAccessible = FALSE;
125 /* mUuid is initialized in Machine::init() */
126
127 mMachineState = MachineState_PoweredOff;
128 RTTimeNow(&mLastStateChange);
129
130 mMachineStateDeps = 0;
131 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
132 mMachineStateChangePending = 0;
133
134 mCurrentStateModified = TRUE;
135 mGuestPropertiesModified = FALSE;
136
137 mSession.mPID = NIL_RTPROCESS;
138 mSession.mLockType = LockType_Null;
139 mSession.mState = SessionState_Unlocked;
140}
141
142Machine::Data::~Data()
143{
144 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
145 {
146 RTSemEventMultiDestroy(mMachineStateDepsSem);
147 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
148 }
149 if (pMachineConfigFile)
150 {
151 delete pMachineConfigFile;
152 pMachineConfigFile = NULL;
153 }
154}
155
156/////////////////////////////////////////////////////////////////////////////
157// Machine::HWData structure
158/////////////////////////////////////////////////////////////////////////////
159
160Machine::HWData::HWData()
161{
162 /* default values for a newly created machine */
163 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
164 mMemorySize = 128;
165 mCPUCount = 1;
166 mCPUHotPlugEnabled = false;
167 mMemoryBalloonSize = 0;
168 mPageFusionEnabled = false;
169 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
170 mVRAMSize = 8;
171 mAccelerate3DEnabled = false;
172 mAccelerate2DVideoEnabled = false;
173 mMonitorCount = 1;
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185 mHWVirtExUseNativeApi = false;
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 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param strOsType OS Type string (stored as is if aOsType is NULL).
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
285 * scheme (includes the UUID).
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 const Utf8Str &strOsType,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 if (llGroups.size())
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Let the OS type select 64-bit ness. */
348 mHWData->mLongMode = aOsType->i_is64Bit()
349 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
350
351 /* Let the OS type enable the X2APIC */
352 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
353 }
354 else if (!strOsType.isEmpty())
355 {
356 /* Store OS type */
357 mUserData->s.strOsType = strOsType;
358
359 /* No guest OS type object. Pick some plausible defaults which the
360 * host can handle. There's no way to know or validate anything. */
361 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 mHWData->mX2APIC = false;
363 }
364
365 /* Apply BIOS defaults. */
366 mBIOSSettings->i_applyDefaults(aOsType);
367
368 /* Apply record defaults. */
369 mRecordingSettings->i_applyDefaults();
370
371 /* Apply network adapters defaults */
372 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
373 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
374
375 /* Apply serial port defaults */
376 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
377 mSerialPorts[slot]->i_applyDefaults(aOsType);
378
379 /* Apply parallel port defaults */
380 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
381 mParallelPorts[slot]->i_applyDefaults();
382
383 /* At this point the changing of the current state modification
384 * flag is allowed. */
385 i_allowStateModification();
386
387 /* commit all changes made during the initialization */
388 i_commit();
389 }
390
391 /* Confirm a successful initialization when it's the case */
392 if (SUCCEEDED(rc))
393 {
394 if (mData->mAccessible)
395 autoInitSpan.setSucceeded();
396 else
397 autoInitSpan.setLimited();
398 }
399
400 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
401 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
402 mData->mRegistered,
403 mData->mAccessible,
404 rc));
405
406 LogFlowThisFuncLeave();
407
408 return rc;
409}
410
411/**
412 * Initializes a new instance with data from machine XML (formerly Init_Registered).
413 * Gets called in two modes:
414 *
415 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
416 * UUID is specified and we mark the machine as "registered";
417 *
418 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
419 * and the machine remains unregistered until RegisterMachine() is called.
420 *
421 * @param aParent Associated parent object
422 * @param strConfigFile Local file system path to the VM settings file (can
423 * be relative to the VirtualBox config directory).
424 * @param aId UUID of the machine or NULL (see above).
425 *
426 * @return Success indicator. if not S_OK, the machine object is invalid
427 */
428HRESULT Machine::initFromSettings(VirtualBox *aParent,
429 const Utf8Str &strConfigFile,
430 const Guid *aId)
431{
432 LogFlowThisFuncEnter();
433 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
434
435 /* Enclose the state transition NotReady->InInit->Ready */
436 AutoInitSpan autoInitSpan(this);
437 AssertReturn(autoInitSpan.isOk(), E_FAIL);
438
439 HRESULT rc = initImpl(aParent, strConfigFile);
440 if (FAILED(rc)) return rc;
441
442 if (aId)
443 {
444 // loading a registered VM:
445 unconst(mData->mUuid) = *aId;
446 mData->mRegistered = TRUE;
447 // now load the settings from XML:
448 rc = i_registeredInit();
449 // this calls initDataAndChildObjects() and loadSettings()
450 }
451 else
452 {
453 // opening an unregistered VM (VirtualBox::OpenMachine()):
454 rc = initDataAndChildObjects();
455
456 if (SUCCEEDED(rc))
457 {
458 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
459 mData->mAccessible = TRUE;
460
461 try
462 {
463 // load and parse machine XML; this will throw on XML or logic errors
464 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
465
466 // reject VM UUID duplicates, they can happen if someone
467 // tries to register an already known VM config again
468 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
469 true /* fPermitInaccessible */,
470 false /* aDoSetError */,
471 NULL) != VBOX_E_OBJECT_NOT_FOUND)
472 {
473 throw setError(E_FAIL,
474 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
475 mData->m_strConfigFile.c_str());
476 }
477
478 // use UUID from machine config
479 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
480
481 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
482 NULL /* puuidRegistry */);
483 if (FAILED(rc)) throw rc;
484
485 /* At this point the changing of the current state modification
486 * flag is allowed. */
487 i_allowStateModification();
488
489 i_commit();
490 }
491 catch (HRESULT err)
492 {
493 /* we assume that error info is set by the thrower */
494 rc = err;
495 }
496 catch (...)
497 {
498 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
499 }
500 }
501 }
502
503 /* Confirm a successful initialization when it's the case */
504 if (SUCCEEDED(rc))
505 {
506 if (mData->mAccessible)
507 autoInitSpan.setSucceeded();
508 else
509 {
510 autoInitSpan.setLimited();
511
512 // uninit media from this machine's media registry, or else
513 // reloading the settings will fail
514 mParent->i_unregisterMachineMedia(i_getId());
515 }
516 }
517
518 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
519 "rc=%08X\n",
520 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
521 mData->mRegistered, mData->mAccessible, rc));
522
523 LogFlowThisFuncLeave();
524
525 return rc;
526}
527
528/**
529 * Initializes a new instance from a machine config that is already in memory
530 * (import OVF case). Since we are importing, the UUID in the machine
531 * config is ignored and we always generate a fresh one.
532 *
533 * @param aParent Associated parent object.
534 * @param strName Name for the new machine; this overrides what is specified in config.
535 * @param strSettingsFilename File name of .vbox file.
536 * @param config Machine configuration loaded and parsed from XML.
537 *
538 * @return Success indicator. if not S_OK, the machine object is invalid
539 */
540HRESULT Machine::init(VirtualBox *aParent,
541 const Utf8Str &strName,
542 const Utf8Str &strSettingsFilename,
543 const settings::MachineConfigFile &config)
544{
545 LogFlowThisFuncEnter();
546
547 /* Enclose the state transition NotReady->InInit->Ready */
548 AutoInitSpan autoInitSpan(this);
549 AssertReturn(autoInitSpan.isOk(), E_FAIL);
550
551 HRESULT rc = initImpl(aParent, strSettingsFilename);
552 if (FAILED(rc)) return rc;
553
554 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
555 if (FAILED(rc)) return rc;
556
557 rc = initDataAndChildObjects();
558
559 if (SUCCEEDED(rc))
560 {
561 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
562 mData->mAccessible = TRUE;
563
564 // create empty machine config for instance data
565 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
566
567 // generate fresh UUID, ignore machine config
568 unconst(mData->mUuid).create();
569
570 rc = i_loadMachineDataFromSettings(config,
571 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
572
573 // override VM name as well, it may be different
574 mUserData->s.strName = strName;
575
576 if (SUCCEEDED(rc))
577 {
578 /* At this point the changing of the current state modification
579 * flag is allowed. */
580 i_allowStateModification();
581
582 /* commit all changes made during the initialization */
583 i_commit();
584 }
585 }
586
587 /* Confirm a successful initialization when it's the case */
588 if (SUCCEEDED(rc))
589 {
590 if (mData->mAccessible)
591 autoInitSpan.setSucceeded();
592 else
593 {
594 /* Ignore all errors from unregistering, they would destroy
595- * the more interesting error information we already have,
596- * pinpointing the issue with the VM config. */
597 ErrorInfoKeeper eik;
598
599 autoInitSpan.setLimited();
600
601 // uninit media from this machine's media registry, or else
602 // reloading the settings will fail
603 mParent->i_unregisterMachineMedia(i_getId());
604 }
605 }
606
607 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
608 "rc=%08X\n",
609 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
610 mData->mRegistered, mData->mAccessible, rc));
611
612 LogFlowThisFuncLeave();
613
614 return rc;
615}
616
617/**
618 * Shared code between the various init() implementations.
619 * @param aParent The VirtualBox object.
620 * @param strConfigFile Settings file.
621 * @return
622 */
623HRESULT Machine::initImpl(VirtualBox *aParent,
624 const Utf8Str &strConfigFile)
625{
626 LogFlowThisFuncEnter();
627
628 AssertReturn(aParent, E_INVALIDARG);
629 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
630
631 HRESULT rc = S_OK;
632
633 /* share the parent weakly */
634 unconst(mParent) = aParent;
635
636 /* allocate the essential machine data structure (the rest will be
637 * allocated later by initDataAndChildObjects() */
638 mData.allocate();
639
640 /* memorize the config file name (as provided) */
641 mData->m_strConfigFile = strConfigFile;
642
643 /* get the full file name */
644 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
645 if (RT_FAILURE(vrc1))
646 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
647 tr("Invalid machine settings file name '%s' (%Rrc)"),
648 strConfigFile.c_str(),
649 vrc1);
650
651 LogFlowThisFuncLeave();
652
653 return rc;
654}
655
656/**
657 * Tries to create a machine settings file in the path stored in the machine
658 * instance data. Used when a new machine is created to fail gracefully if
659 * the settings file could not be written (e.g. because machine dir is read-only).
660 * @return
661 */
662HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
663{
664 HRESULT rc = S_OK;
665
666 // when we create a new machine, we must be able to create the settings file
667 RTFILE f = NIL_RTFILE;
668 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
669 if ( RT_SUCCESS(vrc)
670 || vrc == VERR_SHARING_VIOLATION
671 )
672 {
673 if (RT_SUCCESS(vrc))
674 RTFileClose(f);
675 if (!fForceOverwrite)
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Machine settings file '%s' already exists"),
678 mData->m_strConfigFileFull.c_str());
679 else
680 {
681 /* try to delete the config file, as otherwise the creation
682 * of a new settings file will fail. */
683 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
684 if (RT_FAILURE(vrc2))
685 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
686 tr("Could not delete the existing settings file '%s' (%Rrc)"),
687 mData->m_strConfigFileFull.c_str(), vrc2);
688 }
689 }
690 else if ( vrc != VERR_FILE_NOT_FOUND
691 && vrc != VERR_PATH_NOT_FOUND
692 )
693 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
694 tr("Invalid machine settings file name '%s' (%Rrc)"),
695 mData->m_strConfigFileFull.c_str(),
696 vrc);
697 return rc;
698}
699
700/**
701 * Initializes the registered machine by loading the settings file.
702 * This method is separated from #init() in order to make it possible to
703 * retry the operation after VirtualBox startup instead of refusing to
704 * startup the whole VirtualBox server in case if the settings file of some
705 * registered VM is invalid or inaccessible.
706 *
707 * @note Must be always called from this object's write lock
708 * (unless called from #init() that doesn't need any locking).
709 * @note Locks the mUSBController method for writing.
710 * @note Subclasses must not call this method.
711 */
712HRESULT Machine::i_registeredInit()
713{
714 AssertReturn(!i_isSessionMachine(), E_FAIL);
715 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
716 AssertReturn(mData->mUuid.isValid(), E_FAIL);
717 AssertReturn(!mData->mAccessible, E_FAIL);
718
719 HRESULT rc = initDataAndChildObjects();
720
721 if (SUCCEEDED(rc))
722 {
723 /* Temporarily reset the registered flag in order to let setters
724 * potentially called from loadSettings() succeed (isMutable() used in
725 * all setters will return FALSE for a Machine instance if mRegistered
726 * is TRUE). */
727 mData->mRegistered = FALSE;
728
729 try
730 {
731 // load and parse machine XML; this will throw on XML or logic errors
732 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
733
734 if (mData->mUuid != mData->pMachineConfigFile->uuid)
735 throw setError(E_FAIL,
736 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
737 mData->pMachineConfigFile->uuid.raw(),
738 mData->m_strConfigFileFull.c_str(),
739 mData->mUuid.toString().c_str(),
740 mParent->i_settingsFilePath().c_str());
741
742 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
743 NULL /* const Guid *puuidRegistry */);
744 if (FAILED(rc)) throw rc;
745 }
746 catch (HRESULT err)
747 {
748 /* we assume that error info is set by the thrower */
749 rc = err;
750 }
751 catch (...)
752 {
753 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
754 }
755
756 /* Restore the registered flag (even on failure) */
757 mData->mRegistered = TRUE;
758 }
759
760 if (SUCCEEDED(rc))
761 {
762 /* Set mAccessible to TRUE only if we successfully locked and loaded
763 * the settings file */
764 mData->mAccessible = TRUE;
765
766 /* commit all changes made during loading the settings file */
767 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
768 /// @todo r=klaus for some reason the settings loading logic backs up
769 // the settings, and therefore a commit is needed. Should probably be changed.
770 }
771 else
772 {
773 /* If the machine is registered, then, instead of returning a
774 * failure, we mark it as inaccessible and set the result to
775 * success to give it a try later */
776
777 /* fetch the current error info */
778 mData->mAccessError = com::ErrorInfo();
779 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
780
781 /* rollback all changes */
782 i_rollback(false /* aNotify */);
783
784 // uninit media from this machine's media registry, or else
785 // reloading the settings will fail
786 mParent->i_unregisterMachineMedia(i_getId());
787
788 /* uninitialize the common part to make sure all data is reset to
789 * default (null) values */
790 uninitDataAndChildObjects();
791
792 rc = S_OK;
793 }
794
795 return rc;
796}
797
798/**
799 * Uninitializes the instance.
800 * Called either from FinalRelease() or by the parent when it gets destroyed.
801 *
802 * @note The caller of this method must make sure that this object
803 * a) doesn't have active callers on the current thread and b) is not locked
804 * by the current thread; otherwise uninit() will hang either a) due to
805 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
806 * a dead-lock caused by this thread waiting for all callers on the other
807 * threads are done but preventing them from doing so by holding a lock.
808 */
809void Machine::uninit()
810{
811 LogFlowThisFuncEnter();
812
813 Assert(!isWriteLockOnCurrentThread());
814
815 Assert(!uRegistryNeedsSaving);
816 if (uRegistryNeedsSaving)
817 {
818 AutoCaller autoCaller(this);
819 if (SUCCEEDED(autoCaller.rc()))
820 {
821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
822 i_saveSettings(NULL, Machine::SaveS_Force);
823 }
824 }
825
826 /* Enclose the state transition Ready->InUninit->NotReady */
827 AutoUninitSpan autoUninitSpan(this);
828 if (autoUninitSpan.uninitDone())
829 return;
830
831 Assert(!i_isSnapshotMachine());
832 Assert(!i_isSessionMachine());
833 Assert(!!mData);
834
835 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
836 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
837
838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
839
840 if (!mData->mSession.mMachine.isNull())
841 {
842 /* Theoretically, this can only happen if the VirtualBox server has been
843 * terminated while there were clients running that owned open direct
844 * sessions. Since in this case we are definitely called by
845 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
846 * won't happen on the client watcher thread (because it has a
847 * VirtualBox caller for the duration of the
848 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
849 * cannot happen until the VirtualBox caller is released). This is
850 * important, because SessionMachine::uninit() cannot correctly operate
851 * after we return from this method (it expects the Machine instance is
852 * still valid). We'll call it ourselves below.
853 */
854 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
855 (SessionMachine*)mData->mSession.mMachine));
856
857 if (Global::IsOnlineOrTransient(mData->mMachineState))
858 {
859 Log1WarningThisFunc(("Setting state to Aborted!\n"));
860 /* set machine state using SessionMachine reimplementation */
861 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
862 }
863
864 /*
865 * Uninitialize SessionMachine using public uninit() to indicate
866 * an unexpected uninitialization.
867 */
868 mData->mSession.mMachine->uninit();
869 /* SessionMachine::uninit() must set mSession.mMachine to null */
870 Assert(mData->mSession.mMachine.isNull());
871 }
872
873 // uninit media from this machine's media registry, if they're still there
874 Guid uuidMachine(i_getId());
875
876 /* the lock is no more necessary (SessionMachine is uninitialized) */
877 alock.release();
878
879 /* XXX This will fail with
880 * "cannot be closed because it is still attached to 1 virtual machines"
881 * because at this point we did not call uninitDataAndChildObjects() yet
882 * and therefore also removeBackReference() for all these mediums was not called! */
883
884 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
885 mParent->i_unregisterMachineMedia(uuidMachine);
886
887 // has machine been modified?
888 if (mData->flModifications)
889 {
890 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
891 i_rollback(false /* aNotify */);
892 }
893
894 if (mData->mAccessible)
895 uninitDataAndChildObjects();
896
897 /* free the essential data structure last */
898 mData.free();
899
900 LogFlowThisFuncLeave();
901}
902
903// Wrapped IMachine properties
904/////////////////////////////////////////////////////////////////////////////
905HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
906{
907 /* mParent is constant during life time, no need to lock */
908 ComObjPtr<VirtualBox> pVirtualBox(mParent);
909 aParent = pVirtualBox;
910
911 return S_OK;
912}
913
914
915HRESULT Machine::getAccessible(BOOL *aAccessible)
916{
917 /* In some cases (medium registry related), it is necessary to be able to
918 * go through the list of all machines. Happens when an inaccessible VM
919 * has a sensible medium registry. */
920 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
922
923 HRESULT rc = S_OK;
924
925 if (!mData->mAccessible)
926 {
927 /* try to initialize the VM once more if not accessible */
928
929 AutoReinitSpan autoReinitSpan(this);
930 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
931
932#ifdef DEBUG
933 LogFlowThisFunc(("Dumping media backreferences\n"));
934 mParent->i_dumpAllBackRefs();
935#endif
936
937 if (mData->pMachineConfigFile)
938 {
939 // reset the XML file to force loadSettings() (called from i_registeredInit())
940 // to parse it again; the file might have changed
941 delete mData->pMachineConfigFile;
942 mData->pMachineConfigFile = NULL;
943 }
944
945 rc = i_registeredInit();
946
947 if (SUCCEEDED(rc) && mData->mAccessible)
948 {
949 autoReinitSpan.setSucceeded();
950
951 /* make sure interesting parties will notice the accessibility
952 * state change */
953 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
954 mParent->i_onMachineDataChange(mData->mUuid);
955 }
956 }
957
958 if (SUCCEEDED(rc))
959 *aAccessible = mData->mAccessible;
960
961 LogFlowThisFuncLeave();
962
963 return rc;
964}
965
966HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
967{
968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
969
970 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
971 {
972 /* return shortly */
973 aAccessError = NULL;
974 return S_OK;
975 }
976
977 HRESULT rc = S_OK;
978
979 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
980 rc = errorInfo.createObject();
981 if (SUCCEEDED(rc))
982 {
983 errorInfo->init(mData->mAccessError.getResultCode(),
984 mData->mAccessError.getInterfaceID().ref(),
985 Utf8Str(mData->mAccessError.getComponent()).c_str(),
986 Utf8Str(mData->mAccessError.getText()));
987 aAccessError = errorInfo;
988 }
989
990 return rc;
991}
992
993HRESULT Machine::getName(com::Utf8Str &aName)
994{
995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 aName = mUserData->s.strName;
998
999 return S_OK;
1000}
1001
1002HRESULT Machine::setName(const com::Utf8Str &aName)
1003{
1004 // prohibit setting a UUID only as the machine name, or else it can
1005 // never be found by findMachine()
1006 Guid test(aName);
1007
1008 if (test.isValid())
1009 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1010
1011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 HRESULT rc = i_checkStateDependency(MutableStateDep);
1014 if (FAILED(rc)) return rc;
1015
1016 i_setModified(IsModified_MachineData);
1017 mUserData.backup();
1018 mUserData->s.strName = aName;
1019
1020 return S_OK;
1021}
1022
1023HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1024{
1025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1026
1027 aDescription = mUserData->s.strDescription;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1033{
1034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 // this can be done in principle in any state as it doesn't affect the VM
1037 // significantly, but play safe by not messing around while complex
1038 // activities are going on
1039 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1040 if (FAILED(rc)) return rc;
1041
1042 i_setModified(IsModified_MachineData);
1043 mUserData.backup();
1044 mUserData->s.strDescription = aDescription;
1045
1046 return S_OK;
1047}
1048
1049HRESULT Machine::getId(com::Guid &aId)
1050{
1051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 aId = mData->mUuid;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061 aGroups.resize(mUserData->s.llGroups.size());
1062 size_t i = 0;
1063 for (StringsList::const_iterator
1064 it = mUserData->s.llGroups.begin();
1065 it != mUserData->s.llGroups.end();
1066 ++it, ++i)
1067 aGroups[i] = (*it);
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1073{
1074 StringsList llGroups;
1075 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1076 if (FAILED(rc))
1077 return rc;
1078
1079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 rc = i_checkStateDependency(MutableOrSavedStateDep);
1082 if (FAILED(rc)) return rc;
1083
1084 i_setModified(IsModified_MachineData);
1085 mUserData.backup();
1086 mUserData->s.llGroups = llGroups;
1087
1088 return S_OK;
1089}
1090
1091HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1092{
1093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1094
1095 aOSTypeId = mUserData->s.strOsType;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1101{
1102 /* look up the object by Id to check it is valid */
1103 ComObjPtr<GuestOSType> pGuestOSType;
1104 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1105
1106 /* when setting, always use the "etalon" value for consistency -- lookup
1107 * by ID is case-insensitive and the input value may have different case */
1108 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1109
1110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 HRESULT rc = i_checkStateDependency(MutableStateDep);
1113 if (FAILED(rc)) return rc;
1114
1115 i_setModified(IsModified_MachineData);
1116 mUserData.backup();
1117 mUserData->s.strOsType = osTypeId;
1118
1119 return S_OK;
1120}
1121
1122HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1123{
1124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1125
1126 *aFirmwareType = mHWData->mFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1132{
1133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 HRESULT rc = i_checkStateDependency(MutableStateDep);
1136 if (FAILED(rc)) return rc;
1137
1138 i_setModified(IsModified_MachineData);
1139 mHWData.backup();
1140 mHWData->mFirmwareType = aFirmwareType;
1141
1142 return S_OK;
1143}
1144
1145HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1146{
1147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1148
1149 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1155{
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 HRESULT rc = i_checkStateDependency(MutableStateDep);
1159 if (FAILED(rc)) return rc;
1160
1161 i_setModified(IsModified_MachineData);
1162 mHWData.backup();
1163 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1164
1165 return S_OK;
1166}
1167
1168HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1169{
1170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 *aPointingHIDType = mHWData->mPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1178{
1179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 HRESULT rc = i_checkStateDependency(MutableStateDep);
1182 if (FAILED(rc)) return rc;
1183
1184 i_setModified(IsModified_MachineData);
1185 mHWData.backup();
1186 mHWData->mPointingHIDType = aPointingHIDType;
1187
1188 return S_OK;
1189}
1190
1191HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1192{
1193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1194
1195 *aChipsetType = mHWData->mChipsetType;
1196
1197 return S_OK;
1198}
1199
1200HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1201{
1202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 HRESULT rc = i_checkStateDependency(MutableStateDep);
1205 if (FAILED(rc)) return rc;
1206
1207 if (aChipsetType != mHWData->mChipsetType)
1208 {
1209 i_setModified(IsModified_MachineData);
1210 mHWData.backup();
1211 mHWData->mChipsetType = aChipsetType;
1212
1213 // Resize network adapter array, to be finalized on commit/rollback.
1214 // We must not throw away entries yet, otherwise settings are lost
1215 // without a way to roll back.
1216 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1217 size_t oldCount = mNetworkAdapters.size();
1218 if (newCount > oldCount)
1219 {
1220 mNetworkAdapters.resize(newCount);
1221 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1222 {
1223 unconst(mNetworkAdapters[slot]).createObject();
1224 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1225 }
1226 }
1227 }
1228
1229 return S_OK;
1230}
1231
1232HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1233{
1234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 aParavirtDebug = mHWData->mParavirtDebug;
1237 return S_OK;
1238}
1239
1240HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1241{
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = i_checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 /** @todo Parse/validate options? */
1248 if (aParavirtDebug != mHWData->mParavirtDebug)
1249 {
1250 i_setModified(IsModified_MachineData);
1251 mHWData.backup();
1252 mHWData->mParavirtDebug = aParavirtDebug;
1253 }
1254
1255 return S_OK;
1256}
1257
1258HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1259{
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 *aParavirtProvider = mHWData->mParavirtProvider;
1263
1264 return S_OK;
1265}
1266
1267HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1268{
1269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1270
1271 HRESULT rc = i_checkStateDependency(MutableStateDep);
1272 if (FAILED(rc)) return rc;
1273
1274 if (aParavirtProvider != mHWData->mParavirtProvider)
1275 {
1276 i_setModified(IsModified_MachineData);
1277 mHWData.backup();
1278 mHWData->mParavirtProvider = aParavirtProvider;
1279 }
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1285{
1286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 *aParavirtProvider = mHWData->mParavirtProvider;
1289 switch (mHWData->mParavirtProvider)
1290 {
1291 case ParavirtProvider_None:
1292 case ParavirtProvider_HyperV:
1293 case ParavirtProvider_KVM:
1294 case ParavirtProvider_Minimal:
1295 break;
1296
1297 /* Resolve dynamic provider types to the effective types. */
1298 default:
1299 {
1300 ComObjPtr<GuestOSType> pGuestOSType;
1301 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1302 pGuestOSType);
1303 if (FAILED(hrc2) || pGuestOSType.isNull())
1304 {
1305 *aParavirtProvider = ParavirtProvider_None;
1306 break;
1307 }
1308
1309 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1310 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1311
1312 switch (mHWData->mParavirtProvider)
1313 {
1314 case ParavirtProvider_Legacy:
1315 {
1316 if (fOsXGuest)
1317 *aParavirtProvider = ParavirtProvider_Minimal;
1318 else
1319 *aParavirtProvider = ParavirtProvider_None;
1320 break;
1321 }
1322
1323 case ParavirtProvider_Default:
1324 {
1325 if (fOsXGuest)
1326 *aParavirtProvider = ParavirtProvider_Minimal;
1327 else if ( mUserData->s.strOsType == "Windows10"
1328 || mUserData->s.strOsType == "Windows10_64"
1329 || mUserData->s.strOsType == "Windows81"
1330 || mUserData->s.strOsType == "Windows81_64"
1331 || mUserData->s.strOsType == "Windows8"
1332 || mUserData->s.strOsType == "Windows8_64"
1333 || mUserData->s.strOsType == "Windows7"
1334 || mUserData->s.strOsType == "Windows7_64"
1335 || mUserData->s.strOsType == "WindowsVista"
1336 || mUserData->s.strOsType == "WindowsVista_64"
1337 || mUserData->s.strOsType == "Windows2012"
1338 || mUserData->s.strOsType == "Windows2012_64"
1339 || mUserData->s.strOsType == "Windows2008"
1340 || mUserData->s.strOsType == "Windows2008_64")
1341 {
1342 *aParavirtProvider = ParavirtProvider_HyperV;
1343 }
1344 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1345 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1346 || mUserData->s.strOsType == "Linux"
1347 || mUserData->s.strOsType == "Linux_64"
1348 || mUserData->s.strOsType == "ArchLinux"
1349 || mUserData->s.strOsType == "ArchLinux_64"
1350 || mUserData->s.strOsType == "Debian"
1351 || mUserData->s.strOsType == "Debian_64"
1352 || mUserData->s.strOsType == "Fedora"
1353 || mUserData->s.strOsType == "Fedora_64"
1354 || mUserData->s.strOsType == "Gentoo"
1355 || mUserData->s.strOsType == "Gentoo_64"
1356 || mUserData->s.strOsType == "Mandriva"
1357 || mUserData->s.strOsType == "Mandriva_64"
1358 || mUserData->s.strOsType == "OpenSUSE"
1359 || mUserData->s.strOsType == "OpenSUSE_64"
1360 || mUserData->s.strOsType == "Oracle"
1361 || mUserData->s.strOsType == "Oracle_64"
1362 || mUserData->s.strOsType == "RedHat"
1363 || mUserData->s.strOsType == "RedHat_64"
1364 || mUserData->s.strOsType == "Turbolinux"
1365 || mUserData->s.strOsType == "Turbolinux_64"
1366 || mUserData->s.strOsType == "Ubuntu"
1367 || mUserData->s.strOsType == "Ubuntu_64"
1368 || mUserData->s.strOsType == "Xandros"
1369 || mUserData->s.strOsType == "Xandros_64")
1370 {
1371 *aParavirtProvider = ParavirtProvider_KVM;
1372 }
1373 else
1374 *aParavirtProvider = ParavirtProvider_None;
1375 break;
1376 }
1377
1378 default: AssertFailedBreak(); /* Shut up MSC. */
1379 }
1380 break;
1381 }
1382 }
1383
1384 Assert( *aParavirtProvider == ParavirtProvider_None
1385 || *aParavirtProvider == ParavirtProvider_Minimal
1386 || *aParavirtProvider == ParavirtProvider_HyperV
1387 || *aParavirtProvider == ParavirtProvider_KVM);
1388 return S_OK;
1389}
1390
1391HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1392{
1393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1394
1395 aHardwareVersion = mHWData->mHWVersion;
1396
1397 return S_OK;
1398}
1399
1400HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1401{
1402 /* check known version */
1403 Utf8Str hwVersion = aHardwareVersion;
1404 if ( hwVersion.compare("1") != 0
1405 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1406 return setError(E_INVALIDARG,
1407 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = i_checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 i_setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mHWVersion = aHardwareVersion;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 if (!mHWData->mHardwareUUID.isZero())
1426 aHardwareUUID = mHWData->mHardwareUUID;
1427 else
1428 aHardwareUUID = mData->mUuid;
1429
1430 return S_OK;
1431}
1432
1433HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1434{
1435 if (!aHardwareUUID.isValid())
1436 return E_INVALIDARG;
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 if (aHardwareUUID == mData->mUuid)
1446 mHWData->mHardwareUUID.clear();
1447 else
1448 mHWData->mHardwareUUID = aHardwareUUID;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1454{
1455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 *aMemorySize = mHWData->mMemorySize;
1458
1459 return S_OK;
1460}
1461
1462HRESULT Machine::setMemorySize(ULONG aMemorySize)
1463{
1464 /* check RAM limits */
1465 if ( aMemorySize < MM_RAM_MIN_IN_MB
1466 || aMemorySize > MM_RAM_MAX_IN_MB
1467 )
1468 return setError(E_INVALIDARG,
1469 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1470 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1471
1472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 HRESULT rc = i_checkStateDependency(MutableStateDep);
1475 if (FAILED(rc)) return rc;
1476
1477 i_setModified(IsModified_MachineData);
1478 mHWData.backup();
1479 mHWData->mMemorySize = aMemorySize;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1485{
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aCPUCount = mHWData->mCPUCount;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::setCPUCount(ULONG aCPUCount)
1494{
1495 /* check CPU limits */
1496 if ( aCPUCount < SchemaDefs::MinCPUCount
1497 || aCPUCount > SchemaDefs::MaxCPUCount
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1501 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1502
1503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1504
1505 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1506 if (mHWData->mCPUHotPlugEnabled)
1507 {
1508 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1509 {
1510 if (mHWData->mCPUAttached[idx])
1511 return setError(E_INVALIDARG,
1512 tr("There is still a CPU attached to socket %lu."
1513 "Detach the CPU before removing the socket"),
1514 aCPUCount, idx+1);
1515 }
1516 }
1517
1518 HRESULT rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 i_setModified(IsModified_MachineData);
1522 mHWData.backup();
1523 mHWData->mCPUCount = aCPUCount;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1538{
1539 HRESULT rc = S_OK;
1540
1541 /* check throttle limits */
1542 if ( aCPUExecutionCap < 1
1543 || aCPUExecutionCap > 100
1544 )
1545 return setError(E_INVALIDARG,
1546 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1547 aCPUExecutionCap, 1, 100);
1548
1549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1550
1551 alock.release();
1552 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1553 alock.acquire();
1554 if (FAILED(rc)) return rc;
1555
1556 i_setModified(IsModified_MachineData);
1557 mHWData.backup();
1558 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1559
1560 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1561 if (Global::IsOnline(mData->mMachineState))
1562 i_saveSettings(NULL);
1563
1564 return S_OK;
1565}
1566
1567HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1568{
1569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1570
1571 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1572
1573 return S_OK;
1574}
1575
1576HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1577{
1578 HRESULT rc = S_OK;
1579
1580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
1582 rc = i_checkStateDependency(MutableStateDep);
1583 if (FAILED(rc)) return rc;
1584
1585 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1586 {
1587 if (aCPUHotPlugEnabled)
1588 {
1589 i_setModified(IsModified_MachineData);
1590 mHWData.backup();
1591
1592 /* Add the amount of CPUs currently attached */
1593 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1594 mHWData->mCPUAttached[i] = true;
1595 }
1596 else
1597 {
1598 /*
1599 * We can disable hotplug only if the amount of maximum CPUs is equal
1600 * to the amount of attached CPUs
1601 */
1602 unsigned cCpusAttached = 0;
1603 unsigned iHighestId = 0;
1604
1605 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1606 {
1607 if (mHWData->mCPUAttached[i])
1608 {
1609 cCpusAttached++;
1610 iHighestId = i;
1611 }
1612 }
1613
1614 if ( (cCpusAttached != mHWData->mCPUCount)
1615 || (iHighestId >= mHWData->mCPUCount))
1616 return setError(E_INVALIDARG,
1617 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1618
1619 i_setModified(IsModified_MachineData);
1620 mHWData.backup();
1621 }
1622 }
1623
1624 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1625
1626 return rc;
1627}
1628
1629HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1630{
1631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1634
1635 return S_OK;
1636}
1637
1638HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1639{
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1643 if (SUCCEEDED(hrc))
1644 {
1645 i_setModified(IsModified_MachineData);
1646 mHWData.backup();
1647 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1648 }
1649 return hrc;
1650}
1651
1652HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 aCPUProfile = mHWData->mCpuProfile;
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1660{
1661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1663 if (SUCCEEDED(hrc))
1664 {
1665 i_setModified(IsModified_MachineData);
1666 mHWData.backup();
1667 /* Empty equals 'host'. */
1668 if (aCPUProfile.isNotEmpty())
1669 mHWData->mCpuProfile = aCPUProfile;
1670 else
1671 mHWData->mCpuProfile = "host";
1672 }
1673 return hrc;
1674}
1675
1676HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1677{
1678#ifdef VBOX_WITH_USB_CARDREADER
1679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1682
1683 return S_OK;
1684#else
1685 NOREF(aEmulatedUSBCardReaderEnabled);
1686 return E_NOTIMPL;
1687#endif
1688}
1689
1690HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1691{
1692#ifdef VBOX_WITH_USB_CARDREADER
1693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1696 if (FAILED(rc)) return rc;
1697
1698 i_setModified(IsModified_MachineData);
1699 mHWData.backup();
1700 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1701
1702 return S_OK;
1703#else
1704 NOREF(aEmulatedUSBCardReaderEnabled);
1705 return E_NOTIMPL;
1706#endif
1707}
1708
1709HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1710{
1711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 *aHPETEnabled = mHWData->mHPETEnabled;
1714
1715 return S_OK;
1716}
1717
1718HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1719{
1720 HRESULT rc = S_OK;
1721
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 rc = i_checkStateDependency(MutableStateDep);
1725 if (FAILED(rc)) return rc;
1726
1727 i_setModified(IsModified_MachineData);
1728 mHWData.backup();
1729
1730 mHWData->mHPETEnabled = aHPETEnabled;
1731
1732 return rc;
1733}
1734
1735HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1736{
1737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1740
1741 return S_OK;
1742}
1743
1744HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1745{
1746 switch (aGraphicsControllerType)
1747 {
1748 case GraphicsControllerType_Null:
1749 case GraphicsControllerType_VBoxVGA:
1750#ifdef VBOX_WITH_VMSVGA
1751 case GraphicsControllerType_VMSVGA:
1752 case GraphicsControllerType_VBoxSVGA:
1753#endif
1754 break;
1755 default:
1756 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1757 }
1758
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 HRESULT rc = i_checkStateDependency(MutableStateDep);
1762 if (FAILED(rc)) return rc;
1763
1764 i_setModified(IsModified_MachineData);
1765 mHWData.backup();
1766 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1767
1768 return S_OK;
1769}
1770
1771HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1772{
1773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 *aVRAMSize = mHWData->mVRAMSize;
1776
1777 return S_OK;
1778}
1779
1780HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1781{
1782 /* check VRAM limits */
1783 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1784 return setError(E_INVALIDARG,
1785 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1786 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1787
1788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1789
1790 HRESULT rc = i_checkStateDependency(MutableStateDep);
1791 if (FAILED(rc)) return rc;
1792
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 mHWData->mVRAMSize = aVRAMSize;
1796
1797 return S_OK;
1798}
1799
1800/** @todo this method should not be public */
1801HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1802{
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1806
1807 return S_OK;
1808}
1809
1810/**
1811 * Set the memory balloon size.
1812 *
1813 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1814 * we have to make sure that we never call IGuest from here.
1815 */
1816HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1817{
1818 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1819#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1820 /* check limits */
1821 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1822 return setError(E_INVALIDARG,
1823 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1824 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1825
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 i_setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1831
1832 return S_OK;
1833#else
1834 NOREF(aMemoryBalloonSize);
1835 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1836#endif
1837}
1838
1839HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1840{
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1848{
1849#ifdef VBOX_WITH_PAGE_SHARING
1850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1853 i_setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1856 return S_OK;
1857#else
1858 NOREF(aPageFusionEnabled);
1859 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1860#endif
1861}
1862
1863HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1864{
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1873{
1874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1875
1876 HRESULT rc = i_checkStateDependency(MutableStateDep);
1877 if (FAILED(rc)) return rc;
1878
1879 /** @todo check validity! */
1880
1881 i_setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1884
1885 return S_OK;
1886}
1887
1888
1889HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1890{
1891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1894
1895 return S_OK;
1896}
1897
1898HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1899{
1900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1901
1902 HRESULT rc = i_checkStateDependency(MutableStateDep);
1903 if (FAILED(rc)) return rc;
1904
1905 /** @todo check validity! */
1906 i_setModified(IsModified_MachineData);
1907 mHWData.backup();
1908 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1909
1910 return S_OK;
1911}
1912
1913HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1914{
1915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 *aMonitorCount = mHWData->mMonitorCount;
1918
1919 return S_OK;
1920}
1921
1922HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1923{
1924 /* make sure monitor count is a sensible number */
1925 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1926 return setError(E_INVALIDARG,
1927 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1928 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1929
1930 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1931
1932 HRESULT rc = i_checkStateDependency(MutableStateDep);
1933 if (FAILED(rc)) return rc;
1934
1935 i_setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mMonitorCount = aMonitorCount;
1938
1939 return S_OK;
1940}
1941
1942HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1943{
1944 /* mBIOSSettings is constant during life time, no need to lock */
1945 aBIOSSettings = mBIOSSettings;
1946
1947 return S_OK;
1948}
1949
1950HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1951{
1952 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1953
1954 aRecordingSettings = mRecordingSettings;
1955
1956 return S_OK;
1957}
1958
1959HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1960{
1961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1962
1963 switch (aProperty)
1964 {
1965 case CPUPropertyType_PAE:
1966 *aValue = mHWData->mPAEEnabled;
1967 break;
1968
1969 case CPUPropertyType_LongMode:
1970 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1971 *aValue = TRUE;
1972 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1973 *aValue = FALSE;
1974#if HC_ARCH_BITS == 64
1975 else
1976 *aValue = TRUE;
1977#else
1978 else
1979 {
1980 *aValue = FALSE;
1981
1982 ComObjPtr<GuestOSType> pGuestOSType;
1983 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1984 pGuestOSType);
1985 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1986 {
1987 if (pGuestOSType->i_is64Bit())
1988 {
1989 ComObjPtr<Host> pHost = mParent->i_host();
1990 alock.release();
1991
1992 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1993 if (FAILED(hrc2))
1994 *aValue = FALSE;
1995 }
1996 }
1997 }
1998#endif
1999 break;
2000
2001 case CPUPropertyType_TripleFaultReset:
2002 *aValue = mHWData->mTripleFaultReset;
2003 break;
2004
2005 case CPUPropertyType_APIC:
2006 *aValue = mHWData->mAPIC;
2007 break;
2008
2009 case CPUPropertyType_X2APIC:
2010 *aValue = mHWData->mX2APIC;
2011 break;
2012
2013 case CPUPropertyType_IBPBOnVMExit:
2014 *aValue = mHWData->mIBPBOnVMExit;
2015 break;
2016
2017 case CPUPropertyType_IBPBOnVMEntry:
2018 *aValue = mHWData->mIBPBOnVMEntry;
2019 break;
2020
2021 case CPUPropertyType_SpecCtrl:
2022 *aValue = mHWData->mSpecCtrl;
2023 break;
2024
2025 case CPUPropertyType_SpecCtrlByHost:
2026 *aValue = mHWData->mSpecCtrlByHost;
2027 break;
2028
2029 case CPUPropertyType_HWVirt:
2030 *aValue = mHWData->mNestedHWVirt;
2031 break;
2032
2033 case CPUPropertyType_L1DFlushOnEMTScheduling:
2034 *aValue = mHWData->mL1DFlushOnSched;
2035 break;
2036
2037 case CPUPropertyType_L1DFlushOnVMEntry:
2038 *aValue = mHWData->mL1DFlushOnVMEntry;
2039 break;
2040
2041 case CPUPropertyType_MDSClearOnEMTScheduling:
2042 *aValue = mHWData->mMDSClearOnSched;
2043 break;
2044
2045 case CPUPropertyType_MDSClearOnVMEntry:
2046 *aValue = mHWData->mMDSClearOnVMEntry;
2047 break;
2048
2049 default:
2050 return E_INVALIDARG;
2051 }
2052 return S_OK;
2053}
2054
2055HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2056{
2057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 HRESULT rc = i_checkStateDependency(MutableStateDep);
2060 if (FAILED(rc)) return rc;
2061
2062 switch (aProperty)
2063 {
2064 case CPUPropertyType_PAE:
2065 i_setModified(IsModified_MachineData);
2066 mHWData.backup();
2067 mHWData->mPAEEnabled = !!aValue;
2068 break;
2069
2070 case CPUPropertyType_LongMode:
2071 i_setModified(IsModified_MachineData);
2072 mHWData.backup();
2073 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2074 break;
2075
2076 case CPUPropertyType_TripleFaultReset:
2077 i_setModified(IsModified_MachineData);
2078 mHWData.backup();
2079 mHWData->mTripleFaultReset = !!aValue;
2080 break;
2081
2082 case CPUPropertyType_APIC:
2083 if (mHWData->mX2APIC)
2084 aValue = TRUE;
2085 i_setModified(IsModified_MachineData);
2086 mHWData.backup();
2087 mHWData->mAPIC = !!aValue;
2088 break;
2089
2090 case CPUPropertyType_X2APIC:
2091 i_setModified(IsModified_MachineData);
2092 mHWData.backup();
2093 mHWData->mX2APIC = !!aValue;
2094 if (aValue)
2095 mHWData->mAPIC = !!aValue;
2096 break;
2097
2098 case CPUPropertyType_IBPBOnVMExit:
2099 i_setModified(IsModified_MachineData);
2100 mHWData.backup();
2101 mHWData->mIBPBOnVMExit = !!aValue;
2102 break;
2103
2104 case CPUPropertyType_IBPBOnVMEntry:
2105 i_setModified(IsModified_MachineData);
2106 mHWData.backup();
2107 mHWData->mIBPBOnVMEntry = !!aValue;
2108 break;
2109
2110 case CPUPropertyType_SpecCtrl:
2111 i_setModified(IsModified_MachineData);
2112 mHWData.backup();
2113 mHWData->mSpecCtrl = !!aValue;
2114 break;
2115
2116 case CPUPropertyType_SpecCtrlByHost:
2117 i_setModified(IsModified_MachineData);
2118 mHWData.backup();
2119 mHWData->mSpecCtrlByHost = !!aValue;
2120 break;
2121
2122 case CPUPropertyType_HWVirt:
2123 i_setModified(IsModified_MachineData);
2124 mHWData.backup();
2125 mHWData->mNestedHWVirt = !!aValue;
2126 break;
2127
2128 case CPUPropertyType_L1DFlushOnEMTScheduling:
2129 i_setModified(IsModified_MachineData);
2130 mHWData.backup();
2131 mHWData->mL1DFlushOnSched = !!aValue;
2132 break;
2133
2134 case CPUPropertyType_L1DFlushOnVMEntry:
2135 i_setModified(IsModified_MachineData);
2136 mHWData.backup();
2137 mHWData->mL1DFlushOnVMEntry = !!aValue;
2138 break;
2139
2140 case CPUPropertyType_MDSClearOnEMTScheduling:
2141 i_setModified(IsModified_MachineData);
2142 mHWData.backup();
2143 mHWData->mMDSClearOnSched = !!aValue;
2144 break;
2145
2146 case CPUPropertyType_MDSClearOnVMEntry:
2147 i_setModified(IsModified_MachineData);
2148 mHWData.backup();
2149 mHWData->mMDSClearOnVMEntry = !!aValue;
2150 break;
2151
2152 default:
2153 return E_INVALIDARG;
2154 }
2155 return S_OK;
2156}
2157
2158HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2159 ULONG *aValEcx, ULONG *aValEdx)
2160{
2161 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2162 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2163 {
2164 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2165 it != mHWData->mCpuIdLeafList.end();
2166 ++it)
2167 {
2168 if (aOrdinal == 0)
2169 {
2170 const settings::CpuIdLeaf &rLeaf= *it;
2171 *aIdx = rLeaf.idx;
2172 *aSubIdx = rLeaf.idxSub;
2173 *aValEax = rLeaf.uEax;
2174 *aValEbx = rLeaf.uEbx;
2175 *aValEcx = rLeaf.uEcx;
2176 *aValEdx = rLeaf.uEdx;
2177 return S_OK;
2178 }
2179 aOrdinal--;
2180 }
2181 }
2182 return E_INVALIDARG;
2183}
2184
2185HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2186{
2187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2188
2189 /*
2190 * Search the list.
2191 */
2192 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2193 {
2194 const settings::CpuIdLeaf &rLeaf= *it;
2195 if ( rLeaf.idx == aIdx
2196 && ( aSubIdx == UINT32_MAX
2197 || rLeaf.idxSub == aSubIdx) )
2198 {
2199 *aValEax = rLeaf.uEax;
2200 *aValEbx = rLeaf.uEbx;
2201 *aValEcx = rLeaf.uEcx;
2202 *aValEdx = rLeaf.uEdx;
2203 return S_OK;
2204 }
2205 }
2206
2207 return E_INVALIDARG;
2208}
2209
2210
2211HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2212{
2213 /*
2214 * Validate input before taking locks and checking state.
2215 */
2216 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2217 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2218 if ( aIdx >= UINT32_C(0x20)
2219 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2220 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2221 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2222
2223 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2224 HRESULT rc = i_checkStateDependency(MutableStateDep);
2225 if (FAILED(rc)) return rc;
2226
2227 /*
2228 * Impose a maximum number of leaves.
2229 */
2230 if (mHWData->mCpuIdLeafList.size() > 256)
2231 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2232
2233 /*
2234 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2235 */
2236 i_setModified(IsModified_MachineData);
2237 mHWData.backup();
2238
2239 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2240 {
2241 settings::CpuIdLeaf &rLeaf= *it;
2242 if ( rLeaf.idx == aIdx
2243 && ( aSubIdx == UINT32_MAX
2244 || rLeaf.idxSub == aSubIdx) )
2245 it = mHWData->mCpuIdLeafList.erase(it);
2246 else
2247 ++it;
2248 }
2249
2250 settings::CpuIdLeaf NewLeaf;
2251 NewLeaf.idx = aIdx;
2252 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2253 NewLeaf.uEax = aValEax;
2254 NewLeaf.uEbx = aValEbx;
2255 NewLeaf.uEcx = aValEcx;
2256 NewLeaf.uEdx = aValEdx;
2257 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2258 return S_OK;
2259}
2260
2261HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2262{
2263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2264
2265 HRESULT rc = i_checkStateDependency(MutableStateDep);
2266 if (FAILED(rc)) return rc;
2267
2268 /*
2269 * Do the removal.
2270 */
2271 bool fModified = mHWData.isBackedUp();
2272 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2273 {
2274 settings::CpuIdLeaf &rLeaf= *it;
2275 if ( rLeaf.idx == aIdx
2276 && ( aSubIdx == UINT32_MAX
2277 || rLeaf.idxSub == aSubIdx) )
2278 {
2279 if (!fModified)
2280 {
2281 fModified = true;
2282 i_setModified(IsModified_MachineData);
2283 mHWData.backup();
2284 // Start from the beginning, since mHWData.backup() creates
2285 // a new list, causing iterator mixup. This makes sure that
2286 // the settings are not unnecessarily marked as modified,
2287 // at the price of extra list walking.
2288 it = mHWData->mCpuIdLeafList.begin();
2289 }
2290 else
2291 it = mHWData->mCpuIdLeafList.erase(it);
2292 }
2293 else
2294 ++it;
2295 }
2296
2297 return S_OK;
2298}
2299
2300HRESULT Machine::removeAllCPUIDLeaves()
2301{
2302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2303
2304 HRESULT rc = i_checkStateDependency(MutableStateDep);
2305 if (FAILED(rc)) return rc;
2306
2307 if (mHWData->mCpuIdLeafList.size() > 0)
2308 {
2309 i_setModified(IsModified_MachineData);
2310 mHWData.backup();
2311
2312 mHWData->mCpuIdLeafList.clear();
2313 }
2314
2315 return S_OK;
2316}
2317HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2318{
2319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2320
2321 switch(aProperty)
2322 {
2323 case HWVirtExPropertyType_Enabled:
2324 *aValue = mHWData->mHWVirtExEnabled;
2325 break;
2326
2327 case HWVirtExPropertyType_VPID:
2328 *aValue = mHWData->mHWVirtExVPIDEnabled;
2329 break;
2330
2331 case HWVirtExPropertyType_NestedPaging:
2332 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2333 break;
2334
2335 case HWVirtExPropertyType_UnrestrictedExecution:
2336 *aValue = mHWData->mHWVirtExUXEnabled;
2337 break;
2338
2339 case HWVirtExPropertyType_LargePages:
2340 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2341#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2342 *aValue = FALSE;
2343#endif
2344 break;
2345
2346 case HWVirtExPropertyType_Force:
2347 *aValue = mHWData->mHWVirtExForceEnabled;
2348 break;
2349
2350 case HWVirtExPropertyType_UseNativeApi:
2351 *aValue = mHWData->mHWVirtExUseNativeApi;
2352 break;
2353
2354 default:
2355 return E_INVALIDARG;
2356 }
2357 return S_OK;
2358}
2359
2360HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2361{
2362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 HRESULT rc = i_checkStateDependency(MutableStateDep);
2365 if (FAILED(rc)) return rc;
2366
2367 switch (aProperty)
2368 {
2369 case HWVirtExPropertyType_Enabled:
2370 i_setModified(IsModified_MachineData);
2371 mHWData.backup();
2372 mHWData->mHWVirtExEnabled = !!aValue;
2373 break;
2374
2375 case HWVirtExPropertyType_VPID:
2376 i_setModified(IsModified_MachineData);
2377 mHWData.backup();
2378 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2379 break;
2380
2381 case HWVirtExPropertyType_NestedPaging:
2382 i_setModified(IsModified_MachineData);
2383 mHWData.backup();
2384 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2385 break;
2386
2387 case HWVirtExPropertyType_UnrestrictedExecution:
2388 i_setModified(IsModified_MachineData);
2389 mHWData.backup();
2390 mHWData->mHWVirtExUXEnabled = !!aValue;
2391 break;
2392
2393 case HWVirtExPropertyType_LargePages:
2394 i_setModified(IsModified_MachineData);
2395 mHWData.backup();
2396 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2397 break;
2398
2399 case HWVirtExPropertyType_Force:
2400 i_setModified(IsModified_MachineData);
2401 mHWData.backup();
2402 mHWData->mHWVirtExForceEnabled = !!aValue;
2403 break;
2404
2405 case HWVirtExPropertyType_UseNativeApi:
2406 i_setModified(IsModified_MachineData);
2407 mHWData.backup();
2408 mHWData->mHWVirtExUseNativeApi = !!aValue;
2409 break;
2410
2411 default:
2412 return E_INVALIDARG;
2413 }
2414
2415 return S_OK;
2416}
2417
2418HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2419{
2420 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2421
2422 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2423
2424 return S_OK;
2425}
2426
2427HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2428{
2429 /** @todo (r=dmik):
2430 * 1. Allow to change the name of the snapshot folder containing snapshots
2431 * 2. Rename the folder on disk instead of just changing the property
2432 * value (to be smart and not to leave garbage). Note that it cannot be
2433 * done here because the change may be rolled back. Thus, the right
2434 * place is #saveSettings().
2435 */
2436
2437 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2438
2439 HRESULT rc = i_checkStateDependency(MutableStateDep);
2440 if (FAILED(rc)) return rc;
2441
2442 if (!mData->mCurrentSnapshot.isNull())
2443 return setError(E_FAIL,
2444 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2445
2446 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2447
2448 if (strSnapshotFolder.isEmpty())
2449 strSnapshotFolder = "Snapshots";
2450 int vrc = i_calculateFullPath(strSnapshotFolder,
2451 strSnapshotFolder);
2452 if (RT_FAILURE(vrc))
2453 return setErrorBoth(E_FAIL, vrc,
2454 tr("Invalid snapshot folder '%s' (%Rrc)"),
2455 strSnapshotFolder.c_str(), vrc);
2456
2457 i_setModified(IsModified_MachineData);
2458 mUserData.backup();
2459
2460 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2461
2462 return S_OK;
2463}
2464
2465HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2466{
2467 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2468
2469 aMediumAttachments.resize(mMediumAttachments->size());
2470 size_t i = 0;
2471 for (MediumAttachmentList::const_iterator
2472 it = mMediumAttachments->begin();
2473 it != mMediumAttachments->end();
2474 ++it, ++i)
2475 aMediumAttachments[i] = *it;
2476
2477 return S_OK;
2478}
2479
2480HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2481{
2482 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2483
2484 Assert(!!mVRDEServer);
2485
2486 aVRDEServer = mVRDEServer;
2487
2488 return S_OK;
2489}
2490
2491HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2492{
2493 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2494
2495 aAudioAdapter = mAudioAdapter;
2496
2497 return S_OK;
2498}
2499
2500HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2501{
2502#ifdef VBOX_WITH_VUSB
2503 clearError();
2504 MultiResult rc(S_OK);
2505
2506# ifdef VBOX_WITH_USB
2507 rc = mParent->i_host()->i_checkUSBProxyService();
2508 if (FAILED(rc)) return rc;
2509# endif
2510
2511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2512
2513 aUSBControllers.resize(mUSBControllers->size());
2514 size_t i = 0;
2515 for (USBControllerList::const_iterator
2516 it = mUSBControllers->begin();
2517 it != mUSBControllers->end();
2518 ++it, ++i)
2519 aUSBControllers[i] = *it;
2520
2521 return S_OK;
2522#else
2523 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2524 * extended error info to indicate that USB is simply not available
2525 * (w/o treating it as a failure), for example, as in OSE */
2526 NOREF(aUSBControllers);
2527 ReturnComNotImplemented();
2528#endif /* VBOX_WITH_VUSB */
2529}
2530
2531HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2532{
2533#ifdef VBOX_WITH_VUSB
2534 clearError();
2535 MultiResult rc(S_OK);
2536
2537# ifdef VBOX_WITH_USB
2538 rc = mParent->i_host()->i_checkUSBProxyService();
2539 if (FAILED(rc)) return rc;
2540# endif
2541
2542 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2543
2544 aUSBDeviceFilters = mUSBDeviceFilters;
2545 return rc;
2546#else
2547 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2548 * extended error info to indicate that USB is simply not available
2549 * (w/o treating it as a failure), for example, as in OSE */
2550 NOREF(aUSBDeviceFilters);
2551 ReturnComNotImplemented();
2552#endif /* VBOX_WITH_VUSB */
2553}
2554
2555HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2556{
2557 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2558
2559 aSettingsFilePath = mData->m_strConfigFileFull;
2560
2561 return S_OK;
2562}
2563
2564HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2565{
2566 RT_NOREF(aSettingsFilePath);
2567 ReturnComNotImplemented();
2568}
2569
2570HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2571{
2572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2573
2574 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2575 if (FAILED(rc)) return rc;
2576
2577 if (!mData->pMachineConfigFile->fileExists())
2578 // this is a new machine, and no config file exists yet:
2579 *aSettingsModified = TRUE;
2580 else
2581 *aSettingsModified = (mData->flModifications != 0);
2582
2583 return S_OK;
2584}
2585
2586HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2587{
2588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2589
2590 *aSessionState = mData->mSession.mState;
2591
2592 return S_OK;
2593}
2594
2595HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2596{
2597 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2598
2599 aSessionName = mData->mSession.mName;
2600
2601 return S_OK;
2602}
2603
2604HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2605{
2606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2607
2608 *aSessionPID = mData->mSession.mPID;
2609
2610 return S_OK;
2611}
2612
2613HRESULT Machine::getState(MachineState_T *aState)
2614{
2615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2616
2617 *aState = mData->mMachineState;
2618 Assert(mData->mMachineState != MachineState_Null);
2619
2620 return S_OK;
2621}
2622
2623HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2624{
2625 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2626
2627 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2628
2629 return S_OK;
2630}
2631
2632HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2633{
2634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2635
2636 aStateFilePath = mSSData->strStateFilePath;
2637
2638 return S_OK;
2639}
2640
2641HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2642{
2643 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2644
2645 i_getLogFolder(aLogFolder);
2646
2647 return S_OK;
2648}
2649
2650HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2651{
2652 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2653
2654 aCurrentSnapshot = mData->mCurrentSnapshot;
2655
2656 return S_OK;
2657}
2658
2659HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2660{
2661 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2662
2663 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2664 ? 0
2665 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2666
2667 return S_OK;
2668}
2669
2670HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2671{
2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2673
2674 /* Note: for machines with no snapshots, we always return FALSE
2675 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2676 * reasons :) */
2677
2678 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2679 ? FALSE
2680 : mData->mCurrentStateModified;
2681
2682 return S_OK;
2683}
2684
2685HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2686{
2687 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2688
2689 aSharedFolders.resize(mHWData->mSharedFolders.size());
2690 size_t i = 0;
2691 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2692 it = mHWData->mSharedFolders.begin();
2693 it != mHWData->mSharedFolders.end();
2694 ++it, ++i)
2695 aSharedFolders[i] = *it;
2696
2697 return S_OK;
2698}
2699
2700HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2701{
2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2703
2704 *aClipboardMode = mHWData->mClipboardMode;
2705
2706 return S_OK;
2707}
2708
2709HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2710{
2711 HRESULT rc = S_OK;
2712
2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2714
2715 alock.release();
2716 rc = i_onClipboardModeChange(aClipboardMode);
2717 alock.acquire();
2718 if (FAILED(rc)) return rc;
2719
2720 i_setModified(IsModified_MachineData);
2721 mHWData.backup();
2722 mHWData->mClipboardMode = aClipboardMode;
2723
2724 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2725 if (Global::IsOnline(mData->mMachineState))
2726 i_saveSettings(NULL);
2727
2728 return S_OK;
2729}
2730
2731HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2732{
2733 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2734
2735 *aDnDMode = mHWData->mDnDMode;
2736
2737 return S_OK;
2738}
2739
2740HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2741{
2742 HRESULT rc = S_OK;
2743
2744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2745
2746 alock.release();
2747 rc = i_onDnDModeChange(aDnDMode);
2748
2749 alock.acquire();
2750 if (FAILED(rc)) return rc;
2751
2752 i_setModified(IsModified_MachineData);
2753 mHWData.backup();
2754 mHWData->mDnDMode = aDnDMode;
2755
2756 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2757 if (Global::IsOnline(mData->mMachineState))
2758 i_saveSettings(NULL);
2759
2760 return S_OK;
2761}
2762
2763HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2764{
2765 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2766
2767 aStorageControllers.resize(mStorageControllers->size());
2768 size_t i = 0;
2769 for (StorageControllerList::const_iterator
2770 it = mStorageControllers->begin();
2771 it != mStorageControllers->end();
2772 ++it, ++i)
2773 aStorageControllers[i] = *it;
2774
2775 return S_OK;
2776}
2777
2778HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2779{
2780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2781
2782 *aEnabled = mUserData->s.fTeleporterEnabled;
2783
2784 return S_OK;
2785}
2786
2787HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2788{
2789 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2790
2791 /* Only allow it to be set to true when PoweredOff or Aborted.
2792 (Clearing it is always permitted.) */
2793 if ( aTeleporterEnabled
2794 && mData->mRegistered
2795 && ( !i_isSessionMachine()
2796 || ( mData->mMachineState != MachineState_PoweredOff
2797 && mData->mMachineState != MachineState_Teleported
2798 && mData->mMachineState != MachineState_Aborted
2799 )
2800 )
2801 )
2802 return setError(VBOX_E_INVALID_VM_STATE,
2803 tr("The machine is not powered off (state is %s)"),
2804 Global::stringifyMachineState(mData->mMachineState));
2805
2806 i_setModified(IsModified_MachineData);
2807 mUserData.backup();
2808 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2809
2810 return S_OK;
2811}
2812
2813HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2814{
2815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2816
2817 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2818
2819 return S_OK;
2820}
2821
2822HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2823{
2824 if (aTeleporterPort >= _64K)
2825 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2826
2827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2830 if (FAILED(rc)) return rc;
2831
2832 i_setModified(IsModified_MachineData);
2833 mUserData.backup();
2834 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2835
2836 return S_OK;
2837}
2838
2839HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2840{
2841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2842
2843 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2844
2845 return S_OK;
2846}
2847
2848HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2849{
2850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2851
2852 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2853 if (FAILED(rc)) return rc;
2854
2855 i_setModified(IsModified_MachineData);
2856 mUserData.backup();
2857 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2858
2859 return S_OK;
2860}
2861
2862HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2863{
2864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2865 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2866
2867 return S_OK;
2868}
2869
2870HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2871{
2872 /*
2873 * Hash the password first.
2874 */
2875 com::Utf8Str aT = aTeleporterPassword;
2876
2877 if (!aT.isEmpty())
2878 {
2879 if (VBoxIsPasswordHashed(&aT))
2880 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2881 VBoxHashPassword(&aT);
2882 }
2883
2884 /*
2885 * Do the update.
2886 */
2887 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2888 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2889 if (SUCCEEDED(hrc))
2890 {
2891 i_setModified(IsModified_MachineData);
2892 mUserData.backup();
2893 mUserData->s.strTeleporterPassword = aT;
2894 }
2895
2896 return hrc;
2897}
2898
2899HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2900{
2901 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2902
2903 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2904
2905 return S_OK;
2906}
2907
2908HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2909{
2910 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2911
2912 /* Only allow it to be set to true when PoweredOff or Aborted.
2913 (Clearing it is always permitted.) */
2914 if ( aRTCUseUTC
2915 && mData->mRegistered
2916 && ( !i_isSessionMachine()
2917 || ( mData->mMachineState != MachineState_PoweredOff
2918 && mData->mMachineState != MachineState_Teleported
2919 && mData->mMachineState != MachineState_Aborted
2920 )
2921 )
2922 )
2923 return setError(VBOX_E_INVALID_VM_STATE,
2924 tr("The machine is not powered off (state is %s)"),
2925 Global::stringifyMachineState(mData->mMachineState));
2926
2927 i_setModified(IsModified_MachineData);
2928 mUserData.backup();
2929 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2930
2931 return S_OK;
2932}
2933
2934HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2935{
2936 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2937
2938 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2939
2940 return S_OK;
2941}
2942
2943HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2944{
2945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2946
2947 HRESULT rc = i_checkStateDependency(MutableStateDep);
2948 if (FAILED(rc)) return rc;
2949
2950 i_setModified(IsModified_MachineData);
2951 mHWData.backup();
2952 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2953
2954 return S_OK;
2955}
2956
2957HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2958{
2959 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2960
2961 *aIOCacheSize = mHWData->mIOCacheSize;
2962
2963 return S_OK;
2964}
2965
2966HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2967{
2968 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2969
2970 HRESULT rc = i_checkStateDependency(MutableStateDep);
2971 if (FAILED(rc)) return rc;
2972
2973 i_setModified(IsModified_MachineData);
2974 mHWData.backup();
2975 mHWData->mIOCacheSize = aIOCacheSize;
2976
2977 return S_OK;
2978}
2979
2980
2981/**
2982 * @note Locks objects!
2983 */
2984HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2985 LockType_T aLockType)
2986{
2987 /* check the session state */
2988 SessionState_T state;
2989 HRESULT rc = aSession->COMGETTER(State)(&state);
2990 if (FAILED(rc)) return rc;
2991
2992 if (state != SessionState_Unlocked)
2993 return setError(VBOX_E_INVALID_OBJECT_STATE,
2994 tr("The given session is busy"));
2995
2996 // get the client's IInternalSessionControl interface
2997 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2998 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2999 E_INVALIDARG);
3000
3001 // session name (only used in some code paths)
3002 Utf8Str strSessionName;
3003
3004 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3005
3006 if (!mData->mRegistered)
3007 return setError(E_UNEXPECTED,
3008 tr("The machine '%s' is not registered"),
3009 mUserData->s.strName.c_str());
3010
3011 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3012
3013 SessionState_T oldState = mData->mSession.mState;
3014 /* Hack: in case the session is closing and there is a progress object
3015 * which allows waiting for the session to be closed, take the opportunity
3016 * and do a limited wait (max. 1 second). This helps a lot when the system
3017 * is busy and thus session closing can take a little while. */
3018 if ( mData->mSession.mState == SessionState_Unlocking
3019 && mData->mSession.mProgress)
3020 {
3021 alock.release();
3022 mData->mSession.mProgress->WaitForCompletion(1000);
3023 alock.acquire();
3024 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3025 }
3026
3027 // try again now
3028 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3029 // (i.e. session machine exists)
3030 && (aLockType == LockType_Shared) // caller wants a shared link to the
3031 // existing session that holds the write lock:
3032 )
3033 {
3034 // OK, share the session... we are now dealing with three processes:
3035 // 1) VBoxSVC (where this code runs);
3036 // 2) process C: the caller's client process (who wants a shared session);
3037 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3038
3039 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3040 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3041 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3042 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3043 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3044
3045 /*
3046 * Release the lock before calling the client process. It's safe here
3047 * since the only thing to do after we get the lock again is to add
3048 * the remote control to the list (which doesn't directly influence
3049 * anything).
3050 */
3051 alock.release();
3052
3053 // get the console of the session holding the write lock (this is a remote call)
3054 ComPtr<IConsole> pConsoleW;
3055 if (mData->mSession.mLockType == LockType_VM)
3056 {
3057 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3058 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3059 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3060 if (FAILED(rc))
3061 // the failure may occur w/o any error info (from RPC), so provide one
3062 return setError(VBOX_E_VM_ERROR,
3063 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3064 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3065 }
3066
3067 // share the session machine and W's console with the caller's session
3068 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3069 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3070 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3071
3072 if (FAILED(rc))
3073 // the failure may occur w/o any error info (from RPC), so provide one
3074 return setError(VBOX_E_VM_ERROR,
3075 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3076 alock.acquire();
3077
3078 // need to revalidate the state after acquiring the lock again
3079 if (mData->mSession.mState != SessionState_Locked)
3080 {
3081 pSessionControl->Uninitialize();
3082 return setError(VBOX_E_INVALID_SESSION_STATE,
3083 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3084 mUserData->s.strName.c_str());
3085 }
3086
3087 // add the caller's session to the list
3088 mData->mSession.mRemoteControls.push_back(pSessionControl);
3089 }
3090 else if ( mData->mSession.mState == SessionState_Locked
3091 || mData->mSession.mState == SessionState_Unlocking
3092 )
3093 {
3094 // sharing not permitted, or machine still unlocking:
3095 return setError(VBOX_E_INVALID_OBJECT_STATE,
3096 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3097 mUserData->s.strName.c_str());
3098 }
3099 else
3100 {
3101 // machine is not locked: then write-lock the machine (create the session machine)
3102
3103 // must not be busy
3104 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3105
3106 // get the caller's session PID
3107 RTPROCESS pid = NIL_RTPROCESS;
3108 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3109 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3110 Assert(pid != NIL_RTPROCESS);
3111
3112 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3113
3114 if (fLaunchingVMProcess)
3115 {
3116 if (mData->mSession.mPID == NIL_RTPROCESS)
3117 {
3118 // two or more clients racing for a lock, the one which set the
3119 // session state to Spawning will win, the others will get an
3120 // error as we can't decide here if waiting a little would help
3121 // (only for shared locks this would avoid an error)
3122 return setError(VBOX_E_INVALID_OBJECT_STATE,
3123 tr("The machine '%s' already has a lock request pending"),
3124 mUserData->s.strName.c_str());
3125 }
3126
3127 // this machine is awaiting for a spawning session to be opened:
3128 // then the calling process must be the one that got started by
3129 // LaunchVMProcess()
3130
3131 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3132 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3133
3134#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3135 /* Hardened windows builds spawns three processes when a VM is
3136 launched, the 3rd one is the one that will end up here. */
3137 RTPROCESS ppid;
3138 int rc = RTProcQueryParent(pid, &ppid);
3139 if (RT_SUCCESS(rc))
3140 rc = RTProcQueryParent(ppid, &ppid);
3141 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3142 || rc == VERR_ACCESS_DENIED)
3143 {
3144 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3145 mData->mSession.mPID = pid;
3146 }
3147#endif
3148
3149 if (mData->mSession.mPID != pid)
3150 return setError(E_ACCESSDENIED,
3151 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3152 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3153 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3154 }
3155
3156 // create the mutable SessionMachine from the current machine
3157 ComObjPtr<SessionMachine> sessionMachine;
3158 sessionMachine.createObject();
3159 rc = sessionMachine->init(this);
3160 AssertComRC(rc);
3161
3162 /* NOTE: doing return from this function after this point but
3163 * before the end is forbidden since it may call SessionMachine::uninit()
3164 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3165 * lock while still holding the Machine lock in alock so that a deadlock
3166 * is possible due to the wrong lock order. */
3167
3168 if (SUCCEEDED(rc))
3169 {
3170 /*
3171 * Set the session state to Spawning to protect against subsequent
3172 * attempts to open a session and to unregister the machine after
3173 * we release the lock.
3174 */
3175 SessionState_T origState = mData->mSession.mState;
3176 mData->mSession.mState = SessionState_Spawning;
3177
3178#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3179 /* Get the client token ID to be passed to the client process */
3180 Utf8Str strTokenId;
3181 sessionMachine->i_getTokenId(strTokenId);
3182 Assert(!strTokenId.isEmpty());
3183#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3184 /* Get the client token to be passed to the client process */
3185 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3186 /* The token is now "owned" by pToken, fix refcount */
3187 if (!pToken.isNull())
3188 pToken->Release();
3189#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3190
3191 /*
3192 * Release the lock before calling the client process -- it will call
3193 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3194 * because the state is Spawning, so that LaunchVMProcess() and
3195 * LockMachine() calls will fail. This method, called before we
3196 * acquire the lock again, will fail because of the wrong PID.
3197 *
3198 * Note that mData->mSession.mRemoteControls accessed outside
3199 * the lock may not be modified when state is Spawning, so it's safe.
3200 */
3201 alock.release();
3202
3203 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3204#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3205 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3206#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3207 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3208 /* Now the token is owned by the client process. */
3209 pToken.setNull();
3210#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3211 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3212
3213 /* The failure may occur w/o any error info (from RPC), so provide one */
3214 if (FAILED(rc))
3215 setError(VBOX_E_VM_ERROR,
3216 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3217
3218 // get session name, either to remember or to compare against
3219 // the already known session name.
3220 {
3221 Bstr bstrSessionName;
3222 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3223 if (SUCCEEDED(rc2))
3224 strSessionName = bstrSessionName;
3225 }
3226
3227 if ( SUCCEEDED(rc)
3228 && fLaunchingVMProcess
3229 )
3230 {
3231 /* complete the remote session initialization */
3232
3233 /* get the console from the direct session */
3234 ComPtr<IConsole> console;
3235 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3236 ComAssertComRC(rc);
3237
3238 if (SUCCEEDED(rc) && !console)
3239 {
3240 ComAssert(!!console);
3241 rc = E_FAIL;
3242 }
3243
3244 /* assign machine & console to the remote session */
3245 if (SUCCEEDED(rc))
3246 {
3247 /*
3248 * after LaunchVMProcess(), the first and the only
3249 * entry in remoteControls is that remote session
3250 */
3251 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3252 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3253 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3254
3255 /* The failure may occur w/o any error info (from RPC), so provide one */
3256 if (FAILED(rc))
3257 setError(VBOX_E_VM_ERROR,
3258 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3259 }
3260
3261 if (FAILED(rc))
3262 pSessionControl->Uninitialize();
3263 }
3264
3265 /* acquire the lock again */
3266 alock.acquire();
3267
3268 /* Restore the session state */
3269 mData->mSession.mState = origState;
3270 }
3271
3272 // finalize spawning anyway (this is why we don't return on errors above)
3273 if (fLaunchingVMProcess)
3274 {
3275 Assert(mData->mSession.mName == strSessionName);
3276 /* Note that the progress object is finalized later */
3277 /** @todo Consider checking mData->mSession.mProgress for cancellation
3278 * around here. */
3279
3280 /* We don't reset mSession.mPID here because it is necessary for
3281 * SessionMachine::uninit() to reap the child process later. */
3282
3283 if (FAILED(rc))
3284 {
3285 /* Close the remote session, remove the remote control from the list
3286 * and reset session state to Closed (@note keep the code in sync
3287 * with the relevant part in checkForSpawnFailure()). */
3288
3289 Assert(mData->mSession.mRemoteControls.size() == 1);
3290 if (mData->mSession.mRemoteControls.size() == 1)
3291 {
3292 ErrorInfoKeeper eik;
3293 mData->mSession.mRemoteControls.front()->Uninitialize();
3294 }
3295
3296 mData->mSession.mRemoteControls.clear();
3297 mData->mSession.mState = SessionState_Unlocked;
3298 }
3299 }
3300 else
3301 {
3302 /* memorize PID of the directly opened session */
3303 if (SUCCEEDED(rc))
3304 mData->mSession.mPID = pid;
3305 }
3306
3307 if (SUCCEEDED(rc))
3308 {
3309 mData->mSession.mLockType = aLockType;
3310 /* memorize the direct session control and cache IUnknown for it */
3311 mData->mSession.mDirectControl = pSessionControl;
3312 mData->mSession.mState = SessionState_Locked;
3313 if (!fLaunchingVMProcess)
3314 mData->mSession.mName = strSessionName;
3315 /* associate the SessionMachine with this Machine */
3316 mData->mSession.mMachine = sessionMachine;
3317
3318 /* request an IUnknown pointer early from the remote party for later
3319 * identity checks (it will be internally cached within mDirectControl
3320 * at least on XPCOM) */
3321 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3322 NOREF(unk);
3323 }
3324
3325 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3326 * would break the lock order */
3327 alock.release();
3328
3329 /* uninitialize the created session machine on failure */
3330 if (FAILED(rc))
3331 sessionMachine->uninit();
3332 }
3333
3334 if (SUCCEEDED(rc))
3335 {
3336 /*
3337 * tell the client watcher thread to update the set of
3338 * machines that have open sessions
3339 */
3340 mParent->i_updateClientWatcher();
3341
3342 if (oldState != SessionState_Locked)
3343 /* fire an event */
3344 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3345 }
3346
3347 return rc;
3348}
3349
3350/**
3351 * @note Locks objects!
3352 */
3353HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3354 const com::Utf8Str &aName,
3355 const com::Utf8Str &aEnvironment,
3356 ComPtr<IProgress> &aProgress)
3357{
3358 Utf8Str strFrontend(aName);
3359 /* "emergencystop" doesn't need the session, so skip the checks/interface
3360 * retrieval. This code doesn't quite fit in here, but introducing a
3361 * special API method would be even more effort, and would require explicit
3362 * support by every API client. It's better to hide the feature a bit. */
3363 if (strFrontend != "emergencystop")
3364 CheckComArgNotNull(aSession);
3365
3366 HRESULT rc = S_OK;
3367 if (strFrontend.isEmpty())
3368 {
3369 Bstr bstrFrontend;
3370 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3371 if (FAILED(rc))
3372 return rc;
3373 strFrontend = bstrFrontend;
3374 if (strFrontend.isEmpty())
3375 {
3376 ComPtr<ISystemProperties> systemProperties;
3377 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3378 if (FAILED(rc))
3379 return rc;
3380 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3381 if (FAILED(rc))
3382 return rc;
3383 strFrontend = bstrFrontend;
3384 }
3385 /* paranoia - emergencystop is not a valid default */
3386 if (strFrontend == "emergencystop")
3387 strFrontend = Utf8Str::Empty;
3388 }
3389 /* default frontend: Qt GUI */
3390 if (strFrontend.isEmpty())
3391 strFrontend = "GUI/Qt";
3392
3393 if (strFrontend != "emergencystop")
3394 {
3395 /* check the session state */
3396 SessionState_T state;
3397 rc = aSession->COMGETTER(State)(&state);
3398 if (FAILED(rc))
3399 return rc;
3400
3401 if (state != SessionState_Unlocked)
3402 return setError(VBOX_E_INVALID_OBJECT_STATE,
3403 tr("The given session is busy"));
3404
3405 /* get the IInternalSessionControl interface */
3406 ComPtr<IInternalSessionControl> control(aSession);
3407 ComAssertMsgRet(!control.isNull(),
3408 ("No IInternalSessionControl interface"),
3409 E_INVALIDARG);
3410
3411 /* get the teleporter enable state for the progress object init. */
3412 BOOL fTeleporterEnabled;
3413 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3414 if (FAILED(rc))
3415 return rc;
3416
3417 /* create a progress object */
3418 ComObjPtr<ProgressProxy> progress;
3419 progress.createObject();
3420 rc = progress->init(mParent,
3421 static_cast<IMachine*>(this),
3422 Bstr(tr("Starting VM")).raw(),
3423 TRUE /* aCancelable */,
3424 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3425 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3426 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3427 2 /* uFirstOperationWeight */,
3428 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3429
3430 if (SUCCEEDED(rc))
3431 {
3432 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3433 if (SUCCEEDED(rc))
3434 {
3435 aProgress = progress;
3436
3437 /* signal the client watcher thread */
3438 mParent->i_updateClientWatcher();
3439
3440 /* fire an event */
3441 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3442 }
3443 }
3444 }
3445 else
3446 {
3447 /* no progress object - either instant success or failure */
3448 aProgress = NULL;
3449
3450 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3451
3452 if (mData->mSession.mState != SessionState_Locked)
3453 return setError(VBOX_E_INVALID_OBJECT_STATE,
3454 tr("The machine '%s' is not locked by a session"),
3455 mUserData->s.strName.c_str());
3456
3457 /* must have a VM process associated - do not kill normal API clients
3458 * with an open session */
3459 if (!Global::IsOnline(mData->mMachineState))
3460 return setError(VBOX_E_INVALID_OBJECT_STATE,
3461 tr("The machine '%s' does not have a VM process"),
3462 mUserData->s.strName.c_str());
3463
3464 /* forcibly terminate the VM process */
3465 if (mData->mSession.mPID != NIL_RTPROCESS)
3466 RTProcTerminate(mData->mSession.mPID);
3467
3468 /* signal the client watcher thread, as most likely the client has
3469 * been terminated */
3470 mParent->i_updateClientWatcher();
3471 }
3472
3473 return rc;
3474}
3475
3476HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3477{
3478 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3479 return setError(E_INVALIDARG,
3480 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3481 aPosition, SchemaDefs::MaxBootPosition);
3482
3483 if (aDevice == DeviceType_USB)
3484 return setError(E_NOTIMPL,
3485 tr("Booting from USB device is currently not supported"));
3486
3487 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3488
3489 HRESULT rc = i_checkStateDependency(MutableStateDep);
3490 if (FAILED(rc)) return rc;
3491
3492 i_setModified(IsModified_MachineData);
3493 mHWData.backup();
3494 mHWData->mBootOrder[aPosition - 1] = aDevice;
3495
3496 return S_OK;
3497}
3498
3499HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3500{
3501 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3502 return setError(E_INVALIDARG,
3503 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3504 aPosition, SchemaDefs::MaxBootPosition);
3505
3506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3507
3508 *aDevice = mHWData->mBootOrder[aPosition - 1];
3509
3510 return S_OK;
3511}
3512
3513HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3514 LONG aControllerPort,
3515 LONG aDevice,
3516 DeviceType_T aType,
3517 const ComPtr<IMedium> &aMedium)
3518{
3519 IMedium *aM = aMedium;
3520 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3521 aName.c_str(), aControllerPort, aDevice, aType, aM));
3522
3523 // request the host lock first, since might be calling Host methods for getting host drives;
3524 // next, protect the media tree all the while we're in here, as well as our member variables
3525 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3526 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3527
3528 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3529 if (FAILED(rc)) return rc;
3530
3531 /// @todo NEWMEDIA implicit machine registration
3532 if (!mData->mRegistered)
3533 return setError(VBOX_E_INVALID_OBJECT_STATE,
3534 tr("Cannot attach storage devices to an unregistered machine"));
3535
3536 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3537
3538 /* Check for an existing controller. */
3539 ComObjPtr<StorageController> ctl;
3540 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3541 if (FAILED(rc)) return rc;
3542
3543 StorageControllerType_T ctrlType;
3544 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3545 if (FAILED(rc))
3546 return setError(E_FAIL,
3547 tr("Could not get type of controller '%s'"),
3548 aName.c_str());
3549
3550 bool fSilent = false;
3551 Utf8Str strReconfig;
3552
3553 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3554 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3555 if ( mData->mMachineState == MachineState_Paused
3556 && strReconfig == "1")
3557 fSilent = true;
3558
3559 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3560 bool fHotplug = false;
3561 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3562 fHotplug = true;
3563
3564 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3565 return setError(VBOX_E_INVALID_VM_STATE,
3566 tr("Controller '%s' does not support hotplugging"),
3567 aName.c_str());
3568
3569 // check that the port and device are not out of range
3570 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3571 if (FAILED(rc)) return rc;
3572
3573 /* check if the device slot is already busy */
3574 MediumAttachment *pAttachTemp;
3575 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3576 aName,
3577 aControllerPort,
3578 aDevice)))
3579 {
3580 Medium *pMedium = pAttachTemp->i_getMedium();
3581 if (pMedium)
3582 {
3583 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3584 return setError(VBOX_E_OBJECT_IN_USE,
3585 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3586 pMedium->i_getLocationFull().c_str(),
3587 aControllerPort,
3588 aDevice,
3589 aName.c_str());
3590 }
3591 else
3592 return setError(VBOX_E_OBJECT_IN_USE,
3593 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3594 aControllerPort, aDevice, aName.c_str());
3595 }
3596
3597 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3598 if (aMedium && medium.isNull())
3599 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3600
3601 AutoCaller mediumCaller(medium);
3602 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3603
3604 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3605
3606 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3607 && !medium.isNull()
3608 )
3609 return setError(VBOX_E_OBJECT_IN_USE,
3610 tr("Medium '%s' is already attached to this virtual machine"),
3611 medium->i_getLocationFull().c_str());
3612
3613 if (!medium.isNull())
3614 {
3615 MediumType_T mtype = medium->i_getType();
3616 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3617 // For DVDs it's not written to the config file, so needs no global config
3618 // version bump. For floppies it's a new attribute "type", which is ignored
3619 // by older VirtualBox version, so needs no global config version bump either.
3620 // For hard disks this type is not accepted.
3621 if (mtype == MediumType_MultiAttach)
3622 {
3623 // This type is new with VirtualBox 4.0 and therefore requires settings
3624 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3625 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3626 // two reasons: The medium type is a property of the media registry tree, which
3627 // can reside in the global config file (for pre-4.0 media); we would therefore
3628 // possibly need to bump the global config version. We don't want to do that though
3629 // because that might make downgrading to pre-4.0 impossible.
3630 // As a result, we can only use these two new types if the medium is NOT in the
3631 // global registry:
3632 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3633 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3634 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3635 )
3636 return setError(VBOX_E_INVALID_OBJECT_STATE,
3637 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3638 "to machines that were created with VirtualBox 4.0 or later"),
3639 medium->i_getLocationFull().c_str());
3640 }
3641 }
3642
3643 bool fIndirect = false;
3644 if (!medium.isNull())
3645 fIndirect = medium->i_isReadOnly();
3646 bool associate = true;
3647
3648 do
3649 {
3650 if ( aType == DeviceType_HardDisk
3651 && mMediumAttachments.isBackedUp())
3652 {
3653 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3654
3655 /* check if the medium was attached to the VM before we started
3656 * changing attachments in which case the attachment just needs to
3657 * be restored */
3658 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3659 {
3660 AssertReturn(!fIndirect, E_FAIL);
3661
3662 /* see if it's the same bus/channel/device */
3663 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3664 {
3665 /* the simplest case: restore the whole attachment
3666 * and return, nothing else to do */
3667 mMediumAttachments->push_back(pAttachTemp);
3668
3669 /* Reattach the medium to the VM. */
3670 if (fHotplug || fSilent)
3671 {
3672 mediumLock.release();
3673 treeLock.release();
3674 alock.release();
3675
3676 MediumLockList *pMediumLockList(new MediumLockList());
3677
3678 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3679 medium /* pToLockWrite */,
3680 false /* fMediumLockWriteAll */,
3681 NULL,
3682 *pMediumLockList);
3683 alock.acquire();
3684 if (FAILED(rc))
3685 delete pMediumLockList;
3686 else
3687 {
3688 mData->mSession.mLockedMedia.Unlock();
3689 alock.release();
3690 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3691 mData->mSession.mLockedMedia.Lock();
3692 alock.acquire();
3693 }
3694 alock.release();
3695
3696 if (SUCCEEDED(rc))
3697 {
3698 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3699 /* Remove lock list in case of error. */
3700 if (FAILED(rc))
3701 {
3702 mData->mSession.mLockedMedia.Unlock();
3703 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3704 mData->mSession.mLockedMedia.Lock();
3705 }
3706 }
3707 }
3708
3709 return S_OK;
3710 }
3711
3712 /* bus/channel/device differ; we need a new attachment object,
3713 * but don't try to associate it again */
3714 associate = false;
3715 break;
3716 }
3717 }
3718
3719 /* go further only if the attachment is to be indirect */
3720 if (!fIndirect)
3721 break;
3722
3723 /* perform the so called smart attachment logic for indirect
3724 * attachments. Note that smart attachment is only applicable to base
3725 * hard disks. */
3726
3727 if (medium->i_getParent().isNull())
3728 {
3729 /* first, investigate the backup copy of the current hard disk
3730 * attachments to make it possible to re-attach existing diffs to
3731 * another device slot w/o losing their contents */
3732 if (mMediumAttachments.isBackedUp())
3733 {
3734 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3735
3736 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3737 uint32_t foundLevel = 0;
3738
3739 for (MediumAttachmentList::const_iterator
3740 it = oldAtts.begin();
3741 it != oldAtts.end();
3742 ++it)
3743 {
3744 uint32_t level = 0;
3745 MediumAttachment *pAttach = *it;
3746 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3747 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3748 if (pMedium.isNull())
3749 continue;
3750
3751 if (pMedium->i_getBase(&level) == medium)
3752 {
3753 /* skip the hard disk if its currently attached (we
3754 * cannot attach the same hard disk twice) */
3755 if (i_findAttachment(*mMediumAttachments.data(),
3756 pMedium))
3757 continue;
3758
3759 /* matched device, channel and bus (i.e. attached to the
3760 * same place) will win and immediately stop the search;
3761 * otherwise the attachment that has the youngest
3762 * descendant of medium will be used
3763 */
3764 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3765 {
3766 /* the simplest case: restore the whole attachment
3767 * and return, nothing else to do */
3768 mMediumAttachments->push_back(*it);
3769
3770 /* Reattach the medium to the VM. */
3771 if (fHotplug || fSilent)
3772 {
3773 mediumLock.release();
3774 treeLock.release();
3775 alock.release();
3776
3777 MediumLockList *pMediumLockList(new MediumLockList());
3778
3779 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3780 medium /* pToLockWrite */,
3781 false /* fMediumLockWriteAll */,
3782 NULL,
3783 *pMediumLockList);
3784 alock.acquire();
3785 if (FAILED(rc))
3786 delete pMediumLockList;
3787 else
3788 {
3789 mData->mSession.mLockedMedia.Unlock();
3790 alock.release();
3791 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3792 mData->mSession.mLockedMedia.Lock();
3793 alock.acquire();
3794 }
3795 alock.release();
3796
3797 if (SUCCEEDED(rc))
3798 {
3799 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3800 /* Remove lock list in case of error. */
3801 if (FAILED(rc))
3802 {
3803 mData->mSession.mLockedMedia.Unlock();
3804 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3805 mData->mSession.mLockedMedia.Lock();
3806 }
3807 }
3808 }
3809
3810 return S_OK;
3811 }
3812 else if ( foundIt == oldAtts.end()
3813 || level > foundLevel /* prefer younger */
3814 )
3815 {
3816 foundIt = it;
3817 foundLevel = level;
3818 }
3819 }
3820 }
3821
3822 if (foundIt != oldAtts.end())
3823 {
3824 /* use the previously attached hard disk */
3825 medium = (*foundIt)->i_getMedium();
3826 mediumCaller.attach(medium);
3827 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3828 mediumLock.attach(medium);
3829 /* not implicit, doesn't require association with this VM */
3830 fIndirect = false;
3831 associate = false;
3832 /* go right to the MediumAttachment creation */
3833 break;
3834 }
3835 }
3836
3837 /* must give up the medium lock and medium tree lock as below we
3838 * go over snapshots, which needs a lock with higher lock order. */
3839 mediumLock.release();
3840 treeLock.release();
3841
3842 /* then, search through snapshots for the best diff in the given
3843 * hard disk's chain to base the new diff on */
3844
3845 ComObjPtr<Medium> base;
3846 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3847 while (snap)
3848 {
3849 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3850
3851 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3852
3853 MediumAttachment *pAttachFound = NULL;
3854 uint32_t foundLevel = 0;
3855
3856 for (MediumAttachmentList::const_iterator
3857 it = snapAtts.begin();
3858 it != snapAtts.end();
3859 ++it)
3860 {
3861 MediumAttachment *pAttach = *it;
3862 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3863 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3864 if (pMedium.isNull())
3865 continue;
3866
3867 uint32_t level = 0;
3868 if (pMedium->i_getBase(&level) == medium)
3869 {
3870 /* matched device, channel and bus (i.e. attached to the
3871 * same place) will win and immediately stop the search;
3872 * otherwise the attachment that has the youngest
3873 * descendant of medium will be used
3874 */
3875 if ( pAttach->i_getDevice() == aDevice
3876 && pAttach->i_getPort() == aControllerPort
3877 && pAttach->i_getControllerName() == aName
3878 )
3879 {
3880 pAttachFound = pAttach;
3881 break;
3882 }
3883 else if ( !pAttachFound
3884 || level > foundLevel /* prefer younger */
3885 )
3886 {
3887 pAttachFound = pAttach;
3888 foundLevel = level;
3889 }
3890 }
3891 }
3892
3893 if (pAttachFound)
3894 {
3895 base = pAttachFound->i_getMedium();
3896 break;
3897 }
3898
3899 snap = snap->i_getParent();
3900 }
3901
3902 /* re-lock medium tree and the medium, as we need it below */
3903 treeLock.acquire();
3904 mediumLock.acquire();
3905
3906 /* found a suitable diff, use it as a base */
3907 if (!base.isNull())
3908 {
3909 medium = base;
3910 mediumCaller.attach(medium);
3911 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3912 mediumLock.attach(medium);
3913 }
3914 }
3915
3916 Utf8Str strFullSnapshotFolder;
3917 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3918
3919 ComObjPtr<Medium> diff;
3920 diff.createObject();
3921 // store this diff in the same registry as the parent
3922 Guid uuidRegistryParent;
3923 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3924 {
3925 // parent image has no registry: this can happen if we're attaching a new immutable
3926 // image that has not yet been attached (medium then points to the base and we're
3927 // creating the diff image for the immutable, and the parent is not yet registered);
3928 // put the parent in the machine registry then
3929 mediumLock.release();
3930 treeLock.release();
3931 alock.release();
3932 i_addMediumToRegistry(medium);
3933 alock.acquire();
3934 treeLock.acquire();
3935 mediumLock.acquire();
3936 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3937 }
3938 rc = diff->init(mParent,
3939 medium->i_getPreferredDiffFormat(),
3940 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3941 uuidRegistryParent,
3942 DeviceType_HardDisk);
3943 if (FAILED(rc)) return rc;
3944
3945 /* Apply the normal locking logic to the entire chain. */
3946 MediumLockList *pMediumLockList(new MediumLockList());
3947 mediumLock.release();
3948 treeLock.release();
3949 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3950 diff /* pToLockWrite */,
3951 false /* fMediumLockWriteAll */,
3952 medium,
3953 *pMediumLockList);
3954 treeLock.acquire();
3955 mediumLock.acquire();
3956 if (SUCCEEDED(rc))
3957 {
3958 mediumLock.release();
3959 treeLock.release();
3960 rc = pMediumLockList->Lock();
3961 treeLock.acquire();
3962 mediumLock.acquire();
3963 if (FAILED(rc))
3964 setError(rc,
3965 tr("Could not lock medium when creating diff '%s'"),
3966 diff->i_getLocationFull().c_str());
3967 else
3968 {
3969 /* will release the lock before the potentially lengthy
3970 * operation, so protect with the special state */
3971 MachineState_T oldState = mData->mMachineState;
3972 i_setMachineState(MachineState_SettingUp);
3973
3974 mediumLock.release();
3975 treeLock.release();
3976 alock.release();
3977
3978 rc = medium->i_createDiffStorage(diff,
3979 medium->i_getPreferredDiffVariant(),
3980 pMediumLockList,
3981 NULL /* aProgress */,
3982 true /* aWait */,
3983 false /* aNotify */);
3984
3985 alock.acquire();
3986 treeLock.acquire();
3987 mediumLock.acquire();
3988
3989 i_setMachineState(oldState);
3990 }
3991 }
3992
3993 /* Unlock the media and free the associated memory. */
3994 delete pMediumLockList;
3995
3996 if (FAILED(rc)) return rc;
3997
3998 /* use the created diff for the actual attachment */
3999 medium = diff;
4000 mediumCaller.attach(medium);
4001 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4002 mediumLock.attach(medium);
4003 }
4004 while (0);
4005
4006 ComObjPtr<MediumAttachment> attachment;
4007 attachment.createObject();
4008 rc = attachment->init(this,
4009 medium,
4010 aName,
4011 aControllerPort,
4012 aDevice,
4013 aType,
4014 fIndirect,
4015 false /* fPassthrough */,
4016 false /* fTempEject */,
4017 false /* fNonRotational */,
4018 false /* fDiscard */,
4019 fHotplug /* fHotPluggable */,
4020 Utf8Str::Empty);
4021 if (FAILED(rc)) return rc;
4022
4023 if (associate && !medium.isNull())
4024 {
4025 // as the last step, associate the medium to the VM
4026 rc = medium->i_addBackReference(mData->mUuid);
4027 // here we can fail because of Deleting, or being in process of creating a Diff
4028 if (FAILED(rc)) return rc;
4029
4030 mediumLock.release();
4031 treeLock.release();
4032 alock.release();
4033 i_addMediumToRegistry(medium);
4034 alock.acquire();
4035 treeLock.acquire();
4036 mediumLock.acquire();
4037 }
4038
4039 /* success: finally remember the attachment */
4040 i_setModified(IsModified_Storage);
4041 mMediumAttachments.backup();
4042 mMediumAttachments->push_back(attachment);
4043
4044 mediumLock.release();
4045 treeLock.release();
4046 alock.release();
4047
4048 if (fHotplug || fSilent)
4049 {
4050 if (!medium.isNull())
4051 {
4052 MediumLockList *pMediumLockList(new MediumLockList());
4053
4054 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4055 medium /* pToLockWrite */,
4056 false /* fMediumLockWriteAll */,
4057 NULL,
4058 *pMediumLockList);
4059 alock.acquire();
4060 if (FAILED(rc))
4061 delete pMediumLockList;
4062 else
4063 {
4064 mData->mSession.mLockedMedia.Unlock();
4065 alock.release();
4066 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4067 mData->mSession.mLockedMedia.Lock();
4068 alock.acquire();
4069 }
4070 alock.release();
4071 }
4072
4073 if (SUCCEEDED(rc))
4074 {
4075 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4076 /* Remove lock list in case of error. */
4077 if (FAILED(rc))
4078 {
4079 mData->mSession.mLockedMedia.Unlock();
4080 mData->mSession.mLockedMedia.Remove(attachment);
4081 mData->mSession.mLockedMedia.Lock();
4082 }
4083 }
4084 }
4085
4086 /* Save modified registries, but skip this machine as it's the caller's
4087 * job to save its settings like all other settings changes. */
4088 mParent->i_unmarkRegistryModified(i_getId());
4089 mParent->i_saveModifiedRegistries();
4090
4091 if (SUCCEEDED(rc))
4092 {
4093 if (fIndirect && medium != aM)
4094 mParent->i_onMediumConfigChanged(medium);
4095 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4096 }
4097
4098 return rc;
4099}
4100
4101HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4102 LONG aDevice)
4103{
4104 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4105 aName.c_str(), aControllerPort, aDevice));
4106
4107 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4108
4109 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4110 if (FAILED(rc)) return rc;
4111
4112 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4113
4114 /* Check for an existing controller. */
4115 ComObjPtr<StorageController> ctl;
4116 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4117 if (FAILED(rc)) return rc;
4118
4119 StorageControllerType_T ctrlType;
4120 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4121 if (FAILED(rc))
4122 return setError(E_FAIL,
4123 tr("Could not get type of controller '%s'"),
4124 aName.c_str());
4125
4126 bool fSilent = false;
4127 Utf8Str strReconfig;
4128
4129 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4130 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4131 if ( mData->mMachineState == MachineState_Paused
4132 && strReconfig == "1")
4133 fSilent = true;
4134
4135 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4136 bool fHotplug = false;
4137 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4138 fHotplug = true;
4139
4140 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4141 return setError(VBOX_E_INVALID_VM_STATE,
4142 tr("Controller '%s' does not support hotplugging"),
4143 aName.c_str());
4144
4145 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4146 aName,
4147 aControllerPort,
4148 aDevice);
4149 if (!pAttach)
4150 return setError(VBOX_E_OBJECT_NOT_FOUND,
4151 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4152 aDevice, aControllerPort, aName.c_str());
4153
4154 if (fHotplug && !pAttach->i_getHotPluggable())
4155 return setError(VBOX_E_NOT_SUPPORTED,
4156 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4157 aDevice, aControllerPort, aName.c_str());
4158
4159 /*
4160 * The VM has to detach the device before we delete any implicit diffs.
4161 * If this fails we can roll back without loosing data.
4162 */
4163 if (fHotplug || fSilent)
4164 {
4165 alock.release();
4166 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4167 alock.acquire();
4168 }
4169 if (FAILED(rc)) return rc;
4170
4171 /* If we are here everything went well and we can delete the implicit now. */
4172 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4173
4174 alock.release();
4175
4176 /* Save modified registries, but skip this machine as it's the caller's
4177 * job to save its settings like all other settings changes. */
4178 mParent->i_unmarkRegistryModified(i_getId());
4179 mParent->i_saveModifiedRegistries();
4180
4181 if (SUCCEEDED(rc))
4182 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4183
4184 return rc;
4185}
4186
4187HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4188 LONG aDevice, BOOL aPassthrough)
4189{
4190 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4191 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4192
4193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4194
4195 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4196 if (FAILED(rc)) return rc;
4197
4198 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4199
4200 /* Check for an existing controller. */
4201 ComObjPtr<StorageController> ctl;
4202 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4203 if (FAILED(rc)) return rc;
4204
4205 StorageControllerType_T ctrlType;
4206 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4207 if (FAILED(rc))
4208 return setError(E_FAIL,
4209 tr("Could not get type of controller '%s'"),
4210 aName.c_str());
4211
4212 bool fSilent = false;
4213 Utf8Str strReconfig;
4214
4215 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4216 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4217 if ( mData->mMachineState == MachineState_Paused
4218 && strReconfig == "1")
4219 fSilent = true;
4220
4221 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4222 bool fHotplug = false;
4223 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4224 fHotplug = true;
4225
4226 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4227 return setError(VBOX_E_INVALID_VM_STATE,
4228 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4229 aName.c_str());
4230
4231 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4232 aName,
4233 aControllerPort,
4234 aDevice);
4235 if (!pAttach)
4236 return setError(VBOX_E_OBJECT_NOT_FOUND,
4237 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4238 aDevice, aControllerPort, aName.c_str());
4239
4240
4241 i_setModified(IsModified_Storage);
4242 mMediumAttachments.backup();
4243
4244 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4245
4246 if (pAttach->i_getType() != DeviceType_DVD)
4247 return setError(E_INVALIDARG,
4248 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4249 aDevice, aControllerPort, aName.c_str());
4250
4251 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4252
4253 pAttach->i_updatePassthrough(!!aPassthrough);
4254
4255 attLock.release();
4256 alock.release();
4257 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4258 if (SUCCEEDED(rc) && fValueChanged)
4259 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4260
4261 return rc;
4262}
4263
4264HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4265 LONG aDevice, BOOL aTemporaryEject)
4266{
4267
4268 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4269 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4270
4271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4272
4273 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4274 if (FAILED(rc)) return rc;
4275
4276 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4277 aName,
4278 aControllerPort,
4279 aDevice);
4280 if (!pAttach)
4281 return setError(VBOX_E_OBJECT_NOT_FOUND,
4282 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4283 aDevice, aControllerPort, aName.c_str());
4284
4285
4286 i_setModified(IsModified_Storage);
4287 mMediumAttachments.backup();
4288
4289 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4290
4291 if (pAttach->i_getType() != DeviceType_DVD)
4292 return setError(E_INVALIDARG,
4293 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4294 aDevice, aControllerPort, aName.c_str());
4295 pAttach->i_updateTempEject(!!aTemporaryEject);
4296
4297 return S_OK;
4298}
4299
4300HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4301 LONG aDevice, BOOL aNonRotational)
4302{
4303
4304 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4305 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4306
4307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4308
4309 HRESULT rc = i_checkStateDependency(MutableStateDep);
4310 if (FAILED(rc)) return rc;
4311
4312 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4313
4314 if (Global::IsOnlineOrTransient(mData->mMachineState))
4315 return setError(VBOX_E_INVALID_VM_STATE,
4316 tr("Invalid machine state: %s"),
4317 Global::stringifyMachineState(mData->mMachineState));
4318
4319 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4320 aName,
4321 aControllerPort,
4322 aDevice);
4323 if (!pAttach)
4324 return setError(VBOX_E_OBJECT_NOT_FOUND,
4325 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4326 aDevice, aControllerPort, aName.c_str());
4327
4328
4329 i_setModified(IsModified_Storage);
4330 mMediumAttachments.backup();
4331
4332 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4333
4334 if (pAttach->i_getType() != DeviceType_HardDisk)
4335 return setError(E_INVALIDARG,
4336 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"),
4337 aDevice, aControllerPort, aName.c_str());
4338 pAttach->i_updateNonRotational(!!aNonRotational);
4339
4340 return S_OK;
4341}
4342
4343HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4344 LONG aDevice, BOOL aDiscard)
4345{
4346
4347 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4348 aName.c_str(), aControllerPort, aDevice, aDiscard));
4349
4350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4351
4352 HRESULT rc = i_checkStateDependency(MutableStateDep);
4353 if (FAILED(rc)) return rc;
4354
4355 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4356
4357 if (Global::IsOnlineOrTransient(mData->mMachineState))
4358 return setError(VBOX_E_INVALID_VM_STATE,
4359 tr("Invalid machine state: %s"),
4360 Global::stringifyMachineState(mData->mMachineState));
4361
4362 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4363 aName,
4364 aControllerPort,
4365 aDevice);
4366 if (!pAttach)
4367 return setError(VBOX_E_OBJECT_NOT_FOUND,
4368 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4369 aDevice, aControllerPort, aName.c_str());
4370
4371
4372 i_setModified(IsModified_Storage);
4373 mMediumAttachments.backup();
4374
4375 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4376
4377 if (pAttach->i_getType() != DeviceType_HardDisk)
4378 return setError(E_INVALIDARG,
4379 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"),
4380 aDevice, aControllerPort, aName.c_str());
4381 pAttach->i_updateDiscard(!!aDiscard);
4382
4383 return S_OK;
4384}
4385
4386HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4387 LONG aDevice, BOOL aHotPluggable)
4388{
4389 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4390 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4391
4392 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4393
4394 HRESULT rc = i_checkStateDependency(MutableStateDep);
4395 if (FAILED(rc)) return rc;
4396
4397 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4398
4399 if (Global::IsOnlineOrTransient(mData->mMachineState))
4400 return setError(VBOX_E_INVALID_VM_STATE,
4401 tr("Invalid machine state: %s"),
4402 Global::stringifyMachineState(mData->mMachineState));
4403
4404 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4405 aName,
4406 aControllerPort,
4407 aDevice);
4408 if (!pAttach)
4409 return setError(VBOX_E_OBJECT_NOT_FOUND,
4410 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4411 aDevice, aControllerPort, aName.c_str());
4412
4413 /* Check for an existing controller. */
4414 ComObjPtr<StorageController> ctl;
4415 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4416 if (FAILED(rc)) return rc;
4417
4418 StorageControllerType_T ctrlType;
4419 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4420 if (FAILED(rc))
4421 return setError(E_FAIL,
4422 tr("Could not get type of controller '%s'"),
4423 aName.c_str());
4424
4425 if (!i_isControllerHotplugCapable(ctrlType))
4426 return setError(VBOX_E_NOT_SUPPORTED,
4427 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4428 aName.c_str());
4429
4430 i_setModified(IsModified_Storage);
4431 mMediumAttachments.backup();
4432
4433 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4434
4435 if (pAttach->i_getType() == DeviceType_Floppy)
4436 return setError(E_INVALIDARG,
4437 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"),
4438 aDevice, aControllerPort, aName.c_str());
4439 pAttach->i_updateHotPluggable(!!aHotPluggable);
4440
4441 return S_OK;
4442}
4443
4444HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4445 LONG aDevice)
4446{
4447 int rc = S_OK;
4448 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4449 aName.c_str(), aControllerPort, aDevice));
4450
4451 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4452
4453 return rc;
4454}
4455
4456HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4457 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4458{
4459 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4460 aName.c_str(), aControllerPort, aDevice));
4461
4462 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4463
4464 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4465 if (FAILED(rc)) return rc;
4466
4467 if (Global::IsOnlineOrTransient(mData->mMachineState))
4468 return setError(VBOX_E_INVALID_VM_STATE,
4469 tr("Invalid machine state: %s"),
4470 Global::stringifyMachineState(mData->mMachineState));
4471
4472 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4473 aName,
4474 aControllerPort,
4475 aDevice);
4476 if (!pAttach)
4477 return setError(VBOX_E_OBJECT_NOT_FOUND,
4478 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4479 aDevice, aControllerPort, aName.c_str());
4480
4481
4482 i_setModified(IsModified_Storage);
4483 mMediumAttachments.backup();
4484
4485 IBandwidthGroup *iB = aBandwidthGroup;
4486 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4487 if (aBandwidthGroup && group.isNull())
4488 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4489
4490 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4491
4492 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4493 if (strBandwidthGroupOld.isNotEmpty())
4494 {
4495 /* Get the bandwidth group object and release it - this must not fail. */
4496 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4497 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4498 Assert(SUCCEEDED(rc));
4499
4500 pBandwidthGroupOld->i_release();
4501 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4502 }
4503
4504 if (!group.isNull())
4505 {
4506 group->i_reference();
4507 pAttach->i_updateBandwidthGroup(group->i_getName());
4508 }
4509
4510 return S_OK;
4511}
4512
4513HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4514 LONG aControllerPort,
4515 LONG aDevice,
4516 DeviceType_T aType)
4517{
4518 HRESULT rc = S_OK;
4519
4520 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4521 aName.c_str(), aControllerPort, aDevice, aType));
4522
4523 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4524
4525 return rc;
4526}
4527
4528
4529HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4530 LONG aControllerPort,
4531 LONG aDevice,
4532 BOOL aForce)
4533{
4534 int rc = S_OK;
4535 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4536 aName.c_str(), aControllerPort, aForce));
4537
4538 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4539
4540 return rc;
4541}
4542
4543HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4544 LONG aControllerPort,
4545 LONG aDevice,
4546 const ComPtr<IMedium> &aMedium,
4547 BOOL aForce)
4548{
4549 int rc = S_OK;
4550 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4551 aName.c_str(), aControllerPort, aDevice, aForce));
4552
4553 // request the host lock first, since might be calling Host methods for getting host drives;
4554 // next, protect the media tree all the while we're in here, as well as our member variables
4555 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4556 this->lockHandle(),
4557 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4558
4559 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4560 aName,
4561 aControllerPort,
4562 aDevice);
4563 if (pAttach.isNull())
4564 return setError(VBOX_E_OBJECT_NOT_FOUND,
4565 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4566 aDevice, aControllerPort, aName.c_str());
4567
4568 /* Remember previously mounted medium. The medium before taking the
4569 * backup is not necessarily the same thing. */
4570 ComObjPtr<Medium> oldmedium;
4571 oldmedium = pAttach->i_getMedium();
4572
4573 IMedium *iM = aMedium;
4574 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4575 if (aMedium && pMedium.isNull())
4576 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4577
4578 AutoCaller mediumCaller(pMedium);
4579 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4580
4581 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4582 if (pMedium)
4583 {
4584 DeviceType_T mediumType = pAttach->i_getType();
4585 switch (mediumType)
4586 {
4587 case DeviceType_DVD:
4588 case DeviceType_Floppy:
4589 break;
4590
4591 default:
4592 return setError(VBOX_E_INVALID_OBJECT_STATE,
4593 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4594 aControllerPort,
4595 aDevice,
4596 aName.c_str());
4597 }
4598 }
4599
4600 i_setModified(IsModified_Storage);
4601 mMediumAttachments.backup();
4602
4603 {
4604 // The backup operation makes the pAttach reference point to the
4605 // old settings. Re-get the correct reference.
4606 pAttach = i_findAttachment(*mMediumAttachments.data(),
4607 aName,
4608 aControllerPort,
4609 aDevice);
4610 if (!oldmedium.isNull())
4611 oldmedium->i_removeBackReference(mData->mUuid);
4612 if (!pMedium.isNull())
4613 {
4614 pMedium->i_addBackReference(mData->mUuid);
4615
4616 mediumLock.release();
4617 multiLock.release();
4618 i_addMediumToRegistry(pMedium);
4619 multiLock.acquire();
4620 mediumLock.acquire();
4621 }
4622
4623 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4624 pAttach->i_updateMedium(pMedium);
4625 }
4626
4627 i_setModified(IsModified_Storage);
4628
4629 mediumLock.release();
4630 multiLock.release();
4631 rc = i_onMediumChange(pAttach, aForce);
4632 multiLock.acquire();
4633 mediumLock.acquire();
4634
4635 /* On error roll back this change only. */
4636 if (FAILED(rc))
4637 {
4638 if (!pMedium.isNull())
4639 pMedium->i_removeBackReference(mData->mUuid);
4640 pAttach = i_findAttachment(*mMediumAttachments.data(),
4641 aName,
4642 aControllerPort,
4643 aDevice);
4644 /* If the attachment is gone in the meantime, bail out. */
4645 if (pAttach.isNull())
4646 return rc;
4647 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4648 if (!oldmedium.isNull())
4649 oldmedium->i_addBackReference(mData->mUuid);
4650 pAttach->i_updateMedium(oldmedium);
4651 }
4652
4653 mediumLock.release();
4654 multiLock.release();
4655
4656 /* Save modified registries, but skip this machine as it's the caller's
4657 * job to save its settings like all other settings changes. */
4658 mParent->i_unmarkRegistryModified(i_getId());
4659 mParent->i_saveModifiedRegistries();
4660
4661 return rc;
4662}
4663HRESULT Machine::getMedium(const com::Utf8Str &aName,
4664 LONG aControllerPort,
4665 LONG aDevice,
4666 ComPtr<IMedium> &aMedium)
4667{
4668 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4669 aName.c_str(), aControllerPort, aDevice));
4670
4671 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4672
4673 aMedium = NULL;
4674
4675 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4676 aName,
4677 aControllerPort,
4678 aDevice);
4679 if (pAttach.isNull())
4680 return setError(VBOX_E_OBJECT_NOT_FOUND,
4681 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4682 aDevice, aControllerPort, aName.c_str());
4683
4684 aMedium = pAttach->i_getMedium();
4685
4686 return S_OK;
4687}
4688
4689HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4690{
4691
4692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4693
4694 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4695
4696 return S_OK;
4697}
4698
4699HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4700{
4701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4702
4703 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4704
4705 return S_OK;
4706}
4707
4708HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4709{
4710 /* Do not assert if slot is out of range, just return the advertised
4711 status. testdriver/vbox.py triggers this in logVmInfo. */
4712 if (aSlot >= mNetworkAdapters.size())
4713 return setError(E_INVALIDARG,
4714 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4715 aSlot, mNetworkAdapters.size());
4716
4717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4718
4719 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4720
4721 return S_OK;
4722}
4723
4724HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4725{
4726 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4727
4728 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4729 size_t i = 0;
4730 for (settings::StringsMap::const_iterator
4731 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4732 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4733 ++it, ++i)
4734 aKeys[i] = it->first;
4735
4736 return S_OK;
4737}
4738
4739 /**
4740 * @note Locks this object for reading.
4741 */
4742HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4743 com::Utf8Str &aValue)
4744{
4745 /* start with nothing found */
4746 aValue = "";
4747
4748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4749
4750 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4751 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4752 // found:
4753 aValue = it->second; // source is a Utf8Str
4754
4755 /* return the result to caller (may be empty) */
4756 return S_OK;
4757}
4758
4759 /**
4760 * @note Locks mParent for writing + this object for writing.
4761 */
4762HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4763{
4764 /* Because control characters in aKey have caused problems in the settings
4765 * they are rejected unless the key should be deleted. */
4766 if (!aValue.isEmpty())
4767 {
4768 for (size_t i = 0; i < aKey.length(); ++i)
4769 {
4770 char ch = aKey[i];
4771 if (RTLocCIsCntrl(ch))
4772 return E_INVALIDARG;
4773 }
4774 }
4775
4776 Utf8Str strOldValue; // empty
4777
4778 // locking note: we only hold the read lock briefly to look up the old value,
4779 // then release it and call the onExtraCanChange callbacks. There is a small
4780 // chance of a race insofar as the callback might be called twice if two callers
4781 // change the same key at the same time, but that's a much better solution
4782 // than the deadlock we had here before. The actual changing of the extradata
4783 // is then performed under the write lock and race-free.
4784
4785 // look up the old value first; if nothing has changed then we need not do anything
4786 {
4787 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4788
4789 // For snapshots don't even think about allowing changes, extradata
4790 // is global for a machine, so there is nothing snapshot specific.
4791 if (i_isSnapshotMachine())
4792 return setError(VBOX_E_INVALID_VM_STATE,
4793 tr("Cannot set extradata for a snapshot"));
4794
4795 // check if the right IMachine instance is used
4796 if (mData->mRegistered && !i_isSessionMachine())
4797 return setError(VBOX_E_INVALID_VM_STATE,
4798 tr("Cannot set extradata for an immutable machine"));
4799
4800 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4801 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4802 strOldValue = it->second;
4803 }
4804
4805 bool fChanged;
4806 if ((fChanged = (strOldValue != aValue)))
4807 {
4808 // ask for permission from all listeners outside the locks;
4809 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4810 // lock to copy the list of callbacks to invoke
4811 Bstr error;
4812 Bstr bstrValue(aValue);
4813
4814 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4815 {
4816 const char *sep = error.isEmpty() ? "" : ": ";
4817 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4818 return setError(E_ACCESSDENIED,
4819 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4820 aKey.c_str(),
4821 aValue.c_str(),
4822 sep,
4823 error.raw());
4824 }
4825
4826 // data is changing and change not vetoed: then write it out under the lock
4827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4828
4829 if (aValue.isEmpty())
4830 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4831 else
4832 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4833 // creates a new key if needed
4834
4835 bool fNeedsGlobalSaveSettings = false;
4836 // This saving of settings is tricky: there is no "old state" for the
4837 // extradata items at all (unlike all other settings), so the old/new
4838 // settings comparison would give a wrong result!
4839 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4840
4841 if (fNeedsGlobalSaveSettings)
4842 {
4843 // save the global settings; for that we should hold only the VirtualBox lock
4844 alock.release();
4845 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4846 mParent->i_saveSettings();
4847 }
4848 }
4849
4850 // fire notification outside the lock
4851 if (fChanged)
4852 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4853
4854 return S_OK;
4855}
4856
4857HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4858{
4859 aProgress = NULL;
4860 NOREF(aSettingsFilePath);
4861 ReturnComNotImplemented();
4862}
4863
4864HRESULT Machine::saveSettings()
4865{
4866 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4867
4868 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4869 if (FAILED(rc)) return rc;
4870
4871 /* the settings file path may never be null */
4872 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4873
4874 /* save all VM data excluding snapshots */
4875 bool fNeedsGlobalSaveSettings = false;
4876 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4877 mlock.release();
4878
4879 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4880 {
4881 // save the global settings; for that we should hold only the VirtualBox lock
4882 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4883 rc = mParent->i_saveSettings();
4884 }
4885
4886 return rc;
4887}
4888
4889
4890HRESULT Machine::discardSettings()
4891{
4892 /*
4893 * We need to take the machine list lock here as well as the machine one
4894 * or we'll get into trouble should any media stuff require rolling back.
4895 *
4896 * Details:
4897 *
4898 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4899 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4900 * 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]
4901 * 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
4902 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4903 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4904 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4905 * 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
4906 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4907 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4908 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4909 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4910 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4911 * 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]
4912 * 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] (*)
4913 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4914 * 0:005> k
4915 * # Child-SP RetAddr Call Site
4916 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4917 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4918 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4919 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4920 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4921 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4922 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4923 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4924 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4925 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4926 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4927 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4928 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4929 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4930 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4931 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4932 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4933 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4934 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4935 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4936 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4937 *
4938 */
4939 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4941
4942 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4943 if (FAILED(rc)) return rc;
4944
4945 /*
4946 * during this rollback, the session will be notified if data has
4947 * been actually changed
4948 */
4949 i_rollback(true /* aNotify */);
4950
4951 return S_OK;
4952}
4953
4954/** @note Locks objects! */
4955HRESULT Machine::unregister(AutoCaller &autoCaller,
4956 CleanupMode_T aCleanupMode,
4957 std::vector<ComPtr<IMedium> > &aMedia)
4958{
4959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4960
4961 Guid id(i_getId());
4962
4963 if (mData->mSession.mState != SessionState_Unlocked)
4964 return setError(VBOX_E_INVALID_OBJECT_STATE,
4965 tr("Cannot unregister the machine '%s' while it is locked"),
4966 mUserData->s.strName.c_str());
4967
4968 // wait for state dependents to drop to zero
4969 i_ensureNoStateDependencies();
4970
4971 if (!mData->mAccessible)
4972 {
4973 // inaccessible maschines can only be unregistered; uninitialize ourselves
4974 // here because currently there may be no unregistered that are inaccessible
4975 // (this state combination is not supported). Note releasing the caller and
4976 // leaving the lock before calling uninit()
4977 alock.release();
4978 autoCaller.release();
4979
4980 uninit();
4981
4982 mParent->i_unregisterMachine(this, id);
4983 // calls VirtualBox::i_saveSettings()
4984
4985 return S_OK;
4986 }
4987
4988 HRESULT rc = S_OK;
4989
4990 /// @todo r=klaus this is stupid... why is the saved state always deleted?
4991 // discard saved state
4992 if (mData->mMachineState == MachineState_Saved)
4993 {
4994 // add the saved state file to the list of files the caller should delete
4995 Assert(!mSSData->strStateFilePath.isEmpty());
4996 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4997
4998 mSSData->strStateFilePath.setNull();
4999
5000 // unconditionally set the machine state to powered off, we now
5001 // know no session has locked the machine
5002 mData->mMachineState = MachineState_PoweredOff;
5003 }
5004
5005 size_t cSnapshots = 0;
5006 if (mData->mFirstSnapshot)
5007 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5008 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5009 // fail now before we start detaching media
5010 return setError(VBOX_E_INVALID_OBJECT_STATE,
5011 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5012 mUserData->s.strName.c_str(), cSnapshots);
5013
5014 // This list collects the medium objects from all medium attachments
5015 // which we will detach from the machine and its snapshots, in a specific
5016 // order which allows for closing all media without getting "media in use"
5017 // errors, simply by going through the list from the front to the back:
5018 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5019 // and must be closed before the parent media from the snapshots, or closing the parents
5020 // will fail because they still have children);
5021 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5022 // the root ("first") snapshot of the machine.
5023 MediaList llMedia;
5024
5025 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5026 && mMediumAttachments->size()
5027 )
5028 {
5029 // we have media attachments: detach them all and add the Medium objects to our list
5030 if (aCleanupMode != CleanupMode_UnregisterOnly)
5031 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5032 else
5033 return setError(VBOX_E_INVALID_OBJECT_STATE,
5034 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5035 mUserData->s.strName.c_str(), mMediumAttachments->size());
5036 }
5037
5038 if (cSnapshots)
5039 {
5040 // add the media from the medium attachments of the snapshots to llMedia
5041 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5042 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5043 // into the children first
5044
5045 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5046 MachineState_T oldState = mData->mMachineState;
5047 mData->mMachineState = MachineState_DeletingSnapshot;
5048
5049 // make a copy of the first snapshot so the refcount does not drop to 0
5050 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5051 // because of the AutoCaller voodoo)
5052 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5053
5054 // GO!
5055 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5056
5057 mData->mMachineState = oldState;
5058 }
5059
5060 if (FAILED(rc))
5061 {
5062 i_rollbackMedia();
5063 return rc;
5064 }
5065
5066 // commit all the media changes made above
5067 i_commitMedia();
5068
5069 mData->mRegistered = false;
5070
5071 // machine lock no longer needed
5072 alock.release();
5073
5074 // return media to caller
5075 aMedia.resize(llMedia.size());
5076 size_t i = 0;
5077 for (MediaList::const_iterator
5078 it = llMedia.begin();
5079 it != llMedia.end();
5080 ++it, ++i)
5081 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5082
5083 mParent->i_unregisterMachine(this, id);
5084 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5085
5086 return S_OK;
5087}
5088
5089/**
5090 * Task record for deleting a machine config.
5091 */
5092class Machine::DeleteConfigTask
5093 : public Machine::Task
5094{
5095public:
5096 DeleteConfigTask(Machine *m,
5097 Progress *p,
5098 const Utf8Str &t,
5099 const RTCList<ComPtr<IMedium> > &llMediums,
5100 const StringsList &llFilesToDelete)
5101 : Task(m, p, t),
5102 m_llMediums(llMediums),
5103 m_llFilesToDelete(llFilesToDelete)
5104 {}
5105
5106private:
5107 void handler()
5108 {
5109 try
5110 {
5111 m_pMachine->i_deleteConfigHandler(*this);
5112 }
5113 catch (...)
5114 {
5115 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5116 }
5117 }
5118
5119 RTCList<ComPtr<IMedium> > m_llMediums;
5120 StringsList m_llFilesToDelete;
5121
5122 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5123};
5124
5125/**
5126 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5127 * SessionMachine::taskHandler().
5128 *
5129 * @note Locks this object for writing.
5130 *
5131 * @param task
5132 * @return
5133 */
5134void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5135{
5136 LogFlowThisFuncEnter();
5137
5138 AutoCaller autoCaller(this);
5139 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5140 if (FAILED(autoCaller.rc()))
5141 {
5142 /* we might have been uninitialized because the session was accidentally
5143 * closed by the client, so don't assert */
5144 HRESULT rc = setError(E_FAIL,
5145 tr("The session has been accidentally closed"));
5146 task.m_pProgress->i_notifyComplete(rc);
5147 LogFlowThisFuncLeave();
5148 return;
5149 }
5150
5151 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5152
5153 HRESULT rc = S_OK;
5154
5155 try
5156 {
5157 ULONG uLogHistoryCount = 3;
5158 ComPtr<ISystemProperties> systemProperties;
5159 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5160 if (FAILED(rc)) throw rc;
5161
5162 if (!systemProperties.isNull())
5163 {
5164 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5165 if (FAILED(rc)) throw rc;
5166 }
5167
5168 MachineState_T oldState = mData->mMachineState;
5169 i_setMachineState(MachineState_SettingUp);
5170 alock.release();
5171 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5172 {
5173 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5174 {
5175 AutoCaller mac(pMedium);
5176 if (FAILED(mac.rc())) throw mac.rc();
5177 Utf8Str strLocation = pMedium->i_getLocationFull();
5178 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5179 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5180 if (FAILED(rc)) throw rc;
5181 }
5182 if (pMedium->i_isMediumFormatFile())
5183 {
5184 ComPtr<IProgress> pProgress2;
5185 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5186 if (FAILED(rc)) throw rc;
5187 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5188 if (FAILED(rc)) throw rc;
5189 }
5190
5191 /* Close the medium, deliberately without checking the return
5192 * code, and without leaving any trace in the error info, as
5193 * a failure here is a very minor issue, which shouldn't happen
5194 * as above we even managed to delete the medium. */
5195 {
5196 ErrorInfoKeeper eik;
5197 pMedium->Close();
5198 }
5199 }
5200 i_setMachineState(oldState);
5201 alock.acquire();
5202
5203 // delete the files pushed on the task list by Machine::Delete()
5204 // (this includes saved states of the machine and snapshots and
5205 // medium storage files from the IMedium list passed in, and the
5206 // machine XML file)
5207 for (StringsList::const_iterator
5208 it = task.m_llFilesToDelete.begin();
5209 it != task.m_llFilesToDelete.end();
5210 ++it)
5211 {
5212 const Utf8Str &strFile = *it;
5213 LogFunc(("Deleting file %s\n", strFile.c_str()));
5214 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5215 if (FAILED(rc)) throw rc;
5216
5217 int vrc = RTFileDelete(strFile.c_str());
5218 if (RT_FAILURE(vrc))
5219 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5220 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5221 }
5222
5223 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5224 if (FAILED(rc)) throw rc;
5225
5226 /* delete the settings only when the file actually exists */
5227 if (mData->pMachineConfigFile->fileExists())
5228 {
5229 /* Delete any backup or uncommitted XML files. Ignore failures.
5230 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5231 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5232 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5233 RTFileDelete(otherXml.c_str());
5234 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5235 RTFileDelete(otherXml.c_str());
5236
5237 /* delete the Logs folder, nothing important should be left
5238 * there (we don't check for errors because the user might have
5239 * some private files there that we don't want to delete) */
5240 Utf8Str logFolder;
5241 getLogFolder(logFolder);
5242 Assert(logFolder.length());
5243 if (RTDirExists(logFolder.c_str()))
5244 {
5245 /* Delete all VBox.log[.N] files from the Logs folder
5246 * (this must be in sync with the rotation logic in
5247 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5248 * files that may have been created by the GUI. */
5249 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5250 logFolder.c_str(), RTPATH_DELIMITER);
5251 RTFileDelete(log.c_str());
5252 log = Utf8StrFmt("%s%cVBox.png",
5253 logFolder.c_str(), RTPATH_DELIMITER);
5254 RTFileDelete(log.c_str());
5255 for (int i = uLogHistoryCount; i > 0; i--)
5256 {
5257 log = Utf8StrFmt("%s%cVBox.log.%d",
5258 logFolder.c_str(), RTPATH_DELIMITER, i);
5259 RTFileDelete(log.c_str());
5260 log = Utf8StrFmt("%s%cVBox.png.%d",
5261 logFolder.c_str(), RTPATH_DELIMITER, i);
5262 RTFileDelete(log.c_str());
5263 }
5264#if defined(RT_OS_WINDOWS)
5265 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5266 RTFileDelete(log.c_str());
5267 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5268 RTFileDelete(log.c_str());
5269#endif
5270
5271 RTDirRemove(logFolder.c_str());
5272 }
5273
5274 /* delete the Snapshots folder, nothing important should be left
5275 * there (we don't check for errors because the user might have
5276 * some private files there that we don't want to delete) */
5277 Utf8Str strFullSnapshotFolder;
5278 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5279 Assert(!strFullSnapshotFolder.isEmpty());
5280 if (RTDirExists(strFullSnapshotFolder.c_str()))
5281 RTDirRemove(strFullSnapshotFolder.c_str());
5282
5283 // delete the directory that contains the settings file, but only
5284 // if it matches the VM name
5285 Utf8Str settingsDir;
5286 if (i_isInOwnDir(&settingsDir))
5287 RTDirRemove(settingsDir.c_str());
5288 }
5289
5290 alock.release();
5291
5292 mParent->i_saveModifiedRegistries();
5293 }
5294 catch (HRESULT aRC) { rc = aRC; }
5295
5296 task.m_pProgress->i_notifyComplete(rc);
5297
5298 LogFlowThisFuncLeave();
5299}
5300
5301HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5302{
5303 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5304
5305 HRESULT rc = i_checkStateDependency(MutableStateDep);
5306 if (FAILED(rc)) return rc;
5307
5308 if (mData->mRegistered)
5309 return setError(VBOX_E_INVALID_VM_STATE,
5310 tr("Cannot delete settings of a registered machine"));
5311
5312 // collect files to delete
5313 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5314 if (mData->pMachineConfigFile->fileExists())
5315 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5316
5317 RTCList<ComPtr<IMedium> > llMediums;
5318 for (size_t i = 0; i < aMedia.size(); ++i)
5319 {
5320 IMedium *pIMedium(aMedia[i]);
5321 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5322 if (pMedium.isNull())
5323 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5324 SafeArray<BSTR> ids;
5325 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5326 if (FAILED(rc)) return rc;
5327 /* At this point the medium should not have any back references
5328 * anymore. If it has it is attached to another VM and *must* not
5329 * deleted. */
5330 if (ids.size() < 1)
5331 llMediums.append(pMedium);
5332 }
5333
5334 ComObjPtr<Progress> pProgress;
5335 pProgress.createObject();
5336 rc = pProgress->init(i_getVirtualBox(),
5337 static_cast<IMachine*>(this) /* aInitiator */,
5338 tr("Deleting files"),
5339 true /* fCancellable */,
5340 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5341 tr("Collecting file inventory"));
5342 if (FAILED(rc))
5343 return rc;
5344
5345 /* create and start the task on a separate thread (note that it will not
5346 * start working until we release alock) */
5347 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5348 rc = pTask->createThread();
5349 pTask = NULL;
5350 if (FAILED(rc))
5351 return rc;
5352
5353 pProgress.queryInterfaceTo(aProgress.asOutParam());
5354
5355 LogFlowFuncLeave();
5356
5357 return S_OK;
5358}
5359
5360HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5361{
5362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5363
5364 ComObjPtr<Snapshot> pSnapshot;
5365 HRESULT rc;
5366
5367 if (aNameOrId.isEmpty())
5368 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5369 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5370 else
5371 {
5372 Guid uuid(aNameOrId);
5373 if (uuid.isValid())
5374 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5375 else
5376 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5377 }
5378 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5379
5380 return rc;
5381}
5382
5383HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5384 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5385{
5386 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5387
5388 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5389 if (FAILED(rc)) return rc;
5390
5391 ComObjPtr<SharedFolder> sharedFolder;
5392 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5393 if (SUCCEEDED(rc))
5394 return setError(VBOX_E_OBJECT_IN_USE,
5395 tr("Shared folder named '%s' already exists"),
5396 aName.c_str());
5397
5398 sharedFolder.createObject();
5399 rc = sharedFolder->init(i_getMachine(),
5400 aName,
5401 aHostPath,
5402 !!aWritable,
5403 !!aAutomount,
5404 aAutoMountPoint,
5405 true /* fFailOnError */);
5406 if (FAILED(rc)) return rc;
5407
5408 i_setModified(IsModified_SharedFolders);
5409 mHWData.backup();
5410 mHWData->mSharedFolders.push_back(sharedFolder);
5411
5412 /* inform the direct session if any */
5413 alock.release();
5414 i_onSharedFolderChange();
5415
5416 return S_OK;
5417}
5418
5419HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5420{
5421 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5422
5423 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5424 if (FAILED(rc)) return rc;
5425
5426 ComObjPtr<SharedFolder> sharedFolder;
5427 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5428 if (FAILED(rc)) return rc;
5429
5430 i_setModified(IsModified_SharedFolders);
5431 mHWData.backup();
5432 mHWData->mSharedFolders.remove(sharedFolder);
5433
5434 /* inform the direct session if any */
5435 alock.release();
5436 i_onSharedFolderChange();
5437
5438 return S_OK;
5439}
5440
5441HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5442{
5443 /* start with No */
5444 *aCanShow = FALSE;
5445
5446 ComPtr<IInternalSessionControl> directControl;
5447 {
5448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5449
5450 if (mData->mSession.mState != SessionState_Locked)
5451 return setError(VBOX_E_INVALID_VM_STATE,
5452 tr("Machine is not locked for session (session state: %s)"),
5453 Global::stringifySessionState(mData->mSession.mState));
5454
5455 if (mData->mSession.mLockType == LockType_VM)
5456 directControl = mData->mSession.mDirectControl;
5457 }
5458
5459 /* ignore calls made after #OnSessionEnd() is called */
5460 if (!directControl)
5461 return S_OK;
5462
5463 LONG64 dummy;
5464 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5465}
5466
5467HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5468{
5469 ComPtr<IInternalSessionControl> directControl;
5470 {
5471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5472
5473 if (mData->mSession.mState != SessionState_Locked)
5474 return setError(E_FAIL,
5475 tr("Machine is not locked for session (session state: %s)"),
5476 Global::stringifySessionState(mData->mSession.mState));
5477
5478 if (mData->mSession.mLockType == LockType_VM)
5479 directControl = mData->mSession.mDirectControl;
5480 }
5481
5482 /* ignore calls made after #OnSessionEnd() is called */
5483 if (!directControl)
5484 return S_OK;
5485
5486 BOOL dummy;
5487 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5488}
5489
5490#ifdef VBOX_WITH_GUEST_PROPS
5491/**
5492 * Look up a guest property in VBoxSVC's internal structures.
5493 */
5494HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5495 com::Utf8Str &aValue,
5496 LONG64 *aTimestamp,
5497 com::Utf8Str &aFlags) const
5498{
5499 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5500
5501 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5502 if (it != mHWData->mGuestProperties.end())
5503 {
5504 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5505 aValue = it->second.strValue;
5506 *aTimestamp = it->second.mTimestamp;
5507 GuestPropWriteFlags(it->second.mFlags, szFlags);
5508 aFlags = Utf8Str(szFlags);
5509 }
5510
5511 return S_OK;
5512}
5513
5514/**
5515 * Query the VM that a guest property belongs to for the property.
5516 * @returns E_ACCESSDENIED if the VM process is not available or not
5517 * currently handling queries and the lookup should then be done in
5518 * VBoxSVC.
5519 */
5520HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5521 com::Utf8Str &aValue,
5522 LONG64 *aTimestamp,
5523 com::Utf8Str &aFlags) const
5524{
5525 HRESULT rc = S_OK;
5526 Bstr bstrValue;
5527 Bstr bstrFlags;
5528
5529 ComPtr<IInternalSessionControl> directControl;
5530 {
5531 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5532 if (mData->mSession.mLockType == LockType_VM)
5533 directControl = mData->mSession.mDirectControl;
5534 }
5535
5536 /* ignore calls made after #OnSessionEnd() is called */
5537 if (!directControl)
5538 rc = E_ACCESSDENIED;
5539 else
5540 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5541 0 /* accessMode */,
5542 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5543
5544 aValue = bstrValue;
5545 aFlags = bstrFlags;
5546
5547 return rc;
5548}
5549#endif // VBOX_WITH_GUEST_PROPS
5550
5551HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5552 com::Utf8Str &aValue,
5553 LONG64 *aTimestamp,
5554 com::Utf8Str &aFlags)
5555{
5556#ifndef VBOX_WITH_GUEST_PROPS
5557 ReturnComNotImplemented();
5558#else // VBOX_WITH_GUEST_PROPS
5559
5560 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5561
5562 if (rc == E_ACCESSDENIED)
5563 /* The VM is not running or the service is not (yet) accessible */
5564 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5565 return rc;
5566#endif // VBOX_WITH_GUEST_PROPS
5567}
5568
5569HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5570{
5571 LONG64 dummyTimestamp;
5572 com::Utf8Str dummyFlags;
5573 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5574 return rc;
5575
5576}
5577HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5578{
5579 com::Utf8Str dummyFlags;
5580 com::Utf8Str dummyValue;
5581 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5582 return rc;
5583}
5584
5585#ifdef VBOX_WITH_GUEST_PROPS
5586/**
5587 * Set a guest property in VBoxSVC's internal structures.
5588 */
5589HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5590 const com::Utf8Str &aFlags, bool fDelete)
5591{
5592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5593 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5594 if (FAILED(rc)) return rc;
5595
5596 try
5597 {
5598 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5599 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5600 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5601
5602 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5603 if (it == mHWData->mGuestProperties.end())
5604 {
5605 if (!fDelete)
5606 {
5607 i_setModified(IsModified_MachineData);
5608 mHWData.backupEx();
5609
5610 RTTIMESPEC time;
5611 HWData::GuestProperty prop;
5612 prop.strValue = Bstr(aValue).raw();
5613 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5614 prop.mFlags = fFlags;
5615 mHWData->mGuestProperties[aName] = prop;
5616 }
5617 }
5618 else
5619 {
5620 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5621 {
5622 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5623 }
5624 else
5625 {
5626 i_setModified(IsModified_MachineData);
5627 mHWData.backupEx();
5628
5629 /* The backupEx() operation invalidates our iterator,
5630 * so get a new one. */
5631 it = mHWData->mGuestProperties.find(aName);
5632 Assert(it != mHWData->mGuestProperties.end());
5633
5634 if (!fDelete)
5635 {
5636 RTTIMESPEC time;
5637 it->second.strValue = aValue;
5638 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5639 it->second.mFlags = fFlags;
5640 }
5641 else
5642 mHWData->mGuestProperties.erase(it);
5643 }
5644 }
5645
5646 if (SUCCEEDED(rc))
5647 {
5648 alock.release();
5649
5650 mParent->i_onGuestPropertyChange(mData->mUuid,
5651 Bstr(aName).raw(),
5652 Bstr(aValue).raw(),
5653 Bstr(aFlags).raw());
5654 }
5655 }
5656 catch (std::bad_alloc &)
5657 {
5658 rc = E_OUTOFMEMORY;
5659 }
5660
5661 return rc;
5662}
5663
5664/**
5665 * Set a property on the VM that that property belongs to.
5666 * @returns E_ACCESSDENIED if the VM process is not available or not
5667 * currently handling queries and the setting should then be done in
5668 * VBoxSVC.
5669 */
5670HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5671 const com::Utf8Str &aFlags, bool fDelete)
5672{
5673 HRESULT rc;
5674
5675 try
5676 {
5677 ComPtr<IInternalSessionControl> directControl;
5678 {
5679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5680 if (mData->mSession.mLockType == LockType_VM)
5681 directControl = mData->mSession.mDirectControl;
5682 }
5683
5684 Bstr dummy1; /* will not be changed (setter) */
5685 Bstr dummy2; /* will not be changed (setter) */
5686 LONG64 dummy64;
5687 if (!directControl)
5688 rc = E_ACCESSDENIED;
5689 else
5690 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5691 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5692 fDelete ? 2 : 1 /* accessMode */,
5693 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5694 }
5695 catch (std::bad_alloc &)
5696 {
5697 rc = E_OUTOFMEMORY;
5698 }
5699
5700 return rc;
5701}
5702#endif // VBOX_WITH_GUEST_PROPS
5703
5704HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5705 const com::Utf8Str &aFlags)
5706{
5707#ifndef VBOX_WITH_GUEST_PROPS
5708 ReturnComNotImplemented();
5709#else // VBOX_WITH_GUEST_PROPS
5710 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5711 if (rc == E_ACCESSDENIED)
5712 /* The VM is not running or the service is not (yet) accessible */
5713 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5714 return rc;
5715#endif // VBOX_WITH_GUEST_PROPS
5716}
5717
5718HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5719{
5720 return setGuestProperty(aProperty, aValue, "");
5721}
5722
5723HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5724{
5725#ifndef VBOX_WITH_GUEST_PROPS
5726 ReturnComNotImplemented();
5727#else // VBOX_WITH_GUEST_PROPS
5728 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5729 if (rc == E_ACCESSDENIED)
5730 /* The VM is not running or the service is not (yet) accessible */
5731 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5732 return rc;
5733#endif // VBOX_WITH_GUEST_PROPS
5734}
5735
5736#ifdef VBOX_WITH_GUEST_PROPS
5737/**
5738 * Enumerate the guest properties in VBoxSVC's internal structures.
5739 */
5740HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5741 std::vector<com::Utf8Str> &aNames,
5742 std::vector<com::Utf8Str> &aValues,
5743 std::vector<LONG64> &aTimestamps,
5744 std::vector<com::Utf8Str> &aFlags)
5745{
5746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5747 Utf8Str strPatterns(aPatterns);
5748
5749 /*
5750 * Look for matching patterns and build up a list.
5751 */
5752 HWData::GuestPropertyMap propMap;
5753 for (HWData::GuestPropertyMap::const_iterator
5754 it = mHWData->mGuestProperties.begin();
5755 it != mHWData->mGuestProperties.end();
5756 ++it)
5757 {
5758 if ( strPatterns.isEmpty()
5759 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5760 RTSTR_MAX,
5761 it->first.c_str(),
5762 RTSTR_MAX,
5763 NULL)
5764 )
5765 propMap.insert(*it);
5766 }
5767
5768 alock.release();
5769
5770 /*
5771 * And build up the arrays for returning the property information.
5772 */
5773 size_t cEntries = propMap.size();
5774
5775 aNames.resize(cEntries);
5776 aValues.resize(cEntries);
5777 aTimestamps.resize(cEntries);
5778 aFlags.resize(cEntries);
5779
5780 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5781 size_t i = 0;
5782 for (HWData::GuestPropertyMap::const_iterator
5783 it = propMap.begin();
5784 it != propMap.end();
5785 ++it, ++i)
5786 {
5787 aNames[i] = it->first;
5788 aValues[i] = it->second.strValue;
5789 aTimestamps[i] = it->second.mTimestamp;
5790 GuestPropWriteFlags(it->second.mFlags, szFlags);
5791 aFlags[i] = Utf8Str(szFlags);
5792 }
5793
5794 return S_OK;
5795}
5796
5797/**
5798 * Enumerate the properties managed by a VM.
5799 * @returns E_ACCESSDENIED if the VM process is not available or not
5800 * currently handling queries and the setting should then be done in
5801 * VBoxSVC.
5802 */
5803HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5804 std::vector<com::Utf8Str> &aNames,
5805 std::vector<com::Utf8Str> &aValues,
5806 std::vector<LONG64> &aTimestamps,
5807 std::vector<com::Utf8Str> &aFlags)
5808{
5809 HRESULT rc;
5810 ComPtr<IInternalSessionControl> directControl;
5811 {
5812 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5813 if (mData->mSession.mLockType == LockType_VM)
5814 directControl = mData->mSession.mDirectControl;
5815 }
5816
5817 com::SafeArray<BSTR> bNames;
5818 com::SafeArray<BSTR> bValues;
5819 com::SafeArray<LONG64> bTimestamps;
5820 com::SafeArray<BSTR> bFlags;
5821
5822 if (!directControl)
5823 rc = E_ACCESSDENIED;
5824 else
5825 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5826 ComSafeArrayAsOutParam(bNames),
5827 ComSafeArrayAsOutParam(bValues),
5828 ComSafeArrayAsOutParam(bTimestamps),
5829 ComSafeArrayAsOutParam(bFlags));
5830 size_t i;
5831 aNames.resize(bNames.size());
5832 for (i = 0; i < bNames.size(); ++i)
5833 aNames[i] = Utf8Str(bNames[i]);
5834 aValues.resize(bValues.size());
5835 for (i = 0; i < bValues.size(); ++i)
5836 aValues[i] = Utf8Str(bValues[i]);
5837 aTimestamps.resize(bTimestamps.size());
5838 for (i = 0; i < bTimestamps.size(); ++i)
5839 aTimestamps[i] = bTimestamps[i];
5840 aFlags.resize(bFlags.size());
5841 for (i = 0; i < bFlags.size(); ++i)
5842 aFlags[i] = Utf8Str(bFlags[i]);
5843
5844 return rc;
5845}
5846#endif // VBOX_WITH_GUEST_PROPS
5847HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5848 std::vector<com::Utf8Str> &aNames,
5849 std::vector<com::Utf8Str> &aValues,
5850 std::vector<LONG64> &aTimestamps,
5851 std::vector<com::Utf8Str> &aFlags)
5852{
5853#ifndef VBOX_WITH_GUEST_PROPS
5854 ReturnComNotImplemented();
5855#else // VBOX_WITH_GUEST_PROPS
5856
5857 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5858
5859 if (rc == E_ACCESSDENIED)
5860 /* The VM is not running or the service is not (yet) accessible */
5861 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5862 return rc;
5863#endif // VBOX_WITH_GUEST_PROPS
5864}
5865
5866HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5867 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5868{
5869 MediumAttachmentList atts;
5870
5871 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5872 if (FAILED(rc)) return rc;
5873
5874 aMediumAttachments.resize(atts.size());
5875 size_t i = 0;
5876 for (MediumAttachmentList::const_iterator
5877 it = atts.begin();
5878 it != atts.end();
5879 ++it, ++i)
5880 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5881
5882 return S_OK;
5883}
5884
5885HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5886 LONG aControllerPort,
5887 LONG aDevice,
5888 ComPtr<IMediumAttachment> &aAttachment)
5889{
5890 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5891 aName.c_str(), aControllerPort, aDevice));
5892
5893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5894
5895 aAttachment = NULL;
5896
5897 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5898 aName,
5899 aControllerPort,
5900 aDevice);
5901 if (pAttach.isNull())
5902 return setError(VBOX_E_OBJECT_NOT_FOUND,
5903 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5904 aDevice, aControllerPort, aName.c_str());
5905
5906 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5907
5908 return S_OK;
5909}
5910
5911
5912HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5913 StorageBus_T aConnectionType,
5914 ComPtr<IStorageController> &aController)
5915{
5916 if ( (aConnectionType <= StorageBus_Null)
5917 || (aConnectionType > StorageBus_VirtioSCSI))
5918 return setError(E_INVALIDARG,
5919 tr("Invalid connection type: %d"),
5920 aConnectionType);
5921
5922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5923
5924 HRESULT rc = i_checkStateDependency(MutableStateDep);
5925 if (FAILED(rc)) return rc;
5926
5927 /* try to find one with the name first. */
5928 ComObjPtr<StorageController> ctrl;
5929
5930 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5931 if (SUCCEEDED(rc))
5932 return setError(VBOX_E_OBJECT_IN_USE,
5933 tr("Storage controller named '%s' already exists"),
5934 aName.c_str());
5935
5936 ctrl.createObject();
5937
5938 /* get a new instance number for the storage controller */
5939 ULONG ulInstance = 0;
5940 bool fBootable = true;
5941 for (StorageControllerList::const_iterator
5942 it = mStorageControllers->begin();
5943 it != mStorageControllers->end();
5944 ++it)
5945 {
5946 if ((*it)->i_getStorageBus() == aConnectionType)
5947 {
5948 ULONG ulCurInst = (*it)->i_getInstance();
5949
5950 if (ulCurInst >= ulInstance)
5951 ulInstance = ulCurInst + 1;
5952
5953 /* Only one controller of each type can be marked as bootable. */
5954 if ((*it)->i_getBootable())
5955 fBootable = false;
5956 }
5957 }
5958
5959 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5960 if (FAILED(rc)) return rc;
5961
5962 i_setModified(IsModified_Storage);
5963 mStorageControllers.backup();
5964 mStorageControllers->push_back(ctrl);
5965
5966 ctrl.queryInterfaceTo(aController.asOutParam());
5967
5968 /* inform the direct session if any */
5969 alock.release();
5970 i_onStorageControllerChange(i_getId(), aName);
5971
5972 return S_OK;
5973}
5974
5975HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5976 ComPtr<IStorageController> &aStorageController)
5977{
5978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5979
5980 ComObjPtr<StorageController> ctrl;
5981
5982 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5983 if (SUCCEEDED(rc))
5984 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5985
5986 return rc;
5987}
5988
5989HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5990 ULONG aInstance,
5991 ComPtr<IStorageController> &aStorageController)
5992{
5993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5994
5995 for (StorageControllerList::const_iterator
5996 it = mStorageControllers->begin();
5997 it != mStorageControllers->end();
5998 ++it)
5999 {
6000 if ( (*it)->i_getStorageBus() == aConnectionType
6001 && (*it)->i_getInstance() == aInstance)
6002 {
6003 (*it).queryInterfaceTo(aStorageController.asOutParam());
6004 return S_OK;
6005 }
6006 }
6007
6008 return setError(VBOX_E_OBJECT_NOT_FOUND,
6009 tr("Could not find a storage controller with instance number '%lu'"),
6010 aInstance);
6011}
6012
6013HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6014{
6015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6016
6017 HRESULT rc = i_checkStateDependency(MutableStateDep);
6018 if (FAILED(rc)) return rc;
6019
6020 ComObjPtr<StorageController> ctrl;
6021
6022 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6023 if (SUCCEEDED(rc))
6024 {
6025 /* Ensure that only one controller of each type is marked as bootable. */
6026 if (aBootable == TRUE)
6027 {
6028 for (StorageControllerList::const_iterator
6029 it = mStorageControllers->begin();
6030 it != mStorageControllers->end();
6031 ++it)
6032 {
6033 ComObjPtr<StorageController> aCtrl = (*it);
6034
6035 if ( (aCtrl->i_getName() != aName)
6036 && aCtrl->i_getBootable() == TRUE
6037 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6038 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6039 {
6040 aCtrl->i_setBootable(FALSE);
6041 break;
6042 }
6043 }
6044 }
6045
6046 if (SUCCEEDED(rc))
6047 {
6048 ctrl->i_setBootable(aBootable);
6049 i_setModified(IsModified_Storage);
6050 }
6051 }
6052
6053 if (SUCCEEDED(rc))
6054 {
6055 /* inform the direct session if any */
6056 alock.release();
6057 i_onStorageControllerChange(i_getId(), aName);
6058 }
6059
6060 return rc;
6061}
6062
6063HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6064{
6065 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6066
6067 HRESULT rc = i_checkStateDependency(MutableStateDep);
6068 if (FAILED(rc)) return rc;
6069
6070 ComObjPtr<StorageController> ctrl;
6071 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6072 if (FAILED(rc)) return rc;
6073
6074 MediumAttachmentList llDetachedAttachments;
6075 {
6076 /* find all attached devices to the appropriate storage controller and detach them all */
6077 // make a temporary list because detachDevice invalidates iterators into
6078 // mMediumAttachments
6079 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6080
6081 for (MediumAttachmentList::const_iterator
6082 it = llAttachments2.begin();
6083 it != llAttachments2.end();
6084 ++it)
6085 {
6086 MediumAttachment *pAttachTemp = *it;
6087
6088 AutoCaller localAutoCaller(pAttachTemp);
6089 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6090
6091 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6092
6093 if (pAttachTemp->i_getControllerName() == aName)
6094 {
6095 llDetachedAttachments.push_back(pAttachTemp);
6096 rc = i_detachDevice(pAttachTemp, alock, NULL);
6097 if (FAILED(rc)) return rc;
6098 }
6099 }
6100 }
6101
6102 /* send event about detached devices before removing parent controller */
6103 for (MediumAttachmentList::const_iterator
6104 it = llDetachedAttachments.begin();
6105 it != llDetachedAttachments.end();
6106 ++it)
6107 {
6108 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6109 }
6110
6111 /* We can remove it now. */
6112 i_setModified(IsModified_Storage);
6113 mStorageControllers.backup();
6114
6115 ctrl->i_unshare();
6116
6117 mStorageControllers->remove(ctrl);
6118
6119 /* inform the direct session if any */
6120 alock.release();
6121 i_onStorageControllerChange(i_getId(), aName);
6122
6123 return S_OK;
6124}
6125
6126HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6127 ComPtr<IUSBController> &aController)
6128{
6129 if ( (aType <= USBControllerType_Null)
6130 || (aType >= USBControllerType_Last))
6131 return setError(E_INVALIDARG,
6132 tr("Invalid USB controller type: %d"),
6133 aType);
6134
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 HRESULT rc = i_checkStateDependency(MutableStateDep);
6138 if (FAILED(rc)) return rc;
6139
6140 /* try to find one with the same type first. */
6141 ComObjPtr<USBController> ctrl;
6142
6143 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6144 if (SUCCEEDED(rc))
6145 return setError(VBOX_E_OBJECT_IN_USE,
6146 tr("USB controller named '%s' already exists"),
6147 aName.c_str());
6148
6149 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6150 ULONG maxInstances;
6151 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6152 if (FAILED(rc))
6153 return rc;
6154
6155 ULONG cInstances = i_getUSBControllerCountByType(aType);
6156 if (cInstances >= maxInstances)
6157 return setError(E_INVALIDARG,
6158 tr("Too many USB controllers of this type"));
6159
6160 ctrl.createObject();
6161
6162 rc = ctrl->init(this, aName, aType);
6163 if (FAILED(rc)) return rc;
6164
6165 i_setModified(IsModified_USB);
6166 mUSBControllers.backup();
6167 mUSBControllers->push_back(ctrl);
6168
6169 ctrl.queryInterfaceTo(aController.asOutParam());
6170
6171 /* inform the direct session if any */
6172 alock.release();
6173 i_onUSBControllerChange();
6174
6175 return S_OK;
6176}
6177
6178HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6179{
6180 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6181
6182 ComObjPtr<USBController> ctrl;
6183
6184 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6185 if (SUCCEEDED(rc))
6186 ctrl.queryInterfaceTo(aController.asOutParam());
6187
6188 return rc;
6189}
6190
6191HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6192 ULONG *aControllers)
6193{
6194 if ( (aType <= USBControllerType_Null)
6195 || (aType >= USBControllerType_Last))
6196 return setError(E_INVALIDARG,
6197 tr("Invalid USB controller type: %d"),
6198 aType);
6199
6200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6201
6202 ComObjPtr<USBController> ctrl;
6203
6204 *aControllers = i_getUSBControllerCountByType(aType);
6205
6206 return S_OK;
6207}
6208
6209HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6210{
6211
6212 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6213
6214 HRESULT rc = i_checkStateDependency(MutableStateDep);
6215 if (FAILED(rc)) return rc;
6216
6217 ComObjPtr<USBController> ctrl;
6218 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6219 if (FAILED(rc)) return rc;
6220
6221 i_setModified(IsModified_USB);
6222 mUSBControllers.backup();
6223
6224 ctrl->i_unshare();
6225
6226 mUSBControllers->remove(ctrl);
6227
6228 /* inform the direct session if any */
6229 alock.release();
6230 i_onUSBControllerChange();
6231
6232 return S_OK;
6233}
6234
6235HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6236 ULONG *aOriginX,
6237 ULONG *aOriginY,
6238 ULONG *aWidth,
6239 ULONG *aHeight,
6240 BOOL *aEnabled)
6241{
6242 uint32_t u32OriginX= 0;
6243 uint32_t u32OriginY= 0;
6244 uint32_t u32Width = 0;
6245 uint32_t u32Height = 0;
6246 uint16_t u16Flags = 0;
6247
6248 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6249 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6250 if (RT_FAILURE(vrc))
6251 {
6252#ifdef RT_OS_WINDOWS
6253 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6254 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6255 * So just assign fEnable to TRUE again.
6256 * The right fix would be to change GUI API wrappers to make sure that parameters
6257 * are changed only if API succeeds.
6258 */
6259 *aEnabled = TRUE;
6260#endif
6261 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6262 tr("Saved guest size is not available (%Rrc)"),
6263 vrc);
6264 }
6265
6266 *aOriginX = u32OriginX;
6267 *aOriginY = u32OriginY;
6268 *aWidth = u32Width;
6269 *aHeight = u32Height;
6270 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6271
6272 return S_OK;
6273}
6274
6275HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6276 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6277{
6278 if (aScreenId != 0)
6279 return E_NOTIMPL;
6280
6281 if ( aBitmapFormat != BitmapFormat_BGR0
6282 && aBitmapFormat != BitmapFormat_BGRA
6283 && aBitmapFormat != BitmapFormat_RGBA
6284 && aBitmapFormat != BitmapFormat_PNG)
6285 return setError(E_NOTIMPL,
6286 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6287
6288 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6289
6290 uint8_t *pu8Data = NULL;
6291 uint32_t cbData = 0;
6292 uint32_t u32Width = 0;
6293 uint32_t u32Height = 0;
6294
6295 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6296
6297 if (RT_FAILURE(vrc))
6298 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6299 tr("Saved thumbnail data is not available (%Rrc)"),
6300 vrc);
6301
6302 HRESULT hr = S_OK;
6303
6304 *aWidth = u32Width;
6305 *aHeight = u32Height;
6306
6307 if (cbData > 0)
6308 {
6309 /* Convert pixels to the format expected by the API caller. */
6310 if (aBitmapFormat == BitmapFormat_BGR0)
6311 {
6312 /* [0] B, [1] G, [2] R, [3] 0. */
6313 aData.resize(cbData);
6314 memcpy(&aData.front(), pu8Data, cbData);
6315 }
6316 else if (aBitmapFormat == BitmapFormat_BGRA)
6317 {
6318 /* [0] B, [1] G, [2] R, [3] A. */
6319 aData.resize(cbData);
6320 for (uint32_t i = 0; i < cbData; i += 4)
6321 {
6322 aData[i] = pu8Data[i];
6323 aData[i + 1] = pu8Data[i + 1];
6324 aData[i + 2] = pu8Data[i + 2];
6325 aData[i + 3] = 0xff;
6326 }
6327 }
6328 else if (aBitmapFormat == BitmapFormat_RGBA)
6329 {
6330 /* [0] R, [1] G, [2] B, [3] A. */
6331 aData.resize(cbData);
6332 for (uint32_t i = 0; i < cbData; i += 4)
6333 {
6334 aData[i] = pu8Data[i + 2];
6335 aData[i + 1] = pu8Data[i + 1];
6336 aData[i + 2] = pu8Data[i];
6337 aData[i + 3] = 0xff;
6338 }
6339 }
6340 else if (aBitmapFormat == BitmapFormat_PNG)
6341 {
6342 uint8_t *pu8PNG = NULL;
6343 uint32_t cbPNG = 0;
6344 uint32_t cxPNG = 0;
6345 uint32_t cyPNG = 0;
6346
6347 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6348
6349 if (RT_SUCCESS(vrc))
6350 {
6351 aData.resize(cbPNG);
6352 if (cbPNG)
6353 memcpy(&aData.front(), pu8PNG, cbPNG);
6354 }
6355 else
6356 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6357 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6358 vrc);
6359
6360 RTMemFree(pu8PNG);
6361 }
6362 }
6363
6364 freeSavedDisplayScreenshot(pu8Data);
6365
6366 return hr;
6367}
6368
6369HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6370 ULONG *aWidth,
6371 ULONG *aHeight,
6372 std::vector<BitmapFormat_T> &aBitmapFormats)
6373{
6374 if (aScreenId != 0)
6375 return E_NOTIMPL;
6376
6377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6378
6379 uint8_t *pu8Data = NULL;
6380 uint32_t cbData = 0;
6381 uint32_t u32Width = 0;
6382 uint32_t u32Height = 0;
6383
6384 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6385
6386 if (RT_FAILURE(vrc))
6387 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6388 tr("Saved screenshot data is not available (%Rrc)"),
6389 vrc);
6390
6391 *aWidth = u32Width;
6392 *aHeight = u32Height;
6393 aBitmapFormats.resize(1);
6394 aBitmapFormats[0] = BitmapFormat_PNG;
6395
6396 freeSavedDisplayScreenshot(pu8Data);
6397
6398 return S_OK;
6399}
6400
6401HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6402 BitmapFormat_T aBitmapFormat,
6403 ULONG *aWidth,
6404 ULONG *aHeight,
6405 std::vector<BYTE> &aData)
6406{
6407 if (aScreenId != 0)
6408 return E_NOTIMPL;
6409
6410 if (aBitmapFormat != BitmapFormat_PNG)
6411 return E_NOTIMPL;
6412
6413 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6414
6415 uint8_t *pu8Data = NULL;
6416 uint32_t cbData = 0;
6417 uint32_t u32Width = 0;
6418 uint32_t u32Height = 0;
6419
6420 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6421
6422 if (RT_FAILURE(vrc))
6423 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6424 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6425 vrc);
6426
6427 *aWidth = u32Width;
6428 *aHeight = u32Height;
6429
6430 aData.resize(cbData);
6431 if (cbData)
6432 memcpy(&aData.front(), pu8Data, cbData);
6433
6434 freeSavedDisplayScreenshot(pu8Data);
6435
6436 return S_OK;
6437}
6438
6439HRESULT Machine::hotPlugCPU(ULONG aCpu)
6440{
6441 HRESULT rc = S_OK;
6442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6443
6444 if (!mHWData->mCPUHotPlugEnabled)
6445 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6446
6447 if (aCpu >= mHWData->mCPUCount)
6448 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6449
6450 if (mHWData->mCPUAttached[aCpu])
6451 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6452
6453 alock.release();
6454 rc = i_onCPUChange(aCpu, false);
6455 alock.acquire();
6456 if (FAILED(rc)) return rc;
6457
6458 i_setModified(IsModified_MachineData);
6459 mHWData.backup();
6460 mHWData->mCPUAttached[aCpu] = true;
6461
6462 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6463 if (Global::IsOnline(mData->mMachineState))
6464 i_saveSettings(NULL);
6465
6466 return S_OK;
6467}
6468
6469HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6470{
6471 HRESULT rc = S_OK;
6472
6473 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6474
6475 if (!mHWData->mCPUHotPlugEnabled)
6476 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6477
6478 if (aCpu >= SchemaDefs::MaxCPUCount)
6479 return setError(E_INVALIDARG,
6480 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6481 SchemaDefs::MaxCPUCount);
6482
6483 if (!mHWData->mCPUAttached[aCpu])
6484 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6485
6486 /* CPU 0 can't be detached */
6487 if (aCpu == 0)
6488 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6489
6490 alock.release();
6491 rc = i_onCPUChange(aCpu, true);
6492 alock.acquire();
6493 if (FAILED(rc)) return rc;
6494
6495 i_setModified(IsModified_MachineData);
6496 mHWData.backup();
6497 mHWData->mCPUAttached[aCpu] = false;
6498
6499 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6500 if (Global::IsOnline(mData->mMachineState))
6501 i_saveSettings(NULL);
6502
6503 return S_OK;
6504}
6505
6506HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6507{
6508 *aAttached = false;
6509
6510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6511
6512 /* If hotplug is enabled the CPU is always enabled. */
6513 if (!mHWData->mCPUHotPlugEnabled)
6514 {
6515 if (aCpu < mHWData->mCPUCount)
6516 *aAttached = true;
6517 }
6518 else
6519 {
6520 if (aCpu < SchemaDefs::MaxCPUCount)
6521 *aAttached = mHWData->mCPUAttached[aCpu];
6522 }
6523
6524 return S_OK;
6525}
6526
6527HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6528{
6529 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6530
6531 Utf8Str log = i_getLogFilename(aIdx);
6532 if (!RTFileExists(log.c_str()))
6533 log.setNull();
6534 aFilename = log;
6535
6536 return S_OK;
6537}
6538
6539HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6540{
6541 if (aSize < 0)
6542 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6543
6544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6545
6546 HRESULT rc = S_OK;
6547 Utf8Str log = i_getLogFilename(aIdx);
6548
6549 /* do not unnecessarily hold the lock while doing something which does
6550 * not need the lock and potentially takes a long time. */
6551 alock.release();
6552
6553 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6554 * keeps the SOAP reply size under 1M for the webservice (we're using
6555 * base64 encoded strings for binary data for years now, avoiding the
6556 * expansion of each byte array element to approx. 25 bytes of XML. */
6557 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6558 aData.resize(cbData);
6559
6560 RTFILE LogFile;
6561 int vrc = RTFileOpen(&LogFile, log.c_str(),
6562 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6563 if (RT_SUCCESS(vrc))
6564 {
6565 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6566 if (RT_SUCCESS(vrc))
6567 aData.resize(cbData);
6568 else
6569 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6570 tr("Could not read log file '%s' (%Rrc)"),
6571 log.c_str(), vrc);
6572 RTFileClose(LogFile);
6573 }
6574 else
6575 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6576 tr("Could not open log file '%s' (%Rrc)"),
6577 log.c_str(), vrc);
6578
6579 if (FAILED(rc))
6580 aData.resize(0);
6581
6582 return rc;
6583}
6584
6585
6586/**
6587 * Currently this method doesn't attach device to the running VM,
6588 * just makes sure it's plugged on next VM start.
6589 */
6590HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6591{
6592 // lock scope
6593 {
6594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6595
6596 HRESULT rc = i_checkStateDependency(MutableStateDep);
6597 if (FAILED(rc)) return rc;
6598
6599 ChipsetType_T aChipset = ChipsetType_PIIX3;
6600 COMGETTER(ChipsetType)(&aChipset);
6601
6602 if (aChipset != ChipsetType_ICH9)
6603 {
6604 return setError(E_INVALIDARG,
6605 tr("Host PCI attachment only supported with ICH9 chipset"));
6606 }
6607
6608 // check if device with this host PCI address already attached
6609 for (HWData::PCIDeviceAssignmentList::const_iterator
6610 it = mHWData->mPCIDeviceAssignments.begin();
6611 it != mHWData->mPCIDeviceAssignments.end();
6612 ++it)
6613 {
6614 LONG iHostAddress = -1;
6615 ComPtr<PCIDeviceAttachment> pAttach;
6616 pAttach = *it;
6617 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6618 if (iHostAddress == aHostAddress)
6619 return setError(E_INVALIDARG,
6620 tr("Device with host PCI address already attached to this VM"));
6621 }
6622
6623 ComObjPtr<PCIDeviceAttachment> pda;
6624 char name[32];
6625
6626 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6627 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6628 pda.createObject();
6629 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6630 i_setModified(IsModified_MachineData);
6631 mHWData.backup();
6632 mHWData->mPCIDeviceAssignments.push_back(pda);
6633 }
6634
6635 return S_OK;
6636}
6637
6638/**
6639 * Currently this method doesn't detach device from the running VM,
6640 * just makes sure it's not plugged on next VM start.
6641 */
6642HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6643{
6644 ComObjPtr<PCIDeviceAttachment> pAttach;
6645 bool fRemoved = false;
6646 HRESULT rc;
6647
6648 // lock scope
6649 {
6650 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6651
6652 rc = i_checkStateDependency(MutableStateDep);
6653 if (FAILED(rc)) return rc;
6654
6655 for (HWData::PCIDeviceAssignmentList::const_iterator
6656 it = mHWData->mPCIDeviceAssignments.begin();
6657 it != mHWData->mPCIDeviceAssignments.end();
6658 ++it)
6659 {
6660 LONG iHostAddress = -1;
6661 pAttach = *it;
6662 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6663 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6664 {
6665 i_setModified(IsModified_MachineData);
6666 mHWData.backup();
6667 mHWData->mPCIDeviceAssignments.remove(pAttach);
6668 fRemoved = true;
6669 break;
6670 }
6671 }
6672 }
6673
6674
6675 /* Fire event outside of the lock */
6676 if (fRemoved)
6677 {
6678 Assert(!pAttach.isNull());
6679 ComPtr<IEventSource> es;
6680 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6681 Assert(SUCCEEDED(rc));
6682 Bstr mid;
6683 rc = this->COMGETTER(Id)(mid.asOutParam());
6684 Assert(SUCCEEDED(rc));
6685 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6686 }
6687
6688 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6689 tr("No host PCI device %08x attached"),
6690 aHostAddress
6691 );
6692}
6693
6694HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6695{
6696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6697
6698 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6699 size_t i = 0;
6700 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6701 it = mHWData->mPCIDeviceAssignments.begin();
6702 it != mHWData->mPCIDeviceAssignments.end();
6703 ++it, ++i)
6704 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6705
6706 return S_OK;
6707}
6708
6709HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6710{
6711 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6712
6713 return S_OK;
6714}
6715
6716HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6717{
6718 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6719
6720 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6721
6722 return S_OK;
6723}
6724
6725HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6726{
6727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6728 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6729 if (SUCCEEDED(hrc))
6730 {
6731 hrc = mHWData.backupEx();
6732 if (SUCCEEDED(hrc))
6733 {
6734 i_setModified(IsModified_MachineData);
6735 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6736 }
6737 }
6738 return hrc;
6739}
6740
6741HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6742{
6743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6744 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6745 return S_OK;
6746}
6747
6748HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6749{
6750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6751 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6752 if (SUCCEEDED(hrc))
6753 {
6754 hrc = mHWData.backupEx();
6755 if (SUCCEEDED(hrc))
6756 {
6757 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6758 if (SUCCEEDED(hrc))
6759 i_setModified(IsModified_MachineData);
6760 }
6761 }
6762 return hrc;
6763}
6764
6765HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6766{
6767 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6768
6769 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6770
6771 return S_OK;
6772}
6773
6774HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6775{
6776 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6777 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6778 if (SUCCEEDED(hrc))
6779 {
6780 hrc = mHWData.backupEx();
6781 if (SUCCEEDED(hrc))
6782 {
6783 i_setModified(IsModified_MachineData);
6784 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6785 }
6786 }
6787 return hrc;
6788}
6789
6790HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6791{
6792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6793
6794 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6795
6796 return S_OK;
6797}
6798
6799HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6800{
6801 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6802
6803 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6804 if ( SUCCEEDED(hrc)
6805 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6806 {
6807 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6808 int vrc;
6809
6810 if (aAutostartEnabled)
6811 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6812 else
6813 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6814
6815 if (RT_SUCCESS(vrc))
6816 {
6817 hrc = mHWData.backupEx();
6818 if (SUCCEEDED(hrc))
6819 {
6820 i_setModified(IsModified_MachineData);
6821 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6822 }
6823 }
6824 else if (vrc == VERR_NOT_SUPPORTED)
6825 hrc = setError(VBOX_E_NOT_SUPPORTED,
6826 tr("The VM autostart feature is not supported on this platform"));
6827 else if (vrc == VERR_PATH_NOT_FOUND)
6828 hrc = setError(E_FAIL,
6829 tr("The path to the autostart database is not set"));
6830 else
6831 hrc = setError(E_UNEXPECTED,
6832 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6833 aAutostartEnabled ? "Adding" : "Removing",
6834 mUserData->s.strName.c_str(), vrc);
6835 }
6836 return hrc;
6837}
6838
6839HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6840{
6841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6842
6843 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6844
6845 return S_OK;
6846}
6847
6848HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6849{
6850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6851 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6852 if (SUCCEEDED(hrc))
6853 {
6854 hrc = mHWData.backupEx();
6855 if (SUCCEEDED(hrc))
6856 {
6857 i_setModified(IsModified_MachineData);
6858 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6859 }
6860 }
6861 return hrc;
6862}
6863
6864HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6865{
6866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6867
6868 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6869
6870 return S_OK;
6871}
6872
6873HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6874{
6875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6876 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6877 if ( SUCCEEDED(hrc)
6878 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6879 {
6880 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6881 int vrc;
6882
6883 if (aAutostopType != AutostopType_Disabled)
6884 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6885 else
6886 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6887
6888 if (RT_SUCCESS(vrc))
6889 {
6890 hrc = mHWData.backupEx();
6891 if (SUCCEEDED(hrc))
6892 {
6893 i_setModified(IsModified_MachineData);
6894 mHWData->mAutostart.enmAutostopType = aAutostopType;
6895 }
6896 }
6897 else if (vrc == VERR_NOT_SUPPORTED)
6898 hrc = setError(VBOX_E_NOT_SUPPORTED,
6899 tr("The VM autostop feature is not supported on this platform"));
6900 else if (vrc == VERR_PATH_NOT_FOUND)
6901 hrc = setError(E_FAIL,
6902 tr("The path to the autostart database is not set"));
6903 else
6904 hrc = setError(E_UNEXPECTED,
6905 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6906 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6907 mUserData->s.strName.c_str(), vrc);
6908 }
6909 return hrc;
6910}
6911
6912HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6913{
6914 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6915
6916 aDefaultFrontend = mHWData->mDefaultFrontend;
6917
6918 return S_OK;
6919}
6920
6921HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6922{
6923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6924 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6925 if (SUCCEEDED(hrc))
6926 {
6927 hrc = mHWData.backupEx();
6928 if (SUCCEEDED(hrc))
6929 {
6930 i_setModified(IsModified_MachineData);
6931 mHWData->mDefaultFrontend = aDefaultFrontend;
6932 }
6933 }
6934 return hrc;
6935}
6936
6937HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6938{
6939 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6940 size_t cbIcon = mUserData->s.ovIcon.size();
6941 aIcon.resize(cbIcon);
6942 if (cbIcon)
6943 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6944 return S_OK;
6945}
6946
6947HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6948{
6949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6950 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6951 if (SUCCEEDED(hrc))
6952 {
6953 i_setModified(IsModified_MachineData);
6954 mUserData.backup();
6955 size_t cbIcon = aIcon.size();
6956 mUserData->s.ovIcon.resize(cbIcon);
6957 if (cbIcon)
6958 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6959 }
6960 return hrc;
6961}
6962
6963HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6964{
6965#ifdef VBOX_WITH_USB
6966 *aUSBProxyAvailable = true;
6967#else
6968 *aUSBProxyAvailable = false;
6969#endif
6970 return S_OK;
6971}
6972
6973HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6974{
6975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6976
6977 *aVMProcessPriority = mUserData->s.enmVMPriority;
6978
6979 return S_OK;
6980}
6981
6982HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6983{
6984 RT_NOREF(aVMProcessPriority);
6985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6986 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6987 if (SUCCEEDED(hrc))
6988 {
6989 hrc = mUserData.backupEx();
6990 if (SUCCEEDED(hrc))
6991 {
6992 i_setModified(IsModified_MachineData);
6993 mUserData->s.enmVMPriority = aVMProcessPriority;
6994 }
6995 }
6996 alock.release();
6997 if (SUCCEEDED(hrc))
6998 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6999 return hrc;
7000}
7001
7002HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7003 ComPtr<IProgress> &aProgress)
7004{
7005 ComObjPtr<Progress> pP;
7006 Progress *ppP = pP;
7007 IProgress *iP = static_cast<IProgress *>(ppP);
7008 IProgress **pProgress = &iP;
7009
7010 IMachine *pTarget = aTarget;
7011
7012 /* Convert the options. */
7013 RTCList<CloneOptions_T> optList;
7014 if (aOptions.size())
7015 for (size_t i = 0; i < aOptions.size(); ++i)
7016 optList.append(aOptions[i]);
7017
7018 if (optList.contains(CloneOptions_Link))
7019 {
7020 if (!i_isSnapshotMachine())
7021 return setError(E_INVALIDARG,
7022 tr("Linked clone can only be created from a snapshot"));
7023 if (aMode != CloneMode_MachineState)
7024 return setError(E_INVALIDARG,
7025 tr("Linked clone can only be created for a single machine state"));
7026 }
7027 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7028
7029 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7030
7031 HRESULT rc = pWorker->start(pProgress);
7032
7033 pP = static_cast<Progress *>(*pProgress);
7034 pP.queryInterfaceTo(aProgress.asOutParam());
7035
7036 return rc;
7037
7038}
7039
7040HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7041 const com::Utf8Str &aType,
7042 ComPtr<IProgress> &aProgress)
7043{
7044 LogFlowThisFuncEnter();
7045
7046 ComObjPtr<Progress> ptrProgress;
7047 HRESULT hrc = ptrProgress.createObject();
7048 if (SUCCEEDED(hrc))
7049 {
7050 /* Initialize our worker task */
7051 MachineMoveVM *pTask = NULL;
7052 try
7053 {
7054 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7055 }
7056 catch (std::bad_alloc &)
7057 {
7058 return E_OUTOFMEMORY;
7059 }
7060
7061 hrc = pTask->init();//no exceptions are thrown
7062
7063 if (SUCCEEDED(hrc))
7064 {
7065 hrc = pTask->createThread();
7066 pTask = NULL; /* Consumed by createThread(). */
7067 if (SUCCEEDED(hrc))
7068 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7069 else
7070 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7071 }
7072 else
7073 delete pTask;
7074 }
7075
7076 LogFlowThisFuncLeave();
7077 return hrc;
7078
7079}
7080
7081HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7082{
7083 NOREF(aProgress);
7084 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7085
7086 // This check should always fail.
7087 HRESULT rc = i_checkStateDependency(MutableStateDep);
7088 if (FAILED(rc)) return rc;
7089
7090 AssertFailedReturn(E_NOTIMPL);
7091}
7092
7093HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7094{
7095 NOREF(aSavedStateFile);
7096 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7097
7098 // This check should always fail.
7099 HRESULT rc = i_checkStateDependency(MutableStateDep);
7100 if (FAILED(rc)) return rc;
7101
7102 AssertFailedReturn(E_NOTIMPL);
7103}
7104
7105HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7106{
7107 NOREF(aFRemoveFile);
7108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7109
7110 // This check should always fail.
7111 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7112 if (FAILED(rc)) return rc;
7113
7114 AssertFailedReturn(E_NOTIMPL);
7115}
7116
7117// public methods for internal purposes
7118/////////////////////////////////////////////////////////////////////////////
7119
7120/**
7121 * Adds the given IsModified_* flag to the dirty flags of the machine.
7122 * This must be called either during i_loadSettings or under the machine write lock.
7123 * @param fl Flag
7124 * @param fAllowStateModification If state modifications are allowed.
7125 */
7126void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7127{
7128 mData->flModifications |= fl;
7129 if (fAllowStateModification && i_isStateModificationAllowed())
7130 mData->mCurrentStateModified = true;
7131}
7132
7133/**
7134 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7135 * care of the write locking.
7136 *
7137 * @param fModification The flag to add.
7138 * @param fAllowStateModification If state modifications are allowed.
7139 */
7140void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7141{
7142 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7143 i_setModified(fModification, fAllowStateModification);
7144}
7145
7146/**
7147 * Saves the registry entry of this machine to the given configuration node.
7148 *
7149 * @param data Machine registry data.
7150 *
7151 * @note locks this object for reading.
7152 */
7153HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7154{
7155 AutoLimitedCaller autoCaller(this);
7156 AssertComRCReturnRC(autoCaller.rc());
7157
7158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7159
7160 data.uuid = mData->mUuid;
7161 data.strSettingsFile = mData->m_strConfigFile;
7162
7163 return S_OK;
7164}
7165
7166/**
7167 * Calculates the absolute path of the given path taking the directory of the
7168 * machine settings file as the current directory.
7169 *
7170 * @param strPath Path to calculate the absolute path for.
7171 * @param aResult Where to put the result (used only on success, can be the
7172 * same Utf8Str instance as passed in @a aPath).
7173 * @return IPRT result.
7174 *
7175 * @note Locks this object for reading.
7176 */
7177int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7178{
7179 AutoCaller autoCaller(this);
7180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7181
7182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7183
7184 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7185
7186 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7187
7188 strSettingsDir.stripFilename();
7189 char szFolder[RTPATH_MAX];
7190 size_t cbFolder = sizeof(szFolder);
7191 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7192 if (RT_SUCCESS(vrc))
7193 aResult = szFolder;
7194
7195 return vrc;
7196}
7197
7198/**
7199 * Copies strSource to strTarget, making it relative to the machine folder
7200 * if it is a subdirectory thereof, or simply copying it otherwise.
7201 *
7202 * @param strSource Path to evaluate and copy.
7203 * @param strTarget Buffer to receive target path.
7204 *
7205 * @note Locks this object for reading.
7206 */
7207void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7208 Utf8Str &strTarget)
7209{
7210 AutoCaller autoCaller(this);
7211 AssertComRCReturn(autoCaller.rc(), (void)0);
7212
7213 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7214
7215 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7216 // use strTarget as a temporary buffer to hold the machine settings dir
7217 strTarget = mData->m_strConfigFileFull;
7218 strTarget.stripFilename();
7219 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7220 {
7221 // is relative: then append what's left
7222 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7223 // for empty paths (only possible for subdirs) use "." to avoid
7224 // triggering default settings for not present config attributes.
7225 if (strTarget.isEmpty())
7226 strTarget = ".";
7227 }
7228 else
7229 // is not relative: then overwrite
7230 strTarget = strSource;
7231}
7232
7233/**
7234 * Returns the full path to the machine's log folder in the
7235 * \a aLogFolder argument.
7236 */
7237void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7238{
7239 AutoCaller autoCaller(this);
7240 AssertComRCReturnVoid(autoCaller.rc());
7241
7242 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7243
7244 char szTmp[RTPATH_MAX];
7245 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7246 if (RT_SUCCESS(vrc))
7247 {
7248 if (szTmp[0] && !mUserData.isNull())
7249 {
7250 char szTmp2[RTPATH_MAX];
7251 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7252 if (RT_SUCCESS(vrc))
7253 aLogFolder = Utf8StrFmt("%s%c%s",
7254 szTmp2,
7255 RTPATH_DELIMITER,
7256 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7257 }
7258 else
7259 vrc = VERR_PATH_IS_RELATIVE;
7260 }
7261
7262 if (RT_FAILURE(vrc))
7263 {
7264 // fallback if VBOX_USER_LOGHOME is not set or invalid
7265 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7266 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7267 aLogFolder.append(RTPATH_DELIMITER);
7268 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7269 }
7270}
7271
7272/**
7273 * Returns the full path to the machine's log file for an given index.
7274 */
7275Utf8Str Machine::i_getLogFilename(ULONG idx)
7276{
7277 Utf8Str logFolder;
7278 getLogFolder(logFolder);
7279 Assert(logFolder.length());
7280
7281 Utf8Str log;
7282 if (idx == 0)
7283 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7284#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7285 else if (idx == 1)
7286 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7287 else
7288 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7289#else
7290 else
7291 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7292#endif
7293 return log;
7294}
7295
7296/**
7297 * Returns the full path to the machine's hardened log file.
7298 */
7299Utf8Str Machine::i_getHardeningLogFilename(void)
7300{
7301 Utf8Str strFilename;
7302 getLogFolder(strFilename);
7303 Assert(strFilename.length());
7304 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7305 return strFilename;
7306}
7307
7308
7309/**
7310 * Composes a unique saved state filename based on the current system time. The filename is
7311 * granular to the second so this will work so long as no more than one snapshot is taken on
7312 * a machine per second.
7313 *
7314 * Before version 4.1, we used this formula for saved state files:
7315 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7316 * which no longer works because saved state files can now be shared between the saved state of the
7317 * "saved" machine and an online snapshot, and the following would cause problems:
7318 * 1) save machine
7319 * 2) create online snapshot from that machine state --> reusing saved state file
7320 * 3) save machine again --> filename would be reused, breaking the online snapshot
7321 *
7322 * So instead we now use a timestamp.
7323 *
7324 * @param strStateFilePath
7325 */
7326
7327void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7328{
7329 AutoCaller autoCaller(this);
7330 AssertComRCReturnVoid(autoCaller.rc());
7331
7332 {
7333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7334 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7335 }
7336
7337 RTTIMESPEC ts;
7338 RTTimeNow(&ts);
7339 RTTIME time;
7340 RTTimeExplode(&time, &ts);
7341
7342 strStateFilePath += RTPATH_DELIMITER;
7343 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7344 time.i32Year, time.u8Month, time.u8MonthDay,
7345 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7346}
7347
7348/**
7349 * Returns whether at least one USB controller is present for the VM.
7350 */
7351bool Machine::i_isUSBControllerPresent()
7352{
7353 AutoCaller autoCaller(this);
7354 AssertComRCReturn(autoCaller.rc(), false);
7355
7356 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7357
7358 return (mUSBControllers->size() > 0);
7359}
7360
7361/**
7362 * @note Locks this object for writing, calls the client process
7363 * (inside the lock).
7364 */
7365HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7366 const Utf8Str &strFrontend,
7367 const Utf8Str &strEnvironment,
7368 ProgressProxy *aProgress)
7369{
7370 LogFlowThisFuncEnter();
7371
7372 AssertReturn(aControl, E_FAIL);
7373 AssertReturn(aProgress, E_FAIL);
7374 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7375
7376 AutoCaller autoCaller(this);
7377 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7378
7379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 if (!mData->mRegistered)
7382 return setError(E_UNEXPECTED,
7383 tr("The machine '%s' is not registered"),
7384 mUserData->s.strName.c_str());
7385
7386 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7387
7388 /* The process started when launching a VM with separate UI/VM processes is always
7389 * the UI process, i.e. needs special handling as it won't claim the session. */
7390 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7391
7392 if (fSeparate)
7393 {
7394 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7395 return setError(VBOX_E_INVALID_OBJECT_STATE,
7396 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7397 mUserData->s.strName.c_str());
7398 }
7399 else
7400 {
7401 if ( mData->mSession.mState == SessionState_Locked
7402 || mData->mSession.mState == SessionState_Spawning
7403 || mData->mSession.mState == SessionState_Unlocking)
7404 return setError(VBOX_E_INVALID_OBJECT_STATE,
7405 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7406 mUserData->s.strName.c_str());
7407
7408 /* may not be busy */
7409 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7410 }
7411
7412 /* Hardening logging */
7413#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7414 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7415 {
7416 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7417 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7418 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7419 {
7420 Utf8Str strStartupLogDir = strHardeningLogFile;
7421 strStartupLogDir.stripFilename();
7422 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7423 file without stripping the file. */
7424 }
7425 strSupHardeningLogArg.append(strHardeningLogFile);
7426
7427 /* Remove legacy log filename to avoid confusion. */
7428 Utf8Str strOldStartupLogFile;
7429 getLogFolder(strOldStartupLogFile);
7430 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7431 RTFileDelete(strOldStartupLogFile.c_str());
7432 }
7433#else
7434 Utf8Str strSupHardeningLogArg;
7435#endif
7436
7437 Utf8Str strAppOverride;
7438#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7439 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7440#endif
7441
7442 bool fUseVBoxSDS = false;
7443 Utf8Str strCanonicalName;
7444 if (false)
7445 { }
7446#ifdef VBOX_WITH_QTGUI
7447 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7448 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7449 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7450 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7451 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7452 {
7453 strCanonicalName = "GUI/Qt";
7454 fUseVBoxSDS = true;
7455 }
7456#endif
7457#ifdef VBOX_WITH_VBOXSDL
7458 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7459 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7460 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7461 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7462 {
7463 strCanonicalName = "GUI/SDL";
7464 fUseVBoxSDS = true;
7465 }
7466#endif
7467#ifdef VBOX_WITH_HEADLESS
7468 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7469 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7470 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7471 {
7472 strCanonicalName = "headless";
7473 }
7474#endif
7475 else
7476 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7477
7478 Utf8Str idStr = mData->mUuid.toString();
7479 Utf8Str const &strMachineName = mUserData->s.strName;
7480 RTPROCESS pid = NIL_RTPROCESS;
7481
7482#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7483 RT_NOREF(fUseVBoxSDS);
7484#else
7485 DWORD idCallerSession = ~(DWORD)0;
7486 if (fUseVBoxSDS)
7487 {
7488 /*
7489 * The VBoxSDS should be used for process launching the VM with
7490 * GUI only if the caller and the VBoxSDS are in different Windows
7491 * sessions and the caller in the interactive one.
7492 */
7493 fUseVBoxSDS = false;
7494
7495 /* Get windows session of the current process. The process token used
7496 due to several reasons:
7497 1. The token is absent for the current thread except someone set it
7498 for us.
7499 2. Needs to get the id of the session where the process is started.
7500 We only need to do this once, though. */
7501 static DWORD s_idCurrentSession = ~(DWORD)0;
7502 DWORD idCurrentSession = s_idCurrentSession;
7503 if (idCurrentSession == ~(DWORD)0)
7504 {
7505 HANDLE hCurrentProcessToken = NULL;
7506 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7507 {
7508 DWORD cbIgn = 0;
7509 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7510 s_idCurrentSession = idCurrentSession;
7511 else
7512 {
7513 idCurrentSession = ~(DWORD)0;
7514 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7515 }
7516 CloseHandle(hCurrentProcessToken);
7517 }
7518 else
7519 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7520 }
7521
7522 /* get the caller's session */
7523 HRESULT hrc = CoImpersonateClient();
7524 if (SUCCEEDED(hrc))
7525 {
7526 HANDLE hCallerThreadToken;
7527 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7528 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7529 &hCallerThreadToken))
7530 {
7531 SetLastError(NO_ERROR);
7532 DWORD cbIgn = 0;
7533 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7534 {
7535 /* Only need to use SDS if the session ID differs: */
7536 if (idCurrentSession != idCallerSession)
7537 {
7538 fUseVBoxSDS = false;
7539
7540 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7541 DWORD cbTokenGroups = 0;
7542 PTOKEN_GROUPS pTokenGroups = NULL;
7543 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7544 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7545 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7546 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7547 {
7548 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7549 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7550 PSID pInteractiveSid = NULL;
7551 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7552 {
7553 /* Iterate over the groups looking for the interactive SID: */
7554 fUseVBoxSDS = false;
7555 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7556 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7557 {
7558 fUseVBoxSDS = true;
7559 break;
7560 }
7561 FreeSid(pInteractiveSid);
7562 }
7563 }
7564 else
7565 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7566 RTMemTmpFree(pTokenGroups);
7567 }
7568 }
7569 else
7570 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7571 CloseHandle(hCallerThreadToken);
7572 }
7573 else
7574 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7575 CoRevertToSelf();
7576 }
7577 else
7578 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7579 }
7580 if (fUseVBoxSDS)
7581 {
7582 /* connect to VBoxSDS */
7583 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7584 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7585 if (FAILED(rc))
7586 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7587 strMachineName.c_str());
7588
7589 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7590 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7591 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7592 service to access the files. */
7593 rc = CoSetProxyBlanket(pVBoxSDS,
7594 RPC_C_AUTHN_DEFAULT,
7595 RPC_C_AUTHZ_DEFAULT,
7596 COLE_DEFAULT_PRINCIPAL,
7597 RPC_C_AUTHN_LEVEL_DEFAULT,
7598 RPC_C_IMP_LEVEL_IMPERSONATE,
7599 NULL,
7600 EOAC_DEFAULT);
7601 if (FAILED(rc))
7602 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7603 ULONG uPid = 0;
7604 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7605 Bstr(strEnvironment).raw(), Bstr(strSupHardeningLogArg).raw(),
7606 idCallerSession, &uPid);
7607 if (FAILED(rc))
7608 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7609 pid = (RTPROCESS)uPid;
7610 }
7611 else
7612#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7613 {
7614 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, strEnvironment, strSupHardeningLogArg,
7615 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7616 if (RT_FAILURE(vrc))
7617 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7618 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7619 }
7620
7621 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7622 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7623
7624 if (!fSeparate)
7625 {
7626 /*
7627 * Note that we don't release the lock here before calling the client,
7628 * because it doesn't need to call us back if called with a NULL argument.
7629 * Releasing the lock here is dangerous because we didn't prepare the
7630 * launch data yet, but the client we've just started may happen to be
7631 * too fast and call LockMachine() that will fail (because of PID, etc.),
7632 * so that the Machine will never get out of the Spawning session state.
7633 */
7634
7635 /* inform the session that it will be a remote one */
7636 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7637#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7638 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7639#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7640 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7641#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7642 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7643
7644 if (FAILED(rc))
7645 {
7646 /* restore the session state */
7647 mData->mSession.mState = SessionState_Unlocked;
7648 alock.release();
7649 mParent->i_addProcessToReap(pid);
7650 /* The failure may occur w/o any error info (from RPC), so provide one */
7651 return setError(VBOX_E_VM_ERROR,
7652 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7653 }
7654
7655 /* attach launch data to the machine */
7656 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7657 mData->mSession.mRemoteControls.push_back(aControl);
7658 mData->mSession.mProgress = aProgress;
7659 mData->mSession.mPID = pid;
7660 mData->mSession.mState = SessionState_Spawning;
7661 Assert(strCanonicalName.isNotEmpty());
7662 mData->mSession.mName = strCanonicalName;
7663 }
7664 else
7665 {
7666 /* For separate UI process we declare the launch as completed instantly, as the
7667 * actual headless VM start may or may not come. No point in remembering anything
7668 * yet, as what matters for us is when the headless VM gets started. */
7669 aProgress->i_notifyComplete(S_OK);
7670 }
7671
7672 alock.release();
7673 mParent->i_addProcessToReap(pid);
7674
7675 LogFlowThisFuncLeave();
7676 return S_OK;
7677}
7678
7679/**
7680 * Returns @c true if the given session machine instance has an open direct
7681 * session (and optionally also for direct sessions which are closing) and
7682 * returns the session control machine instance if so.
7683 *
7684 * Note that when the method returns @c false, the arguments remain unchanged.
7685 *
7686 * @param aMachine Session machine object.
7687 * @param aControl Direct session control object (optional).
7688 * @param aRequireVM If true then only allow VM sessions.
7689 * @param aAllowClosing If true then additionally a session which is currently
7690 * being closed will also be allowed.
7691 *
7692 * @note locks this object for reading.
7693 */
7694bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7695 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7696 bool aRequireVM /*= false*/,
7697 bool aAllowClosing /*= false*/)
7698{
7699 AutoLimitedCaller autoCaller(this);
7700 AssertComRCReturn(autoCaller.rc(), false);
7701
7702 /* just return false for inaccessible machines */
7703 if (getObjectState().getState() != ObjectState::Ready)
7704 return false;
7705
7706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7707
7708 if ( ( mData->mSession.mState == SessionState_Locked
7709 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7710 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7711 )
7712 {
7713 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7714
7715 aMachine = mData->mSession.mMachine;
7716
7717 if (aControl != NULL)
7718 *aControl = mData->mSession.mDirectControl;
7719
7720 return true;
7721 }
7722
7723 return false;
7724}
7725
7726/**
7727 * Returns @c true if the given machine has an spawning direct session.
7728 *
7729 * @note locks this object for reading.
7730 */
7731bool Machine::i_isSessionSpawning()
7732{
7733 AutoLimitedCaller autoCaller(this);
7734 AssertComRCReturn(autoCaller.rc(), false);
7735
7736 /* just return false for inaccessible machines */
7737 if (getObjectState().getState() != ObjectState::Ready)
7738 return false;
7739
7740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7741
7742 if (mData->mSession.mState == SessionState_Spawning)
7743 return true;
7744
7745 return false;
7746}
7747
7748/**
7749 * Called from the client watcher thread to check for unexpected client process
7750 * death during Session_Spawning state (e.g. before it successfully opened a
7751 * direct session).
7752 *
7753 * On Win32 and on OS/2, this method is called only when we've got the
7754 * direct client's process termination notification, so it always returns @c
7755 * true.
7756 *
7757 * On other platforms, this method returns @c true if the client process is
7758 * terminated and @c false if it's still alive.
7759 *
7760 * @note Locks this object for writing.
7761 */
7762bool Machine::i_checkForSpawnFailure()
7763{
7764 AutoCaller autoCaller(this);
7765 if (!autoCaller.isOk())
7766 {
7767 /* nothing to do */
7768 LogFlowThisFunc(("Already uninitialized!\n"));
7769 return true;
7770 }
7771
7772 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7773
7774 if (mData->mSession.mState != SessionState_Spawning)
7775 {
7776 /* nothing to do */
7777 LogFlowThisFunc(("Not spawning any more!\n"));
7778 return true;
7779 }
7780
7781 HRESULT rc = S_OK;
7782
7783 /* PID not yet initialized, skip check. */
7784 if (mData->mSession.mPID == NIL_RTPROCESS)
7785 return false;
7786
7787 RTPROCSTATUS status;
7788 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7789
7790 if (vrc != VERR_PROCESS_RUNNING)
7791 {
7792 Utf8Str strExtraInfo;
7793
7794#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7795 /* If the startup logfile exists and is of non-zero length, tell the
7796 user to look there for more details to encourage them to attach it
7797 when reporting startup issues. */
7798 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7799 uint64_t cbStartupLogFile = 0;
7800 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7801 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7802 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7803#endif
7804
7805 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7806 rc = setError(E_FAIL,
7807 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7808 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7809 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7810 rc = setError(E_FAIL,
7811 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7812 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7813 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7814 rc = setError(E_FAIL,
7815 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7816 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7817 else
7818 rc = setErrorBoth(E_FAIL, vrc,
7819 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7820 i_getName().c_str(), vrc, strExtraInfo.c_str());
7821 }
7822
7823 if (FAILED(rc))
7824 {
7825 /* Close the remote session, remove the remote control from the list
7826 * and reset session state to Closed (@note keep the code in sync with
7827 * the relevant part in LockMachine()). */
7828
7829 Assert(mData->mSession.mRemoteControls.size() == 1);
7830 if (mData->mSession.mRemoteControls.size() == 1)
7831 {
7832 ErrorInfoKeeper eik;
7833 mData->mSession.mRemoteControls.front()->Uninitialize();
7834 }
7835
7836 mData->mSession.mRemoteControls.clear();
7837 mData->mSession.mState = SessionState_Unlocked;
7838
7839 /* finalize the progress after setting the state */
7840 if (!mData->mSession.mProgress.isNull())
7841 {
7842 mData->mSession.mProgress->notifyComplete(rc);
7843 mData->mSession.mProgress.setNull();
7844 }
7845
7846 mData->mSession.mPID = NIL_RTPROCESS;
7847
7848 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7849 return true;
7850 }
7851
7852 return false;
7853}
7854
7855/**
7856 * Checks whether the machine can be registered. If so, commits and saves
7857 * all settings.
7858 *
7859 * @note Must be called from mParent's write lock. Locks this object and
7860 * children for writing.
7861 */
7862HRESULT Machine::i_prepareRegister()
7863{
7864 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7865
7866 AutoLimitedCaller autoCaller(this);
7867 AssertComRCReturnRC(autoCaller.rc());
7868
7869 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7870
7871 /* wait for state dependents to drop to zero */
7872 i_ensureNoStateDependencies();
7873
7874 if (!mData->mAccessible)
7875 return setError(VBOX_E_INVALID_OBJECT_STATE,
7876 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7877 mUserData->s.strName.c_str(),
7878 mData->mUuid.toString().c_str());
7879
7880 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7881
7882 if (mData->mRegistered)
7883 return setError(VBOX_E_INVALID_OBJECT_STATE,
7884 tr("The machine '%s' with UUID {%s} is already registered"),
7885 mUserData->s.strName.c_str(),
7886 mData->mUuid.toString().c_str());
7887
7888 HRESULT rc = S_OK;
7889
7890 // Ensure the settings are saved. If we are going to be registered and
7891 // no config file exists yet, create it by calling i_saveSettings() too.
7892 if ( (mData->flModifications)
7893 || (!mData->pMachineConfigFile->fileExists())
7894 )
7895 {
7896 rc = i_saveSettings(NULL);
7897 // no need to check whether VirtualBox.xml needs saving too since
7898 // we can't have a machine XML file rename pending
7899 if (FAILED(rc)) return rc;
7900 }
7901
7902 /* more config checking goes here */
7903
7904 if (SUCCEEDED(rc))
7905 {
7906 /* we may have had implicit modifications we want to fix on success */
7907 i_commit();
7908
7909 mData->mRegistered = true;
7910 }
7911 else
7912 {
7913 /* we may have had implicit modifications we want to cancel on failure*/
7914 i_rollback(false /* aNotify */);
7915 }
7916
7917 return rc;
7918}
7919
7920/**
7921 * Increases the number of objects dependent on the machine state or on the
7922 * registered state. Guarantees that these two states will not change at least
7923 * until #i_releaseStateDependency() is called.
7924 *
7925 * Depending on the @a aDepType value, additional state checks may be made.
7926 * These checks will set extended error info on failure. See
7927 * #i_checkStateDependency() for more info.
7928 *
7929 * If this method returns a failure, the dependency is not added and the caller
7930 * is not allowed to rely on any particular machine state or registration state
7931 * value and may return the failed result code to the upper level.
7932 *
7933 * @param aDepType Dependency type to add.
7934 * @param aState Current machine state (NULL if not interested).
7935 * @param aRegistered Current registered state (NULL if not interested).
7936 *
7937 * @note Locks this object for writing.
7938 */
7939HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7940 MachineState_T *aState /* = NULL */,
7941 BOOL *aRegistered /* = NULL */)
7942{
7943 AutoCaller autoCaller(this);
7944 AssertComRCReturnRC(autoCaller.rc());
7945
7946 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7947
7948 HRESULT rc = i_checkStateDependency(aDepType);
7949 if (FAILED(rc)) return rc;
7950
7951 {
7952 if (mData->mMachineStateChangePending != 0)
7953 {
7954 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7955 * drop to zero so don't add more. It may make sense to wait a bit
7956 * and retry before reporting an error (since the pending state
7957 * transition should be really quick) but let's just assert for
7958 * now to see if it ever happens on practice. */
7959
7960 AssertFailed();
7961
7962 return setError(E_ACCESSDENIED,
7963 tr("Machine state change is in progress. Please retry the operation later."));
7964 }
7965
7966 ++mData->mMachineStateDeps;
7967 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7968 }
7969
7970 if (aState)
7971 *aState = mData->mMachineState;
7972 if (aRegistered)
7973 *aRegistered = mData->mRegistered;
7974
7975 return S_OK;
7976}
7977
7978/**
7979 * Decreases the number of objects dependent on the machine state.
7980 * Must always complete the #i_addStateDependency() call after the state
7981 * dependency is no more necessary.
7982 */
7983void Machine::i_releaseStateDependency()
7984{
7985 AutoCaller autoCaller(this);
7986 AssertComRCReturnVoid(autoCaller.rc());
7987
7988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7989
7990 /* releaseStateDependency() w/o addStateDependency()? */
7991 AssertReturnVoid(mData->mMachineStateDeps != 0);
7992 -- mData->mMachineStateDeps;
7993
7994 if (mData->mMachineStateDeps == 0)
7995 {
7996 /* inform i_ensureNoStateDependencies() that there are no more deps */
7997 if (mData->mMachineStateChangePending != 0)
7998 {
7999 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8000 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8001 }
8002 }
8003}
8004
8005Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8006{
8007 /* start with nothing found */
8008 Utf8Str strResult("");
8009
8010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8011
8012 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8013 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8014 // found:
8015 strResult = it->second; // source is a Utf8Str
8016
8017 return strResult;
8018}
8019
8020// protected methods
8021/////////////////////////////////////////////////////////////////////////////
8022
8023/**
8024 * Performs machine state checks based on the @a aDepType value. If a check
8025 * fails, this method will set extended error info, otherwise it will return
8026 * S_OK. It is supposed, that on failure, the caller will immediately return
8027 * the return value of this method to the upper level.
8028 *
8029 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8030 *
8031 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8032 * current state of this machine object allows to change settings of the
8033 * machine (i.e. the machine is not registered, or registered but not running
8034 * and not saved). It is useful to call this method from Machine setters
8035 * before performing any change.
8036 *
8037 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8038 * as for MutableStateDep except that if the machine is saved, S_OK is also
8039 * returned. This is useful in setters which allow changing machine
8040 * properties when it is in the saved state.
8041 *
8042 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8043 * if the current state of this machine object allows to change runtime
8044 * changeable settings of the machine (i.e. the machine is not registered, or
8045 * registered but either running or not running and not saved). It is useful
8046 * to call this method from Machine setters before performing any changes to
8047 * runtime changeable settings.
8048 *
8049 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8050 * the same as for MutableOrRunningStateDep except that if the machine is
8051 * saved, S_OK is also returned. This is useful in setters which allow
8052 * changing runtime and saved state changeable machine properties.
8053 *
8054 * @param aDepType Dependency type to check.
8055 *
8056 * @note Non Machine based classes should use #i_addStateDependency() and
8057 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8058 * template.
8059 *
8060 * @note This method must be called from under this object's read or write
8061 * lock.
8062 */
8063HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8064{
8065 switch (aDepType)
8066 {
8067 case AnyStateDep:
8068 {
8069 break;
8070 }
8071 case MutableStateDep:
8072 {
8073 if ( mData->mRegistered
8074 && ( !i_isSessionMachine()
8075 || ( mData->mMachineState != MachineState_Aborted
8076 && mData->mMachineState != MachineState_Teleported
8077 && mData->mMachineState != MachineState_PoweredOff
8078 )
8079 )
8080 )
8081 return setError(VBOX_E_INVALID_VM_STATE,
8082 tr("The machine is not mutable (state is %s)"),
8083 Global::stringifyMachineState(mData->mMachineState));
8084 break;
8085 }
8086 case MutableOrSavedStateDep:
8087 {
8088 if ( mData->mRegistered
8089 && ( !i_isSessionMachine()
8090 || ( mData->mMachineState != MachineState_Aborted
8091 && mData->mMachineState != MachineState_Teleported
8092 && mData->mMachineState != MachineState_Saved
8093 && mData->mMachineState != MachineState_PoweredOff
8094 )
8095 )
8096 )
8097 return setError(VBOX_E_INVALID_VM_STATE,
8098 tr("The machine is not mutable or saved (state is %s)"),
8099 Global::stringifyMachineState(mData->mMachineState));
8100 break;
8101 }
8102 case MutableOrRunningStateDep:
8103 {
8104 if ( mData->mRegistered
8105 && ( !i_isSessionMachine()
8106 || ( mData->mMachineState != MachineState_Aborted
8107 && mData->mMachineState != MachineState_Teleported
8108 && mData->mMachineState != MachineState_PoweredOff
8109 && !Global::IsOnline(mData->mMachineState)
8110 )
8111 )
8112 )
8113 return setError(VBOX_E_INVALID_VM_STATE,
8114 tr("The machine is not mutable or running (state is %s)"),
8115 Global::stringifyMachineState(mData->mMachineState));
8116 break;
8117 }
8118 case MutableOrSavedOrRunningStateDep:
8119 {
8120 if ( mData->mRegistered
8121 && ( !i_isSessionMachine()
8122 || ( mData->mMachineState != MachineState_Aborted
8123 && mData->mMachineState != MachineState_Teleported
8124 && mData->mMachineState != MachineState_Saved
8125 && mData->mMachineState != MachineState_PoweredOff
8126 && !Global::IsOnline(mData->mMachineState)
8127 )
8128 )
8129 )
8130 return setError(VBOX_E_INVALID_VM_STATE,
8131 tr("The machine is not mutable, saved or running (state is %s)"),
8132 Global::stringifyMachineState(mData->mMachineState));
8133 break;
8134 }
8135 }
8136
8137 return S_OK;
8138}
8139
8140/**
8141 * Helper to initialize all associated child objects and allocate data
8142 * structures.
8143 *
8144 * This method must be called as a part of the object's initialization procedure
8145 * (usually done in the #init() method).
8146 *
8147 * @note Must be called only from #init() or from #i_registeredInit().
8148 */
8149HRESULT Machine::initDataAndChildObjects()
8150{
8151 AutoCaller autoCaller(this);
8152 AssertComRCReturnRC(autoCaller.rc());
8153 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8154 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8155
8156 AssertReturn(!mData->mAccessible, E_FAIL);
8157
8158 /* allocate data structures */
8159 mSSData.allocate();
8160 mUserData.allocate();
8161 mHWData.allocate();
8162 mMediumAttachments.allocate();
8163 mStorageControllers.allocate();
8164 mUSBControllers.allocate();
8165
8166 /* initialize mOSTypeId */
8167 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8168
8169/** @todo r=bird: init() methods never fails, right? Why don't we make them
8170 * return void then! */
8171
8172 /* create associated BIOS settings object */
8173 unconst(mBIOSSettings).createObject();
8174 mBIOSSettings->init(this);
8175
8176 /* create associated record settings object */
8177 unconst(mRecordingSettings).createObject();
8178 mRecordingSettings->init(this);
8179
8180 /* create an associated VRDE object (default is disabled) */
8181 unconst(mVRDEServer).createObject();
8182 mVRDEServer->init(this);
8183
8184 /* create associated serial port objects */
8185 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8186 {
8187 unconst(mSerialPorts[slot]).createObject();
8188 mSerialPorts[slot]->init(this, slot);
8189 }
8190
8191 /* create associated parallel port objects */
8192 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8193 {
8194 unconst(mParallelPorts[slot]).createObject();
8195 mParallelPorts[slot]->init(this, slot);
8196 }
8197
8198 /* create the audio adapter object (always present, default is disabled) */
8199 unconst(mAudioAdapter).createObject();
8200 mAudioAdapter->init(this);
8201
8202 /* create the USB device filters object (always present) */
8203 unconst(mUSBDeviceFilters).createObject();
8204 mUSBDeviceFilters->init(this);
8205
8206 /* create associated network adapter objects */
8207 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8208 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8209 {
8210 unconst(mNetworkAdapters[slot]).createObject();
8211 mNetworkAdapters[slot]->init(this, slot);
8212 }
8213
8214 /* create the bandwidth control */
8215 unconst(mBandwidthControl).createObject();
8216 mBandwidthControl->init(this);
8217
8218 return S_OK;
8219}
8220
8221/**
8222 * Helper to uninitialize all associated child objects and to free all data
8223 * structures.
8224 *
8225 * This method must be called as a part of the object's uninitialization
8226 * procedure (usually done in the #uninit() method).
8227 *
8228 * @note Must be called only from #uninit() or from #i_registeredInit().
8229 */
8230void Machine::uninitDataAndChildObjects()
8231{
8232 AutoCaller autoCaller(this);
8233 AssertComRCReturnVoid(autoCaller.rc());
8234 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8235 || getObjectState().getState() == ObjectState::Limited);
8236
8237 /* tell all our other child objects we've been uninitialized */
8238 if (mBandwidthControl)
8239 {
8240 mBandwidthControl->uninit();
8241 unconst(mBandwidthControl).setNull();
8242 }
8243
8244 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8245 {
8246 if (mNetworkAdapters[slot])
8247 {
8248 mNetworkAdapters[slot]->uninit();
8249 unconst(mNetworkAdapters[slot]).setNull();
8250 }
8251 }
8252
8253 if (mUSBDeviceFilters)
8254 {
8255 mUSBDeviceFilters->uninit();
8256 unconst(mUSBDeviceFilters).setNull();
8257 }
8258
8259 if (mAudioAdapter)
8260 {
8261 mAudioAdapter->uninit();
8262 unconst(mAudioAdapter).setNull();
8263 }
8264
8265 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8266 {
8267 if (mParallelPorts[slot])
8268 {
8269 mParallelPorts[slot]->uninit();
8270 unconst(mParallelPorts[slot]).setNull();
8271 }
8272 }
8273
8274 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8275 {
8276 if (mSerialPorts[slot])
8277 {
8278 mSerialPorts[slot]->uninit();
8279 unconst(mSerialPorts[slot]).setNull();
8280 }
8281 }
8282
8283 if (mVRDEServer)
8284 {
8285 mVRDEServer->uninit();
8286 unconst(mVRDEServer).setNull();
8287 }
8288
8289 if (mBIOSSettings)
8290 {
8291 mBIOSSettings->uninit();
8292 unconst(mBIOSSettings).setNull();
8293 }
8294
8295 if (mRecordingSettings)
8296 {
8297 mRecordingSettings->uninit();
8298 unconst(mRecordingSettings).setNull();
8299 }
8300
8301 /* Deassociate media (only when a real Machine or a SnapshotMachine
8302 * instance is uninitialized; SessionMachine instances refer to real
8303 * Machine media). This is necessary for a clean re-initialization of
8304 * the VM after successfully re-checking the accessibility state. Note
8305 * that in case of normal Machine or SnapshotMachine uninitialization (as
8306 * a result of unregistering or deleting the snapshot), outdated media
8307 * attachments will already be uninitialized and deleted, so this
8308 * code will not affect them. */
8309 if ( !mMediumAttachments.isNull()
8310 && !i_isSessionMachine()
8311 )
8312 {
8313 for (MediumAttachmentList::const_iterator
8314 it = mMediumAttachments->begin();
8315 it != mMediumAttachments->end();
8316 ++it)
8317 {
8318 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8319 if (pMedium.isNull())
8320 continue;
8321 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8322 AssertComRC(rc);
8323 }
8324 }
8325
8326 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8327 {
8328 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8329 if (mData->mFirstSnapshot)
8330 {
8331 // snapshots tree is protected by machine write lock; strictly
8332 // this isn't necessary here since we're deleting the entire
8333 // machine, but otherwise we assert in Snapshot::uninit()
8334 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8335 mData->mFirstSnapshot->uninit();
8336 mData->mFirstSnapshot.setNull();
8337 }
8338
8339 mData->mCurrentSnapshot.setNull();
8340 }
8341
8342 /* free data structures (the essential mData structure is not freed here
8343 * since it may be still in use) */
8344 mMediumAttachments.free();
8345 mStorageControllers.free();
8346 mUSBControllers.free();
8347 mHWData.free();
8348 mUserData.free();
8349 mSSData.free();
8350}
8351
8352/**
8353 * Returns a pointer to the Machine object for this machine that acts like a
8354 * parent for complex machine data objects such as shared folders, etc.
8355 *
8356 * For primary Machine objects and for SnapshotMachine objects, returns this
8357 * object's pointer itself. For SessionMachine objects, returns the peer
8358 * (primary) machine pointer.
8359 */
8360Machine *Machine::i_getMachine()
8361{
8362 if (i_isSessionMachine())
8363 return (Machine*)mPeer;
8364 return this;
8365}
8366
8367/**
8368 * Makes sure that there are no machine state dependents. If necessary, waits
8369 * for the number of dependents to drop to zero.
8370 *
8371 * Make sure this method is called from under this object's write lock to
8372 * guarantee that no new dependents may be added when this method returns
8373 * control to the caller.
8374 *
8375 * @note Locks this object for writing. The lock will be released while waiting
8376 * (if necessary).
8377 *
8378 * @warning To be used only in methods that change the machine state!
8379 */
8380void Machine::i_ensureNoStateDependencies()
8381{
8382 AssertReturnVoid(isWriteLockOnCurrentThread());
8383
8384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8385
8386 /* Wait for all state dependents if necessary */
8387 if (mData->mMachineStateDeps != 0)
8388 {
8389 /* lazy semaphore creation */
8390 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8391 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8392
8393 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8394 mData->mMachineStateDeps));
8395
8396 ++mData->mMachineStateChangePending;
8397
8398 /* reset the semaphore before waiting, the last dependent will signal
8399 * it */
8400 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8401
8402 alock.release();
8403
8404 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8405
8406 alock.acquire();
8407
8408 -- mData->mMachineStateChangePending;
8409 }
8410}
8411
8412/**
8413 * Changes the machine state and informs callbacks.
8414 *
8415 * This method is not intended to fail so it either returns S_OK or asserts (and
8416 * returns a failure).
8417 *
8418 * @note Locks this object for writing.
8419 */
8420HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8421{
8422 LogFlowThisFuncEnter();
8423 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8424 Assert(aMachineState != MachineState_Null);
8425
8426 AutoCaller autoCaller(this);
8427 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8428
8429 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8430
8431 /* wait for state dependents to drop to zero */
8432 i_ensureNoStateDependencies();
8433
8434 MachineState_T const enmOldState = mData->mMachineState;
8435 if (enmOldState != aMachineState)
8436 {
8437 mData->mMachineState = aMachineState;
8438 RTTimeNow(&mData->mLastStateChange);
8439
8440#ifdef VBOX_WITH_DTRACE_R3_MAIN
8441 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8442#endif
8443 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8444 }
8445
8446 LogFlowThisFuncLeave();
8447 return S_OK;
8448}
8449
8450/**
8451 * Searches for a shared folder with the given logical name
8452 * in the collection of shared folders.
8453 *
8454 * @param aName logical name of the shared folder
8455 * @param aSharedFolder where to return the found object
8456 * @param aSetError whether to set the error info if the folder is
8457 * not found
8458 * @return
8459 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8460 *
8461 * @note
8462 * must be called from under the object's lock!
8463 */
8464HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8465 ComObjPtr<SharedFolder> &aSharedFolder,
8466 bool aSetError /* = false */)
8467{
8468 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8469 for (HWData::SharedFolderList::const_iterator
8470 it = mHWData->mSharedFolders.begin();
8471 it != mHWData->mSharedFolders.end();
8472 ++it)
8473 {
8474 SharedFolder *pSF = *it;
8475 AutoCaller autoCaller(pSF);
8476 if (pSF->i_getName() == aName)
8477 {
8478 aSharedFolder = pSF;
8479 rc = S_OK;
8480 break;
8481 }
8482 }
8483
8484 if (aSetError && FAILED(rc))
8485 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8486
8487 return rc;
8488}
8489
8490/**
8491 * Initializes all machine instance data from the given settings structures
8492 * from XML. The exception is the machine UUID which needs special handling
8493 * depending on the caller's use case, so the caller needs to set that herself.
8494 *
8495 * This gets called in several contexts during machine initialization:
8496 *
8497 * -- When machine XML exists on disk already and needs to be loaded into memory,
8498 * for example, from #i_registeredInit() to load all registered machines on
8499 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8500 * attached to the machine should be part of some media registry already.
8501 *
8502 * -- During OVF import, when a machine config has been constructed from an
8503 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8504 * ensure that the media listed as attachments in the config (which have
8505 * been imported from the OVF) receive the correct registry ID.
8506 *
8507 * -- During VM cloning.
8508 *
8509 * @param config Machine settings from XML.
8510 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8511 * for each attached medium in the config.
8512 * @return
8513 */
8514HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8515 const Guid *puuidRegistry)
8516{
8517 // copy name, description, OS type, teleporter, UTC etc.
8518 mUserData->s = config.machineUserData;
8519
8520 // look up the object by Id to check it is valid
8521 ComObjPtr<GuestOSType> pGuestOSType;
8522 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8523 if (!pGuestOSType.isNull())
8524 mUserData->s.strOsType = pGuestOSType->i_id();
8525
8526 // stateFile (optional)
8527 if (config.strStateFile.isEmpty())
8528 mSSData->strStateFilePath.setNull();
8529 else
8530 {
8531 Utf8Str stateFilePathFull(config.strStateFile);
8532 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8533 if (RT_FAILURE(vrc))
8534 return setErrorBoth(E_FAIL, vrc,
8535 tr("Invalid saved state file path '%s' (%Rrc)"),
8536 config.strStateFile.c_str(),
8537 vrc);
8538 mSSData->strStateFilePath = stateFilePathFull;
8539 }
8540
8541 // snapshot folder needs special processing so set it again
8542 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8543 if (FAILED(rc)) return rc;
8544
8545 /* Copy the extra data items (config may or may not be the same as
8546 * mData->pMachineConfigFile) if necessary. When loading the XML files
8547 * from disk they are the same, but not for OVF import. */
8548 if (mData->pMachineConfigFile != &config)
8549 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8550
8551 /* currentStateModified (optional, default is true) */
8552 mData->mCurrentStateModified = config.fCurrentStateModified;
8553
8554 mData->mLastStateChange = config.timeLastStateChange;
8555
8556 /*
8557 * note: all mUserData members must be assigned prior this point because
8558 * we need to commit changes in order to let mUserData be shared by all
8559 * snapshot machine instances.
8560 */
8561 mUserData.commitCopy();
8562
8563 // machine registry, if present (must be loaded before snapshots)
8564 if (config.canHaveOwnMediaRegistry())
8565 {
8566 // determine machine folder
8567 Utf8Str strMachineFolder = i_getSettingsFileFull();
8568 strMachineFolder.stripFilename();
8569 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8570 config.mediaRegistry,
8571 strMachineFolder);
8572 if (FAILED(rc)) return rc;
8573 }
8574
8575 /* Snapshot node (optional) */
8576 size_t cRootSnapshots;
8577 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8578 {
8579 // there must be only one root snapshot
8580 Assert(cRootSnapshots == 1);
8581
8582 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8583
8584 rc = i_loadSnapshot(snap,
8585 config.uuidCurrentSnapshot,
8586 NULL); // no parent == first snapshot
8587 if (FAILED(rc)) return rc;
8588 }
8589
8590 // hardware data
8591 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8592 if (FAILED(rc)) return rc;
8593
8594 /*
8595 * NOTE: the assignment below must be the last thing to do,
8596 * otherwise it will be not possible to change the settings
8597 * somewhere in the code above because all setters will be
8598 * blocked by i_checkStateDependency(MutableStateDep).
8599 */
8600
8601 /* set the machine state to Aborted or Saved when appropriate */
8602 if (config.fAborted)
8603 {
8604 mSSData->strStateFilePath.setNull();
8605
8606 /* no need to use i_setMachineState() during init() */
8607 mData->mMachineState = MachineState_Aborted;
8608 }
8609 else if (!mSSData->strStateFilePath.isEmpty())
8610 {
8611 /* no need to use i_setMachineState() during init() */
8612 mData->mMachineState = MachineState_Saved;
8613 }
8614
8615 // after loading settings, we are no longer different from the XML on disk
8616 mData->flModifications = 0;
8617
8618 return S_OK;
8619}
8620
8621/**
8622 * Recursively loads all snapshots starting from the given.
8623 *
8624 * @param data snapshot settings.
8625 * @param aCurSnapshotId Current snapshot ID from the settings file.
8626 * @param aParentSnapshot Parent snapshot.
8627 */
8628HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8629 const Guid &aCurSnapshotId,
8630 Snapshot *aParentSnapshot)
8631{
8632 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8633 AssertReturn(!i_isSessionMachine(), E_FAIL);
8634
8635 HRESULT rc = S_OK;
8636
8637 Utf8Str strStateFile;
8638 if (!data.strStateFile.isEmpty())
8639 {
8640 /* optional */
8641 strStateFile = data.strStateFile;
8642 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8643 if (RT_FAILURE(vrc))
8644 return setErrorBoth(E_FAIL, vrc,
8645 tr("Invalid saved state file path '%s' (%Rrc)"),
8646 strStateFile.c_str(),
8647 vrc);
8648 }
8649
8650 /* create a snapshot machine object */
8651 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8652 pSnapshotMachine.createObject();
8653 rc = pSnapshotMachine->initFromSettings(this,
8654 data.hardware,
8655 &data.debugging,
8656 &data.autostart,
8657 data.uuid.ref(),
8658 strStateFile);
8659 if (FAILED(rc)) return rc;
8660
8661 /* create a snapshot object */
8662 ComObjPtr<Snapshot> pSnapshot;
8663 pSnapshot.createObject();
8664 /* initialize the snapshot */
8665 rc = pSnapshot->init(mParent, // VirtualBox object
8666 data.uuid,
8667 data.strName,
8668 data.strDescription,
8669 data.timestamp,
8670 pSnapshotMachine,
8671 aParentSnapshot);
8672 if (FAILED(rc)) return rc;
8673
8674 /* memorize the first snapshot if necessary */
8675 if (!mData->mFirstSnapshot)
8676 mData->mFirstSnapshot = pSnapshot;
8677
8678 /* memorize the current snapshot when appropriate */
8679 if ( !mData->mCurrentSnapshot
8680 && pSnapshot->i_getId() == aCurSnapshotId
8681 )
8682 mData->mCurrentSnapshot = pSnapshot;
8683
8684 // now create the children
8685 for (settings::SnapshotsList::const_iterator
8686 it = data.llChildSnapshots.begin();
8687 it != data.llChildSnapshots.end();
8688 ++it)
8689 {
8690 const settings::Snapshot &childData = *it;
8691 // recurse
8692 rc = i_loadSnapshot(childData,
8693 aCurSnapshotId,
8694 pSnapshot); // parent = the one we created above
8695 if (FAILED(rc)) return rc;
8696 }
8697
8698 return rc;
8699}
8700
8701/**
8702 * Loads settings into mHWData.
8703 *
8704 * @param puuidRegistry Registry ID.
8705 * @param puuidSnapshot Snapshot ID
8706 * @param data Reference to the hardware settings.
8707 * @param pDbg Pointer to the debugging settings.
8708 * @param pAutostart Pointer to the autostart settings.
8709 */
8710HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8711 const Guid *puuidSnapshot,
8712 const settings::Hardware &data,
8713 const settings::Debugging *pDbg,
8714 const settings::Autostart *pAutostart)
8715{
8716 AssertReturn(!i_isSessionMachine(), E_FAIL);
8717
8718 HRESULT rc = S_OK;
8719
8720 try
8721 {
8722 ComObjPtr<GuestOSType> pGuestOSType;
8723 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8724
8725 /* The hardware version attribute (optional). */
8726 mHWData->mHWVersion = data.strVersion;
8727 mHWData->mHardwareUUID = data.uuid;
8728
8729 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8730 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8731 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8732 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8733 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8734 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8735 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8736 mHWData->mPAEEnabled = data.fPAE;
8737 mHWData->mLongMode = data.enmLongMode;
8738 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8739 mHWData->mAPIC = data.fAPIC;
8740 mHWData->mX2APIC = data.fX2APIC;
8741 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8742 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8743 mHWData->mSpecCtrl = data.fSpecCtrl;
8744 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8745 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8746 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8747 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8748 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8749 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8750 mHWData->mCPUCount = data.cCPUs;
8751 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8752 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8753 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8754 mHWData->mCpuProfile = data.strCpuProfile;
8755
8756 // cpu
8757 if (mHWData->mCPUHotPlugEnabled)
8758 {
8759 for (settings::CpuList::const_iterator
8760 it = data.llCpus.begin();
8761 it != data.llCpus.end();
8762 ++it)
8763 {
8764 const settings::Cpu &cpu = *it;
8765
8766 mHWData->mCPUAttached[cpu.ulId] = true;
8767 }
8768 }
8769
8770 // cpuid leafs
8771 for (settings::CpuIdLeafsList::const_iterator
8772 it = data.llCpuIdLeafs.begin();
8773 it != data.llCpuIdLeafs.end();
8774 ++it)
8775 {
8776 const settings::CpuIdLeaf &rLeaf= *it;
8777 if ( rLeaf.idx < UINT32_C(0x20)
8778 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8779 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8780 mHWData->mCpuIdLeafList.push_back(rLeaf);
8781 /* else: just ignore */
8782 }
8783
8784 mHWData->mMemorySize = data.ulMemorySizeMB;
8785 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8786
8787 // boot order
8788 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8789 {
8790 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8791 if (it == data.mapBootOrder.end())
8792 mHWData->mBootOrder[i] = DeviceType_Null;
8793 else
8794 mHWData->mBootOrder[i] = it->second;
8795 }
8796
8797 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8798 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8799 mHWData->mMonitorCount = data.cMonitors;
8800 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8801 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8802 mHWData->mFirmwareType = data.firmwareType;
8803 mHWData->mPointingHIDType = data.pointingHIDType;
8804 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8805 mHWData->mChipsetType = data.chipsetType;
8806 mHWData->mParavirtProvider = data.paravirtProvider;
8807 mHWData->mParavirtDebug = data.strParavirtDebug;
8808 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8809 mHWData->mHPETEnabled = data.fHPETEnabled;
8810
8811 /* VRDEServer */
8812 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8813 if (FAILED(rc)) return rc;
8814
8815 /* BIOS */
8816 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8817 if (FAILED(rc)) return rc;
8818
8819 /* Recording settings */
8820 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8821 if (FAILED(rc)) return rc;
8822
8823 // Bandwidth control (must come before network adapters)
8824 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8825 if (FAILED(rc)) return rc;
8826
8827 /* USB controllers */
8828 for (settings::USBControllerList::const_iterator
8829 it = data.usbSettings.llUSBControllers.begin();
8830 it != data.usbSettings.llUSBControllers.end();
8831 ++it)
8832 {
8833 const settings::USBController &settingsCtrl = *it;
8834 ComObjPtr<USBController> newCtrl;
8835
8836 newCtrl.createObject();
8837 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8838 mUSBControllers->push_back(newCtrl);
8839 }
8840
8841 /* USB device filters */
8842 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8843 if (FAILED(rc)) return rc;
8844
8845 // network adapters (establish array size first and apply defaults, to
8846 // ensure reading the same settings as we saved, since the list skips
8847 // adapters having defaults)
8848 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8849 size_t oldCount = mNetworkAdapters.size();
8850 if (newCount > oldCount)
8851 {
8852 mNetworkAdapters.resize(newCount);
8853 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8854 {
8855 unconst(mNetworkAdapters[slot]).createObject();
8856 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8857 }
8858 }
8859 else if (newCount < oldCount)
8860 mNetworkAdapters.resize(newCount);
8861 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8862 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8863 for (settings::NetworkAdaptersList::const_iterator
8864 it = data.llNetworkAdapters.begin();
8865 it != data.llNetworkAdapters.end();
8866 ++it)
8867 {
8868 const settings::NetworkAdapter &nic = *it;
8869
8870 /* slot uniqueness is guaranteed by XML Schema */
8871 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8872 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8873 if (FAILED(rc)) return rc;
8874 }
8875
8876 // serial ports (establish defaults first, to ensure reading the same
8877 // settings as we saved, since the list skips ports having defaults)
8878 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8879 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8880 for (settings::SerialPortsList::const_iterator
8881 it = data.llSerialPorts.begin();
8882 it != data.llSerialPorts.end();
8883 ++it)
8884 {
8885 const settings::SerialPort &s = *it;
8886
8887 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8888 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8889 if (FAILED(rc)) return rc;
8890 }
8891
8892 // parallel ports (establish defaults first, to ensure reading the same
8893 // settings as we saved, since the list skips ports having defaults)
8894 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8895 mParallelPorts[i]->i_applyDefaults();
8896 for (settings::ParallelPortsList::const_iterator
8897 it = data.llParallelPorts.begin();
8898 it != data.llParallelPorts.end();
8899 ++it)
8900 {
8901 const settings::ParallelPort &p = *it;
8902
8903 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8904 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8905 if (FAILED(rc)) return rc;
8906 }
8907
8908 /* AudioAdapter */
8909 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8910 if (FAILED(rc)) return rc;
8911
8912 /* storage controllers */
8913 rc = i_loadStorageControllers(data.storage,
8914 puuidRegistry,
8915 puuidSnapshot);
8916 if (FAILED(rc)) return rc;
8917
8918 /* Shared folders */
8919 for (settings::SharedFoldersList::const_iterator
8920 it = data.llSharedFolders.begin();
8921 it != data.llSharedFolders.end();
8922 ++it)
8923 {
8924 const settings::SharedFolder &sf = *it;
8925
8926 ComObjPtr<SharedFolder> sharedFolder;
8927 /* Check for double entries. Not allowed! */
8928 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8929 if (SUCCEEDED(rc))
8930 return setError(VBOX_E_OBJECT_IN_USE,
8931 tr("Shared folder named '%s' already exists"),
8932 sf.strName.c_str());
8933
8934 /* Create the new shared folder. Don't break on error. This will be
8935 * reported when the machine starts. */
8936 sharedFolder.createObject();
8937 rc = sharedFolder->init(i_getMachine(),
8938 sf.strName,
8939 sf.strHostPath,
8940 RT_BOOL(sf.fWritable),
8941 RT_BOOL(sf.fAutoMount),
8942 sf.strAutoMountPoint,
8943 false /* fFailOnError */);
8944 if (FAILED(rc)) return rc;
8945 mHWData->mSharedFolders.push_back(sharedFolder);
8946 }
8947
8948 // Clipboard
8949 mHWData->mClipboardMode = data.clipboardMode;
8950
8951 // drag'n'drop
8952 mHWData->mDnDMode = data.dndMode;
8953
8954 // guest settings
8955 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8956
8957 // IO settings
8958 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8959 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8960
8961 // Host PCI devices
8962 for (settings::HostPCIDeviceAttachmentList::const_iterator
8963 it = data.pciAttachments.begin();
8964 it != data.pciAttachments.end();
8965 ++it)
8966 {
8967 const settings::HostPCIDeviceAttachment &hpda = *it;
8968 ComObjPtr<PCIDeviceAttachment> pda;
8969
8970 pda.createObject();
8971 pda->i_loadSettings(this, hpda);
8972 mHWData->mPCIDeviceAssignments.push_back(pda);
8973 }
8974
8975 /*
8976 * (The following isn't really real hardware, but it lives in HWData
8977 * for reasons of convenience.)
8978 */
8979
8980#ifdef VBOX_WITH_GUEST_PROPS
8981 /* Guest properties (optional) */
8982
8983 /* Only load transient guest properties for configs which have saved
8984 * state, because there shouldn't be any for powered off VMs. The same
8985 * logic applies for snapshots, as offline snapshots shouldn't have
8986 * any such properties. They confuse the code in various places.
8987 * Note: can't rely on the machine state, as it isn't set yet. */
8988 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8989 /* apologies for the hacky unconst() usage, but this needs hacking
8990 * actually inconsistent settings into consistency, otherwise there
8991 * will be some corner cases where the inconsistency survives
8992 * surprisingly long without getting fixed, especially for snapshots
8993 * as there are no config changes. */
8994 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8995 for (settings::GuestPropertiesList::iterator
8996 it = llGuestProperties.begin();
8997 it != llGuestProperties.end();
8998 /*nothing*/)
8999 {
9000 const settings::GuestProperty &prop = *it;
9001 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9002 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9003 if ( fSkipTransientGuestProperties
9004 && ( fFlags & GUEST_PROP_F_TRANSIENT
9005 || fFlags & GUEST_PROP_F_TRANSRESET))
9006 {
9007 it = llGuestProperties.erase(it);
9008 continue;
9009 }
9010 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9011 mHWData->mGuestProperties[prop.strName] = property;
9012 ++it;
9013 }
9014#endif /* VBOX_WITH_GUEST_PROPS defined */
9015
9016 rc = i_loadDebugging(pDbg);
9017 if (FAILED(rc))
9018 return rc;
9019
9020 mHWData->mAutostart = *pAutostart;
9021
9022 /* default frontend */
9023 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9024 }
9025 catch (std::bad_alloc &)
9026 {
9027 return E_OUTOFMEMORY;
9028 }
9029
9030 AssertComRC(rc);
9031 return rc;
9032}
9033
9034/**
9035 * Called from i_loadHardware() to load the debugging settings of the
9036 * machine.
9037 *
9038 * @param pDbg Pointer to the settings.
9039 */
9040HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9041{
9042 mHWData->mDebugging = *pDbg;
9043 /* no more processing currently required, this will probably change. */
9044 return S_OK;
9045}
9046
9047/**
9048 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9049 *
9050 * @param data storage settings.
9051 * @param puuidRegistry media registry ID to set media to or NULL;
9052 * see Machine::i_loadMachineDataFromSettings()
9053 * @param puuidSnapshot snapshot ID
9054 * @return
9055 */
9056HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9057 const Guid *puuidRegistry,
9058 const Guid *puuidSnapshot)
9059{
9060 AssertReturn(!i_isSessionMachine(), E_FAIL);
9061
9062 HRESULT rc = S_OK;
9063
9064 for (settings::StorageControllersList::const_iterator
9065 it = data.llStorageControllers.begin();
9066 it != data.llStorageControllers.end();
9067 ++it)
9068 {
9069 const settings::StorageController &ctlData = *it;
9070
9071 ComObjPtr<StorageController> pCtl;
9072 /* Try to find one with the name first. */
9073 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9074 if (SUCCEEDED(rc))
9075 return setError(VBOX_E_OBJECT_IN_USE,
9076 tr("Storage controller named '%s' already exists"),
9077 ctlData.strName.c_str());
9078
9079 pCtl.createObject();
9080 rc = pCtl->init(this,
9081 ctlData.strName,
9082 ctlData.storageBus,
9083 ctlData.ulInstance,
9084 ctlData.fBootable);
9085 if (FAILED(rc)) return rc;
9086
9087 mStorageControllers->push_back(pCtl);
9088
9089 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9090 if (FAILED(rc)) return rc;
9091
9092 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9093 if (FAILED(rc)) return rc;
9094
9095 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9096 if (FAILED(rc)) return rc;
9097
9098 /* Load the attached devices now. */
9099 rc = i_loadStorageDevices(pCtl,
9100 ctlData,
9101 puuidRegistry,
9102 puuidSnapshot);
9103 if (FAILED(rc)) return rc;
9104 }
9105
9106 return S_OK;
9107}
9108
9109/**
9110 * Called from i_loadStorageControllers for a controller's devices.
9111 *
9112 * @param aStorageController
9113 * @param data
9114 * @param puuidRegistry media registry ID to set media to or NULL; see
9115 * Machine::i_loadMachineDataFromSettings()
9116 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9117 * @return
9118 */
9119HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9120 const settings::StorageController &data,
9121 const Guid *puuidRegistry,
9122 const Guid *puuidSnapshot)
9123{
9124 HRESULT rc = S_OK;
9125
9126 /* paranoia: detect duplicate attachments */
9127 for (settings::AttachedDevicesList::const_iterator
9128 it = data.llAttachedDevices.begin();
9129 it != data.llAttachedDevices.end();
9130 ++it)
9131 {
9132 const settings::AttachedDevice &ad = *it;
9133
9134 for (settings::AttachedDevicesList::const_iterator it2 = it;
9135 it2 != data.llAttachedDevices.end();
9136 ++it2)
9137 {
9138 if (it == it2)
9139 continue;
9140
9141 const settings::AttachedDevice &ad2 = *it2;
9142
9143 if ( ad.lPort == ad2.lPort
9144 && ad.lDevice == ad2.lDevice)
9145 {
9146 return setError(E_FAIL,
9147 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9148 aStorageController->i_getName().c_str(),
9149 ad.lPort,
9150 ad.lDevice,
9151 mUserData->s.strName.c_str());
9152 }
9153 }
9154 }
9155
9156 for (settings::AttachedDevicesList::const_iterator
9157 it = data.llAttachedDevices.begin();
9158 it != data.llAttachedDevices.end();
9159 ++it)
9160 {
9161 const settings::AttachedDevice &dev = *it;
9162 ComObjPtr<Medium> medium;
9163
9164 switch (dev.deviceType)
9165 {
9166 case DeviceType_Floppy:
9167 case DeviceType_DVD:
9168 if (dev.strHostDriveSrc.isNotEmpty())
9169 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9170 false /* fRefresh */, medium);
9171 else
9172 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9173 dev.uuid,
9174 false /* fRefresh */,
9175 false /* aSetError */,
9176 medium);
9177 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9178 // This is not an error. The host drive or UUID might have vanished, so just go
9179 // ahead without this removeable medium attachment
9180 rc = S_OK;
9181 break;
9182
9183 case DeviceType_HardDisk:
9184 {
9185 /* find a hard disk by UUID */
9186 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9187 if (FAILED(rc))
9188 {
9189 if (i_isSnapshotMachine())
9190 {
9191 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9192 // so the user knows that the bad disk is in a snapshot somewhere
9193 com::ErrorInfo info;
9194 return setError(E_FAIL,
9195 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9196 puuidSnapshot->raw(),
9197 info.getText().raw());
9198 }
9199 else
9200 return rc;
9201 }
9202
9203 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9204
9205 if (medium->i_getType() == MediumType_Immutable)
9206 {
9207 if (i_isSnapshotMachine())
9208 return setError(E_FAIL,
9209 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9210 "of the virtual machine '%s' ('%s')"),
9211 medium->i_getLocationFull().c_str(),
9212 dev.uuid.raw(),
9213 puuidSnapshot->raw(),
9214 mUserData->s.strName.c_str(),
9215 mData->m_strConfigFileFull.c_str());
9216
9217 return setError(E_FAIL,
9218 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9219 medium->i_getLocationFull().c_str(),
9220 dev.uuid.raw(),
9221 mUserData->s.strName.c_str(),
9222 mData->m_strConfigFileFull.c_str());
9223 }
9224
9225 if (medium->i_getType() == MediumType_MultiAttach)
9226 {
9227 if (i_isSnapshotMachine())
9228 return setError(E_FAIL,
9229 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9230 "of the virtual machine '%s' ('%s')"),
9231 medium->i_getLocationFull().c_str(),
9232 dev.uuid.raw(),
9233 puuidSnapshot->raw(),
9234 mUserData->s.strName.c_str(),
9235 mData->m_strConfigFileFull.c_str());
9236
9237 return setError(E_FAIL,
9238 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9239 medium->i_getLocationFull().c_str(),
9240 dev.uuid.raw(),
9241 mUserData->s.strName.c_str(),
9242 mData->m_strConfigFileFull.c_str());
9243 }
9244
9245 if ( !i_isSnapshotMachine()
9246 && medium->i_getChildren().size() != 0
9247 )
9248 return setError(E_FAIL,
9249 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9250 "because it has %d differencing child hard disks"),
9251 medium->i_getLocationFull().c_str(),
9252 dev.uuid.raw(),
9253 mUserData->s.strName.c_str(),
9254 mData->m_strConfigFileFull.c_str(),
9255 medium->i_getChildren().size());
9256
9257 if (i_findAttachment(*mMediumAttachments.data(),
9258 medium))
9259 return setError(E_FAIL,
9260 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9261 medium->i_getLocationFull().c_str(),
9262 dev.uuid.raw(),
9263 mUserData->s.strName.c_str(),
9264 mData->m_strConfigFileFull.c_str());
9265
9266 break;
9267 }
9268
9269 default:
9270 return setError(E_FAIL,
9271 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9272 medium->i_getLocationFull().c_str(),
9273 mUserData->s.strName.c_str(),
9274 mData->m_strConfigFileFull.c_str());
9275 }
9276
9277 if (FAILED(rc))
9278 break;
9279
9280 /* Bandwidth groups are loaded at this point. */
9281 ComObjPtr<BandwidthGroup> pBwGroup;
9282
9283 if (!dev.strBwGroup.isEmpty())
9284 {
9285 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9286 if (FAILED(rc))
9287 return setError(E_FAIL,
9288 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9289 medium->i_getLocationFull().c_str(),
9290 dev.strBwGroup.c_str(),
9291 mUserData->s.strName.c_str(),
9292 mData->m_strConfigFileFull.c_str());
9293 pBwGroup->i_reference();
9294 }
9295
9296 const Utf8Str controllerName = aStorageController->i_getName();
9297 ComObjPtr<MediumAttachment> pAttachment;
9298 pAttachment.createObject();
9299 rc = pAttachment->init(this,
9300 medium,
9301 controllerName,
9302 dev.lPort,
9303 dev.lDevice,
9304 dev.deviceType,
9305 false,
9306 dev.fPassThrough,
9307 dev.fTempEject,
9308 dev.fNonRotational,
9309 dev.fDiscard,
9310 dev.fHotPluggable,
9311 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9312 if (FAILED(rc)) break;
9313
9314 /* associate the medium with this machine and snapshot */
9315 if (!medium.isNull())
9316 {
9317 AutoCaller medCaller(medium);
9318 if (FAILED(medCaller.rc())) return medCaller.rc();
9319 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9320
9321 if (i_isSnapshotMachine())
9322 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9323 else
9324 rc = medium->i_addBackReference(mData->mUuid);
9325 /* If the medium->addBackReference fails it sets an appropriate
9326 * error message, so no need to do any guesswork here. */
9327
9328 if (puuidRegistry)
9329 // caller wants registry ID to be set on all attached media (OVF import case)
9330 medium->i_addRegistry(*puuidRegistry);
9331 }
9332
9333 if (FAILED(rc))
9334 break;
9335
9336 /* back up mMediumAttachments to let registeredInit() properly rollback
9337 * on failure (= limited accessibility) */
9338 i_setModified(IsModified_Storage);
9339 mMediumAttachments.backup();
9340 mMediumAttachments->push_back(pAttachment);
9341 }
9342
9343 return rc;
9344}
9345
9346/**
9347 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9348 *
9349 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9350 * @param aSnapshot where to return the found snapshot
9351 * @param aSetError true to set extended error info on failure
9352 */
9353HRESULT Machine::i_findSnapshotById(const Guid &aId,
9354 ComObjPtr<Snapshot> &aSnapshot,
9355 bool aSetError /* = false */)
9356{
9357 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9358
9359 if (!mData->mFirstSnapshot)
9360 {
9361 if (aSetError)
9362 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9363 return E_FAIL;
9364 }
9365
9366 if (aId.isZero())
9367 aSnapshot = mData->mFirstSnapshot;
9368 else
9369 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9370
9371 if (!aSnapshot)
9372 {
9373 if (aSetError)
9374 return setError(E_FAIL,
9375 tr("Could not find a snapshot with UUID {%s}"),
9376 aId.toString().c_str());
9377 return E_FAIL;
9378 }
9379
9380 return S_OK;
9381}
9382
9383/**
9384 * Returns the snapshot with the given name or fails of no such snapshot.
9385 *
9386 * @param strName snapshot name to find
9387 * @param aSnapshot where to return the found snapshot
9388 * @param aSetError true to set extended error info on failure
9389 */
9390HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9391 ComObjPtr<Snapshot> &aSnapshot,
9392 bool aSetError /* = false */)
9393{
9394 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9395
9396 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9397
9398 if (!mData->mFirstSnapshot)
9399 {
9400 if (aSetError)
9401 return setError(VBOX_E_OBJECT_NOT_FOUND,
9402 tr("This machine does not have any snapshots"));
9403 return VBOX_E_OBJECT_NOT_FOUND;
9404 }
9405
9406 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9407
9408 if (!aSnapshot)
9409 {
9410 if (aSetError)
9411 return setError(VBOX_E_OBJECT_NOT_FOUND,
9412 tr("Could not find a snapshot named '%s'"), strName.c_str());
9413 return VBOX_E_OBJECT_NOT_FOUND;
9414 }
9415
9416 return S_OK;
9417}
9418
9419/**
9420 * Returns a storage controller object with the given name.
9421 *
9422 * @param aName storage controller name to find
9423 * @param aStorageController where to return the found storage controller
9424 * @param aSetError true to set extended error info on failure
9425 */
9426HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9427 ComObjPtr<StorageController> &aStorageController,
9428 bool aSetError /* = false */)
9429{
9430 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9431
9432 for (StorageControllerList::const_iterator
9433 it = mStorageControllers->begin();
9434 it != mStorageControllers->end();
9435 ++it)
9436 {
9437 if ((*it)->i_getName() == aName)
9438 {
9439 aStorageController = (*it);
9440 return S_OK;
9441 }
9442 }
9443
9444 if (aSetError)
9445 return setError(VBOX_E_OBJECT_NOT_FOUND,
9446 tr("Could not find a storage controller named '%s'"),
9447 aName.c_str());
9448 return VBOX_E_OBJECT_NOT_FOUND;
9449}
9450
9451/**
9452 * Returns a USB controller object with the given name.
9453 *
9454 * @param aName USB controller name to find
9455 * @param aUSBController where to return the found USB controller
9456 * @param aSetError true to set extended error info on failure
9457 */
9458HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9459 ComObjPtr<USBController> &aUSBController,
9460 bool aSetError /* = false */)
9461{
9462 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9463
9464 for (USBControllerList::const_iterator
9465 it = mUSBControllers->begin();
9466 it != mUSBControllers->end();
9467 ++it)
9468 {
9469 if ((*it)->i_getName() == aName)
9470 {
9471 aUSBController = (*it);
9472 return S_OK;
9473 }
9474 }
9475
9476 if (aSetError)
9477 return setError(VBOX_E_OBJECT_NOT_FOUND,
9478 tr("Could not find a storage controller named '%s'"),
9479 aName.c_str());
9480 return VBOX_E_OBJECT_NOT_FOUND;
9481}
9482
9483/**
9484 * Returns the number of USB controller instance of the given type.
9485 *
9486 * @param enmType USB controller type.
9487 */
9488ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9489{
9490 ULONG cCtrls = 0;
9491
9492 for (USBControllerList::const_iterator
9493 it = mUSBControllers->begin();
9494 it != mUSBControllers->end();
9495 ++it)
9496 {
9497 if ((*it)->i_getControllerType() == enmType)
9498 cCtrls++;
9499 }
9500
9501 return cCtrls;
9502}
9503
9504HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9505 MediumAttachmentList &atts)
9506{
9507 AutoCaller autoCaller(this);
9508 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9509
9510 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9511
9512 for (MediumAttachmentList::const_iterator
9513 it = mMediumAttachments->begin();
9514 it != mMediumAttachments->end();
9515 ++it)
9516 {
9517 const ComObjPtr<MediumAttachment> &pAtt = *it;
9518 // should never happen, but deal with NULL pointers in the list.
9519 AssertContinue(!pAtt.isNull());
9520
9521 // getControllerName() needs caller+read lock
9522 AutoCaller autoAttCaller(pAtt);
9523 if (FAILED(autoAttCaller.rc()))
9524 {
9525 atts.clear();
9526 return autoAttCaller.rc();
9527 }
9528 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9529
9530 if (pAtt->i_getControllerName() == aName)
9531 atts.push_back(pAtt);
9532 }
9533
9534 return S_OK;
9535}
9536
9537
9538/**
9539 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9540 * file if the machine name was changed and about creating a new settings file
9541 * if this is a new machine.
9542 *
9543 * @note Must be never called directly but only from #saveSettings().
9544 */
9545HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9546{
9547 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9548
9549 HRESULT rc = S_OK;
9550
9551 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9552
9553 /// @todo need to handle primary group change, too
9554
9555 /* attempt to rename the settings file if machine name is changed */
9556 if ( mUserData->s.fNameSync
9557 && mUserData.isBackedUp()
9558 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9559 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9560 )
9561 {
9562 bool dirRenamed = false;
9563 bool fileRenamed = false;
9564
9565 Utf8Str configFile, newConfigFile;
9566 Utf8Str configFilePrev, newConfigFilePrev;
9567 Utf8Str configDir, newConfigDir;
9568
9569 do
9570 {
9571 int vrc = VINF_SUCCESS;
9572
9573 Utf8Str name = mUserData.backedUpData()->s.strName;
9574 Utf8Str newName = mUserData->s.strName;
9575 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9576 if (group == "/")
9577 group.setNull();
9578 Utf8Str newGroup = mUserData->s.llGroups.front();
9579 if (newGroup == "/")
9580 newGroup.setNull();
9581
9582 configFile = mData->m_strConfigFileFull;
9583
9584 /* first, rename the directory if it matches the group and machine name */
9585 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9586 group.c_str(), RTPATH_DELIMITER, name.c_str());
9587 /** @todo hack, make somehow use of ComposeMachineFilename */
9588 if (mUserData->s.fDirectoryIncludesUUID)
9589 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9590 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9591 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9592 /** @todo hack, make somehow use of ComposeMachineFilename */
9593 if (mUserData->s.fDirectoryIncludesUUID)
9594 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9595 configDir = configFile;
9596 configDir.stripFilename();
9597 newConfigDir = configDir;
9598 if ( configDir.length() >= groupPlusName.length()
9599 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9600 groupPlusName.c_str()))
9601 {
9602 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9603 Utf8Str newConfigBaseDir(newConfigDir);
9604 newConfigDir.append(newGroupPlusName);
9605 /* consistency: use \ if appropriate on the platform */
9606 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9607 /* new dir and old dir cannot be equal here because of 'if'
9608 * above and because name != newName */
9609 Assert(configDir != newConfigDir);
9610 if (!fSettingsFileIsNew)
9611 {
9612 /* perform real rename only if the machine is not new */
9613 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9614 if ( vrc == VERR_FILE_NOT_FOUND
9615 || vrc == VERR_PATH_NOT_FOUND)
9616 {
9617 /* create the parent directory, then retry renaming */
9618 Utf8Str parent(newConfigDir);
9619 parent.stripFilename();
9620 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9621 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9622 }
9623 if (RT_FAILURE(vrc))
9624 {
9625 rc = setErrorBoth(E_FAIL, vrc,
9626 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9627 configDir.c_str(),
9628 newConfigDir.c_str(),
9629 vrc);
9630 break;
9631 }
9632 /* delete subdirectories which are no longer needed */
9633 Utf8Str dir(configDir);
9634 dir.stripFilename();
9635 while (dir != newConfigBaseDir && dir != ".")
9636 {
9637 vrc = RTDirRemove(dir.c_str());
9638 if (RT_FAILURE(vrc))
9639 break;
9640 dir.stripFilename();
9641 }
9642 dirRenamed = true;
9643 }
9644 }
9645
9646 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9647 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9648
9649 /* then try to rename the settings file itself */
9650 if (newConfigFile != configFile)
9651 {
9652 /* get the path to old settings file in renamed directory */
9653 configFile = Utf8StrFmt("%s%c%s",
9654 newConfigDir.c_str(),
9655 RTPATH_DELIMITER,
9656 RTPathFilename(configFile.c_str()));
9657 if (!fSettingsFileIsNew)
9658 {
9659 /* perform real rename only if the machine is not new */
9660 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9661 if (RT_FAILURE(vrc))
9662 {
9663 rc = setErrorBoth(E_FAIL, vrc,
9664 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9665 configFile.c_str(),
9666 newConfigFile.c_str(),
9667 vrc);
9668 break;
9669 }
9670 fileRenamed = true;
9671 configFilePrev = configFile;
9672 configFilePrev += "-prev";
9673 newConfigFilePrev = newConfigFile;
9674 newConfigFilePrev += "-prev";
9675 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9676 }
9677 }
9678
9679 // update m_strConfigFileFull amd mConfigFile
9680 mData->m_strConfigFileFull = newConfigFile;
9681 // compute the relative path too
9682 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9683
9684 // store the old and new so that VirtualBox::i_saveSettings() can update
9685 // the media registry
9686 if ( mData->mRegistered
9687 && (configDir != newConfigDir || configFile != newConfigFile))
9688 {
9689 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9690
9691 if (pfNeedsGlobalSaveSettings)
9692 *pfNeedsGlobalSaveSettings = true;
9693 }
9694
9695 // in the saved state file path, replace the old directory with the new directory
9696 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9697 {
9698 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9699 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9700 }
9701
9702 // and do the same thing for the saved state file paths of all the online snapshots
9703 if (mData->mFirstSnapshot)
9704 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9705 newConfigDir.c_str());
9706 }
9707 while (0);
9708
9709 if (FAILED(rc))
9710 {
9711 /* silently try to rename everything back */
9712 if (fileRenamed)
9713 {
9714 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9715 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9716 }
9717 if (dirRenamed)
9718 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9719 }
9720
9721 if (FAILED(rc)) return rc;
9722 }
9723
9724 if (fSettingsFileIsNew)
9725 {
9726 /* create a virgin config file */
9727 int vrc = VINF_SUCCESS;
9728
9729 /* ensure the settings directory exists */
9730 Utf8Str path(mData->m_strConfigFileFull);
9731 path.stripFilename();
9732 if (!RTDirExists(path.c_str()))
9733 {
9734 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9735 if (RT_FAILURE(vrc))
9736 {
9737 return setErrorBoth(E_FAIL, vrc,
9738 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9739 path.c_str(),
9740 vrc);
9741 }
9742 }
9743
9744 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9745 path = Utf8Str(mData->m_strConfigFileFull);
9746 RTFILE f = NIL_RTFILE;
9747 vrc = RTFileOpen(&f, path.c_str(),
9748 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9749 if (RT_FAILURE(vrc))
9750 return setErrorBoth(E_FAIL, vrc,
9751 tr("Could not create the settings file '%s' (%Rrc)"),
9752 path.c_str(),
9753 vrc);
9754 RTFileClose(f);
9755 }
9756
9757 return rc;
9758}
9759
9760/**
9761 * Saves and commits machine data, user data and hardware data.
9762 *
9763 * Note that on failure, the data remains uncommitted.
9764 *
9765 * @a aFlags may combine the following flags:
9766 *
9767 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9768 * Used when saving settings after an operation that makes them 100%
9769 * correspond to the settings from the current snapshot.
9770 * - SaveS_Force: settings will be saved without doing a deep compare of the
9771 * settings structures. This is used when this is called because snapshots
9772 * have changed to avoid the overhead of the deep compare.
9773 *
9774 * @note Must be called from under this object's write lock. Locks children for
9775 * writing.
9776 *
9777 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9778 * initialized to false and that will be set to true by this function if
9779 * the caller must invoke VirtualBox::i_saveSettings() because the global
9780 * settings have changed. This will happen if a machine rename has been
9781 * saved and the global machine and media registries will therefore need
9782 * updating.
9783 * @param aFlags Flags.
9784 */
9785HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9786 int aFlags /*= 0*/)
9787{
9788 LogFlowThisFuncEnter();
9789
9790 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9791
9792 /* make sure child objects are unable to modify the settings while we are
9793 * saving them */
9794 i_ensureNoStateDependencies();
9795
9796 AssertReturn(!i_isSnapshotMachine(),
9797 E_FAIL);
9798
9799 if (!mData->mAccessible)
9800 return setError(VBOX_E_INVALID_VM_STATE,
9801 tr("The machine is not accessible, so cannot save settings"));
9802
9803 HRESULT rc = S_OK;
9804 bool fNeedsWrite = false;
9805
9806 /* First, prepare to save settings. It will care about renaming the
9807 * settings directory and file if the machine name was changed and about
9808 * creating a new settings file if this is a new machine. */
9809 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9810 if (FAILED(rc)) return rc;
9811
9812 // keep a pointer to the current settings structures
9813 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9814 settings::MachineConfigFile *pNewConfig = NULL;
9815
9816 try
9817 {
9818 // make a fresh one to have everyone write stuff into
9819 pNewConfig = new settings::MachineConfigFile(NULL);
9820 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9821
9822 // now go and copy all the settings data from COM to the settings structures
9823 // (this calls i_saveSettings() on all the COM objects in the machine)
9824 i_copyMachineDataToSettings(*pNewConfig);
9825
9826 if (aFlags & SaveS_ResetCurStateModified)
9827 {
9828 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9829 mData->mCurrentStateModified = FALSE;
9830 fNeedsWrite = true; // always, no need to compare
9831 }
9832 else if (aFlags & SaveS_Force)
9833 {
9834 fNeedsWrite = true; // always, no need to compare
9835 }
9836 else
9837 {
9838 if (!mData->mCurrentStateModified)
9839 {
9840 // do a deep compare of the settings that we just saved with the settings
9841 // previously stored in the config file; this invokes MachineConfigFile::operator==
9842 // which does a deep compare of all the settings, which is expensive but less expensive
9843 // than writing out XML in vain
9844 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9845
9846 // could still be modified if any settings changed
9847 mData->mCurrentStateModified = fAnySettingsChanged;
9848
9849 fNeedsWrite = fAnySettingsChanged;
9850 }
9851 else
9852 fNeedsWrite = true;
9853 }
9854
9855 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9856
9857 if (fNeedsWrite)
9858 // now spit it all out!
9859 pNewConfig->write(mData->m_strConfigFileFull);
9860
9861 mData->pMachineConfigFile = pNewConfig;
9862 delete pOldConfig;
9863 i_commit();
9864
9865 // after saving settings, we are no longer different from the XML on disk
9866 mData->flModifications = 0;
9867 }
9868 catch (HRESULT err)
9869 {
9870 // we assume that error info is set by the thrower
9871 rc = err;
9872
9873 // restore old config
9874 delete pNewConfig;
9875 mData->pMachineConfigFile = pOldConfig;
9876 }
9877 catch (...)
9878 {
9879 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9880 }
9881
9882 if (fNeedsWrite)
9883 {
9884 /* Fire the data change event, even on failure (since we've already
9885 * committed all data). This is done only for SessionMachines because
9886 * mutable Machine instances are always not registered (i.e. private
9887 * to the client process that creates them) and thus don't need to
9888 * inform callbacks. */
9889 if (i_isSessionMachine())
9890 mParent->i_onMachineDataChange(mData->mUuid);
9891 }
9892
9893 LogFlowThisFunc(("rc=%08X\n", rc));
9894 LogFlowThisFuncLeave();
9895 return rc;
9896}
9897
9898/**
9899 * Implementation for saving the machine settings into the given
9900 * settings::MachineConfigFile instance. This copies machine extradata
9901 * from the previous machine config file in the instance data, if any.
9902 *
9903 * This gets called from two locations:
9904 *
9905 * -- Machine::i_saveSettings(), during the regular XML writing;
9906 *
9907 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9908 * exported to OVF and we write the VirtualBox proprietary XML
9909 * into a <vbox:Machine> tag.
9910 *
9911 * This routine fills all the fields in there, including snapshots, *except*
9912 * for the following:
9913 *
9914 * -- fCurrentStateModified. There is some special logic associated with that.
9915 *
9916 * The caller can then call MachineConfigFile::write() or do something else
9917 * with it.
9918 *
9919 * Caller must hold the machine lock!
9920 *
9921 * This throws XML errors and HRESULT, so the caller must have a catch block!
9922 */
9923void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9924{
9925 // deep copy extradata, being extra careful with self assignment (the STL
9926 // map assignment on Mac OS X clang based Xcode isn't checking)
9927 if (&config != mData->pMachineConfigFile)
9928 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9929
9930 config.uuid = mData->mUuid;
9931
9932 // copy name, description, OS type, teleport, UTC etc.
9933 config.machineUserData = mUserData->s;
9934
9935 if ( mData->mMachineState == MachineState_Saved
9936 || mData->mMachineState == MachineState_Restoring
9937 // when doing certain snapshot operations we may or may not have
9938 // a saved state in the current state, so keep everything as is
9939 || ( ( mData->mMachineState == MachineState_Snapshotting
9940 || mData->mMachineState == MachineState_DeletingSnapshot
9941 || mData->mMachineState == MachineState_RestoringSnapshot)
9942 && (!mSSData->strStateFilePath.isEmpty())
9943 )
9944 )
9945 {
9946 Assert(!mSSData->strStateFilePath.isEmpty());
9947 /* try to make the file name relative to the settings file dir */
9948 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9949 }
9950 else
9951 {
9952 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9953 config.strStateFile.setNull();
9954 }
9955
9956 if (mData->mCurrentSnapshot)
9957 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9958 else
9959 config.uuidCurrentSnapshot.clear();
9960
9961 config.timeLastStateChange = mData->mLastStateChange;
9962 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9963 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9964
9965 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9966 if (FAILED(rc)) throw rc;
9967
9968 // save machine's media registry if this is VirtualBox 4.0 or later
9969 if (config.canHaveOwnMediaRegistry())
9970 {
9971 // determine machine folder
9972 Utf8Str strMachineFolder = i_getSettingsFileFull();
9973 strMachineFolder.stripFilename();
9974 mParent->i_saveMediaRegistry(config.mediaRegistry,
9975 i_getId(), // only media with registry ID == machine UUID
9976 strMachineFolder);
9977 // this throws HRESULT
9978 }
9979
9980 // save snapshots
9981 rc = i_saveAllSnapshots(config);
9982 if (FAILED(rc)) throw rc;
9983}
9984
9985/**
9986 * Saves all snapshots of the machine into the given machine config file. Called
9987 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9988 * @param config
9989 * @return
9990 */
9991HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9992{
9993 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9994
9995 HRESULT rc = S_OK;
9996
9997 try
9998 {
9999 config.llFirstSnapshot.clear();
10000
10001 if (mData->mFirstSnapshot)
10002 {
10003 // the settings use a list for "the first snapshot"
10004 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10005
10006 // get reference to the snapshot on the list and work on that
10007 // element straight in the list to avoid excessive copying later
10008 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10009 if (FAILED(rc)) throw rc;
10010 }
10011
10012// if (mType == IsSessionMachine)
10013// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10014
10015 }
10016 catch (HRESULT err)
10017 {
10018 /* we assume that error info is set by the thrower */
10019 rc = err;
10020 }
10021 catch (...)
10022 {
10023 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10024 }
10025
10026 return rc;
10027}
10028
10029/**
10030 * Saves the VM hardware configuration. It is assumed that the
10031 * given node is empty.
10032 *
10033 * @param data Reference to the settings object for the hardware config.
10034 * @param pDbg Pointer to the settings object for the debugging config
10035 * which happens to live in mHWData.
10036 * @param pAutostart Pointer to the settings object for the autostart config
10037 * which happens to live in mHWData.
10038 */
10039HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10040 settings::Autostart *pAutostart)
10041{
10042 HRESULT rc = S_OK;
10043
10044 try
10045 {
10046 /* The hardware version attribute (optional).
10047 Automatically upgrade from 1 to current default hardware version
10048 when there is no saved state. (ugly!) */
10049 if ( mHWData->mHWVersion == "1"
10050 && mSSData->strStateFilePath.isEmpty()
10051 )
10052 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10053
10054 data.strVersion = mHWData->mHWVersion;
10055 data.uuid = mHWData->mHardwareUUID;
10056
10057 // CPU
10058 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10059 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10060 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10061 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10062 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10063 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10064 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10065 data.fPAE = !!mHWData->mPAEEnabled;
10066 data.enmLongMode = mHWData->mLongMode;
10067 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10068 data.fAPIC = !!mHWData->mAPIC;
10069 data.fX2APIC = !!mHWData->mX2APIC;
10070 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10071 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10072 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10073 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10074 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10075 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10076 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10077 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10078 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10079 data.cCPUs = mHWData->mCPUCount;
10080 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10081 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10082 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10083 data.strCpuProfile = mHWData->mCpuProfile;
10084
10085 data.llCpus.clear();
10086 if (data.fCpuHotPlug)
10087 {
10088 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10089 {
10090 if (mHWData->mCPUAttached[idx])
10091 {
10092 settings::Cpu cpu;
10093 cpu.ulId = idx;
10094 data.llCpus.push_back(cpu);
10095 }
10096 }
10097 }
10098
10099 /* Standard and Extended CPUID leafs. */
10100 data.llCpuIdLeafs.clear();
10101 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10102
10103 // memory
10104 data.ulMemorySizeMB = mHWData->mMemorySize;
10105 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10106
10107 // firmware
10108 data.firmwareType = mHWData->mFirmwareType;
10109
10110 // HID
10111 data.pointingHIDType = mHWData->mPointingHIDType;
10112 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10113
10114 // chipset
10115 data.chipsetType = mHWData->mChipsetType;
10116
10117 // paravirt
10118 data.paravirtProvider = mHWData->mParavirtProvider;
10119 data.strParavirtDebug = mHWData->mParavirtDebug;
10120
10121 // emulated USB card reader
10122 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10123
10124 // HPET
10125 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10126
10127 // boot order
10128 data.mapBootOrder.clear();
10129 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10130 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10131
10132 // display
10133 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10134 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10135 data.cMonitors = mHWData->mMonitorCount;
10136 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10137 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10138
10139 /* VRDEServer settings (optional) */
10140 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10141 if (FAILED(rc)) throw rc;
10142
10143 /* BIOS settings (required) */
10144 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10145 if (FAILED(rc)) throw rc;
10146
10147 /* Recording settings (required) */
10148 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10149 if (FAILED(rc)) throw rc;
10150
10151 /* USB Controller (required) */
10152 data.usbSettings.llUSBControllers.clear();
10153 for (USBControllerList::const_iterator
10154 it = mUSBControllers->begin();
10155 it != mUSBControllers->end();
10156 ++it)
10157 {
10158 ComObjPtr<USBController> ctrl = *it;
10159 settings::USBController settingsCtrl;
10160
10161 settingsCtrl.strName = ctrl->i_getName();
10162 settingsCtrl.enmType = ctrl->i_getControllerType();
10163
10164 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10165 }
10166
10167 /* USB device filters (required) */
10168 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10169 if (FAILED(rc)) throw rc;
10170
10171 /* Network adapters (required) */
10172 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10173 data.llNetworkAdapters.clear();
10174 /* Write out only the nominal number of network adapters for this
10175 * chipset type. Since Machine::commit() hasn't been called there
10176 * may be extra NIC settings in the vector. */
10177 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10178 {
10179 settings::NetworkAdapter nic;
10180 nic.ulSlot = (uint32_t)slot;
10181 /* paranoia check... must not be NULL, but must not crash either. */
10182 if (mNetworkAdapters[slot])
10183 {
10184 if (mNetworkAdapters[slot]->i_hasDefaults())
10185 continue;
10186
10187 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10188 if (FAILED(rc)) throw rc;
10189
10190 data.llNetworkAdapters.push_back(nic);
10191 }
10192 }
10193
10194 /* Serial ports */
10195 data.llSerialPorts.clear();
10196 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10197 {
10198 if (mSerialPorts[slot]->i_hasDefaults())
10199 continue;
10200
10201 settings::SerialPort s;
10202 s.ulSlot = slot;
10203 rc = mSerialPorts[slot]->i_saveSettings(s);
10204 if (FAILED(rc)) return rc;
10205
10206 data.llSerialPorts.push_back(s);
10207 }
10208
10209 /* Parallel ports */
10210 data.llParallelPorts.clear();
10211 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10212 {
10213 if (mParallelPorts[slot]->i_hasDefaults())
10214 continue;
10215
10216 settings::ParallelPort p;
10217 p.ulSlot = slot;
10218 rc = mParallelPorts[slot]->i_saveSettings(p);
10219 if (FAILED(rc)) return rc;
10220
10221 data.llParallelPorts.push_back(p);
10222 }
10223
10224 /* Audio adapter */
10225 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10226 if (FAILED(rc)) return rc;
10227
10228 rc = i_saveStorageControllers(data.storage);
10229 if (FAILED(rc)) return rc;
10230
10231 /* Shared folders */
10232 data.llSharedFolders.clear();
10233 for (HWData::SharedFolderList::const_iterator
10234 it = mHWData->mSharedFolders.begin();
10235 it != mHWData->mSharedFolders.end();
10236 ++it)
10237 {
10238 SharedFolder *pSF = *it;
10239 AutoCaller sfCaller(pSF);
10240 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10241 settings::SharedFolder sf;
10242 sf.strName = pSF->i_getName();
10243 sf.strHostPath = pSF->i_getHostPath();
10244 sf.fWritable = !!pSF->i_isWritable();
10245 sf.fAutoMount = !!pSF->i_isAutoMounted();
10246 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10247
10248 data.llSharedFolders.push_back(sf);
10249 }
10250
10251 // clipboard
10252 data.clipboardMode = mHWData->mClipboardMode;
10253
10254 // drag'n'drop
10255 data.dndMode = mHWData->mDnDMode;
10256
10257 /* Guest */
10258 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10259
10260 // IO settings
10261 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10262 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10263
10264 /* BandwidthControl (required) */
10265 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10266 if (FAILED(rc)) throw rc;
10267
10268 /* Host PCI devices */
10269 data.pciAttachments.clear();
10270 for (HWData::PCIDeviceAssignmentList::const_iterator
10271 it = mHWData->mPCIDeviceAssignments.begin();
10272 it != mHWData->mPCIDeviceAssignments.end();
10273 ++it)
10274 {
10275 ComObjPtr<PCIDeviceAttachment> pda = *it;
10276 settings::HostPCIDeviceAttachment hpda;
10277
10278 rc = pda->i_saveSettings(hpda);
10279 if (FAILED(rc)) throw rc;
10280
10281 data.pciAttachments.push_back(hpda);
10282 }
10283
10284 // guest properties
10285 data.llGuestProperties.clear();
10286#ifdef VBOX_WITH_GUEST_PROPS
10287 for (HWData::GuestPropertyMap::const_iterator
10288 it = mHWData->mGuestProperties.begin();
10289 it != mHWData->mGuestProperties.end();
10290 ++it)
10291 {
10292 HWData::GuestProperty property = it->second;
10293
10294 /* Remove transient guest properties at shutdown unless we
10295 * are saving state. Note that restoring snapshot intentionally
10296 * keeps them, they will be removed if appropriate once the final
10297 * machine state is set (as crashes etc. need to work). */
10298 if ( ( mData->mMachineState == MachineState_PoweredOff
10299 || mData->mMachineState == MachineState_Aborted
10300 || mData->mMachineState == MachineState_Teleported)
10301 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10302 continue;
10303 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10304 prop.strName = it->first;
10305 prop.strValue = property.strValue;
10306 prop.timestamp = property.mTimestamp;
10307 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10308 GuestPropWriteFlags(property.mFlags, szFlags);
10309 prop.strFlags = szFlags;
10310
10311 data.llGuestProperties.push_back(prop);
10312 }
10313
10314 /* I presume this doesn't require a backup(). */
10315 mData->mGuestPropertiesModified = FALSE;
10316#endif /* VBOX_WITH_GUEST_PROPS defined */
10317
10318 *pDbg = mHWData->mDebugging;
10319 *pAutostart = mHWData->mAutostart;
10320
10321 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10322 }
10323 catch (std::bad_alloc &)
10324 {
10325 return E_OUTOFMEMORY;
10326 }
10327
10328 AssertComRC(rc);
10329 return rc;
10330}
10331
10332/**
10333 * Saves the storage controller configuration.
10334 *
10335 * @param data storage settings.
10336 */
10337HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10338{
10339 data.llStorageControllers.clear();
10340
10341 for (StorageControllerList::const_iterator
10342 it = mStorageControllers->begin();
10343 it != mStorageControllers->end();
10344 ++it)
10345 {
10346 HRESULT rc;
10347 ComObjPtr<StorageController> pCtl = *it;
10348
10349 settings::StorageController ctl;
10350 ctl.strName = pCtl->i_getName();
10351 ctl.controllerType = pCtl->i_getControllerType();
10352 ctl.storageBus = pCtl->i_getStorageBus();
10353 ctl.ulInstance = pCtl->i_getInstance();
10354 ctl.fBootable = pCtl->i_getBootable();
10355
10356 /* Save the port count. */
10357 ULONG portCount;
10358 rc = pCtl->COMGETTER(PortCount)(&portCount);
10359 ComAssertComRCRet(rc, rc);
10360 ctl.ulPortCount = portCount;
10361
10362 /* Save fUseHostIOCache */
10363 BOOL fUseHostIOCache;
10364 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10365 ComAssertComRCRet(rc, rc);
10366 ctl.fUseHostIOCache = !!fUseHostIOCache;
10367
10368 /* save the devices now. */
10369 rc = i_saveStorageDevices(pCtl, ctl);
10370 ComAssertComRCRet(rc, rc);
10371
10372 data.llStorageControllers.push_back(ctl);
10373 }
10374
10375 return S_OK;
10376}
10377
10378/**
10379 * Saves the hard disk configuration.
10380 */
10381HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10382 settings::StorageController &data)
10383{
10384 MediumAttachmentList atts;
10385
10386 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10387 if (FAILED(rc)) return rc;
10388
10389 data.llAttachedDevices.clear();
10390 for (MediumAttachmentList::const_iterator
10391 it = atts.begin();
10392 it != atts.end();
10393 ++it)
10394 {
10395 settings::AttachedDevice dev;
10396 IMediumAttachment *iA = *it;
10397 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10398 Medium *pMedium = pAttach->i_getMedium();
10399
10400 dev.deviceType = pAttach->i_getType();
10401 dev.lPort = pAttach->i_getPort();
10402 dev.lDevice = pAttach->i_getDevice();
10403 dev.fPassThrough = pAttach->i_getPassthrough();
10404 dev.fHotPluggable = pAttach->i_getHotPluggable();
10405 if (pMedium)
10406 {
10407 if (pMedium->i_isHostDrive())
10408 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10409 else
10410 dev.uuid = pMedium->i_getId();
10411 dev.fTempEject = pAttach->i_getTempEject();
10412 dev.fNonRotational = pAttach->i_getNonRotational();
10413 dev.fDiscard = pAttach->i_getDiscard();
10414 }
10415
10416 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10417
10418 data.llAttachedDevices.push_back(dev);
10419 }
10420
10421 return S_OK;
10422}
10423
10424/**
10425 * Saves machine state settings as defined by aFlags
10426 * (SaveSTS_* values).
10427 *
10428 * @param aFlags Combination of SaveSTS_* flags.
10429 *
10430 * @note Locks objects for writing.
10431 */
10432HRESULT Machine::i_saveStateSettings(int aFlags)
10433{
10434 if (aFlags == 0)
10435 return S_OK;
10436
10437 AutoCaller autoCaller(this);
10438 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10439
10440 /* This object's write lock is also necessary to serialize file access
10441 * (prevent concurrent reads and writes) */
10442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10443
10444 HRESULT rc = S_OK;
10445
10446 Assert(mData->pMachineConfigFile);
10447
10448 try
10449 {
10450 if (aFlags & SaveSTS_CurStateModified)
10451 mData->pMachineConfigFile->fCurrentStateModified = true;
10452
10453 if (aFlags & SaveSTS_StateFilePath)
10454 {
10455 if (!mSSData->strStateFilePath.isEmpty())
10456 /* try to make the file name relative to the settings file dir */
10457 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10458 else
10459 mData->pMachineConfigFile->strStateFile.setNull();
10460 }
10461
10462 if (aFlags & SaveSTS_StateTimeStamp)
10463 {
10464 Assert( mData->mMachineState != MachineState_Aborted
10465 || mSSData->strStateFilePath.isEmpty());
10466
10467 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10468
10469 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10470/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10471 }
10472
10473 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10474 }
10475 catch (...)
10476 {
10477 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10478 }
10479
10480 return rc;
10481}
10482
10483/**
10484 * Ensures that the given medium is added to a media registry. If this machine
10485 * was created with 4.0 or later, then the machine registry is used. Otherwise
10486 * the global VirtualBox media registry is used.
10487 *
10488 * Caller must NOT hold machine lock, media tree or any medium locks!
10489 *
10490 * @param pMedium
10491 */
10492void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10493{
10494 /* Paranoia checks: do not hold machine or media tree locks. */
10495 AssertReturnVoid(!isWriteLockOnCurrentThread());
10496 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10497
10498 ComObjPtr<Medium> pBase;
10499 {
10500 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10501 pBase = pMedium->i_getBase();
10502 }
10503
10504 /* Paranoia checks: do not hold medium locks. */
10505 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10506 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10507
10508 // decide which medium registry to use now that the medium is attached:
10509 Guid uuid;
10510 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10511 if (fCanHaveOwnMediaRegistry)
10512 // machine XML is VirtualBox 4.0 or higher:
10513 uuid = i_getId(); // machine UUID
10514 else
10515 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10516
10517 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10518 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10519 if (pMedium->i_addRegistry(uuid))
10520 mParent->i_markRegistryModified(uuid);
10521
10522 /* For more complex hard disk structures it can happen that the base
10523 * medium isn't yet associated with any medium registry. Do that now. */
10524 if (pMedium != pBase)
10525 {
10526 /* Tree lock needed by Medium::addRegistry when recursing. */
10527 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10528 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10529 {
10530 treeLock.release();
10531 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10532 treeLock.acquire();
10533 }
10534 if (pBase->i_addRegistryRecursive(uuid))
10535 {
10536 treeLock.release();
10537 mParent->i_markRegistryModified(uuid);
10538 }
10539 }
10540}
10541
10542/**
10543 * Creates differencing hard disks for all normal hard disks attached to this
10544 * machine and a new set of attachments to refer to created disks.
10545 *
10546 * Used when taking a snapshot or when deleting the current state. Gets called
10547 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10548 *
10549 * This method assumes that mMediumAttachments contains the original hard disk
10550 * attachments it needs to create diffs for. On success, these attachments will
10551 * be replaced with the created diffs.
10552 *
10553 * Attachments with non-normal hard disks are left as is.
10554 *
10555 * If @a aOnline is @c false then the original hard disks that require implicit
10556 * diffs will be locked for reading. Otherwise it is assumed that they are
10557 * already locked for writing (when the VM was started). Note that in the latter
10558 * case it is responsibility of the caller to lock the newly created diffs for
10559 * writing if this method succeeds.
10560 *
10561 * @param aProgress Progress object to run (must contain at least as
10562 * many operations left as the number of hard disks
10563 * attached).
10564 * @param aWeight Weight of this operation.
10565 * @param aOnline Whether the VM was online prior to this operation.
10566 *
10567 * @note The progress object is not marked as completed, neither on success nor
10568 * on failure. This is a responsibility of the caller.
10569 *
10570 * @note Locks this object and the media tree for writing.
10571 */
10572HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10573 ULONG aWeight,
10574 bool aOnline)
10575{
10576 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10577
10578 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10579 AssertReturn(!!pProgressControl, E_INVALIDARG);
10580
10581 AutoCaller autoCaller(this);
10582 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10583
10584 AutoMultiWriteLock2 alock(this->lockHandle(),
10585 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10586
10587 /* must be in a protective state because we release the lock below */
10588 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10589 || mData->mMachineState == MachineState_OnlineSnapshotting
10590 || mData->mMachineState == MachineState_LiveSnapshotting
10591 || mData->mMachineState == MachineState_RestoringSnapshot
10592 || mData->mMachineState == MachineState_DeletingSnapshot
10593 , E_FAIL);
10594
10595 HRESULT rc = S_OK;
10596
10597 // use appropriate locked media map (online or offline)
10598 MediumLockListMap lockedMediaOffline;
10599 MediumLockListMap *lockedMediaMap;
10600 if (aOnline)
10601 lockedMediaMap = &mData->mSession.mLockedMedia;
10602 else
10603 lockedMediaMap = &lockedMediaOffline;
10604
10605 try
10606 {
10607 if (!aOnline)
10608 {
10609 /* lock all attached hard disks early to detect "in use"
10610 * situations before creating actual diffs */
10611 for (MediumAttachmentList::const_iterator
10612 it = mMediumAttachments->begin();
10613 it != mMediumAttachments->end();
10614 ++it)
10615 {
10616 MediumAttachment *pAtt = *it;
10617 if (pAtt->i_getType() == DeviceType_HardDisk)
10618 {
10619 Medium *pMedium = pAtt->i_getMedium();
10620 Assert(pMedium);
10621
10622 MediumLockList *pMediumLockList(new MediumLockList());
10623 alock.release();
10624 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10625 NULL /* pToLockWrite */,
10626 false /* fMediumLockWriteAll */,
10627 NULL,
10628 *pMediumLockList);
10629 alock.acquire();
10630 if (FAILED(rc))
10631 {
10632 delete pMediumLockList;
10633 throw rc;
10634 }
10635 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10636 if (FAILED(rc))
10637 {
10638 throw setError(rc,
10639 tr("Collecting locking information for all attached media failed"));
10640 }
10641 }
10642 }
10643
10644 /* Now lock all media. If this fails, nothing is locked. */
10645 alock.release();
10646 rc = lockedMediaMap->Lock();
10647 alock.acquire();
10648 if (FAILED(rc))
10649 {
10650 throw setError(rc,
10651 tr("Locking of attached media failed"));
10652 }
10653 }
10654
10655 /* remember the current list (note that we don't use backup() since
10656 * mMediumAttachments may be already backed up) */
10657 MediumAttachmentList atts = *mMediumAttachments.data();
10658
10659 /* start from scratch */
10660 mMediumAttachments->clear();
10661
10662 /* go through remembered attachments and create diffs for normal hard
10663 * disks and attach them */
10664 for (MediumAttachmentList::const_iterator
10665 it = atts.begin();
10666 it != atts.end();
10667 ++it)
10668 {
10669 MediumAttachment *pAtt = *it;
10670
10671 DeviceType_T devType = pAtt->i_getType();
10672 Medium *pMedium = pAtt->i_getMedium();
10673
10674 if ( devType != DeviceType_HardDisk
10675 || pMedium == NULL
10676 || pMedium->i_getType() != MediumType_Normal)
10677 {
10678 /* copy the attachment as is */
10679
10680 /** @todo the progress object created in SessionMachine::TakeSnaphot
10681 * only expects operations for hard disks. Later other
10682 * device types need to show up in the progress as well. */
10683 if (devType == DeviceType_HardDisk)
10684 {
10685 if (pMedium == NULL)
10686 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10687 aWeight); // weight
10688 else
10689 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10690 pMedium->i_getBase()->i_getName().c_str()).raw(),
10691 aWeight); // weight
10692 }
10693
10694 mMediumAttachments->push_back(pAtt);
10695 continue;
10696 }
10697
10698 /* need a diff */
10699 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10700 pMedium->i_getBase()->i_getName().c_str()).raw(),
10701 aWeight); // weight
10702
10703 Utf8Str strFullSnapshotFolder;
10704 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10705
10706 ComObjPtr<Medium> diff;
10707 diff.createObject();
10708 // store the diff in the same registry as the parent
10709 // (this cannot fail here because we can't create implicit diffs for
10710 // unregistered images)
10711 Guid uuidRegistryParent;
10712 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10713 Assert(fInRegistry); NOREF(fInRegistry);
10714 rc = diff->init(mParent,
10715 pMedium->i_getPreferredDiffFormat(),
10716 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10717 uuidRegistryParent,
10718 DeviceType_HardDisk);
10719 if (FAILED(rc)) throw rc;
10720
10721 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10722 * the push_back? Looks like we're going to release medium with the
10723 * wrong kind of lock (general issue with if we fail anywhere at all)
10724 * and an orphaned VDI in the snapshots folder. */
10725
10726 /* update the appropriate lock list */
10727 MediumLockList *pMediumLockList;
10728 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10729 AssertComRCThrowRC(rc);
10730 if (aOnline)
10731 {
10732 alock.release();
10733 /* The currently attached medium will be read-only, change
10734 * the lock type to read. */
10735 rc = pMediumLockList->Update(pMedium, false);
10736 alock.acquire();
10737 AssertComRCThrowRC(rc);
10738 }
10739
10740 /* release the locks before the potentially lengthy operation */
10741 alock.release();
10742 rc = pMedium->i_createDiffStorage(diff,
10743 pMedium->i_getPreferredDiffVariant(),
10744 pMediumLockList,
10745 NULL /* aProgress */,
10746 true /* aWait */,
10747 false /* aNotify */);
10748 alock.acquire();
10749 if (FAILED(rc)) throw rc;
10750
10751 /* actual lock list update is done in Machine::i_commitMedia */
10752
10753 rc = diff->i_addBackReference(mData->mUuid);
10754 AssertComRCThrowRC(rc);
10755
10756 /* add a new attachment */
10757 ComObjPtr<MediumAttachment> attachment;
10758 attachment.createObject();
10759 rc = attachment->init(this,
10760 diff,
10761 pAtt->i_getControllerName(),
10762 pAtt->i_getPort(),
10763 pAtt->i_getDevice(),
10764 DeviceType_HardDisk,
10765 true /* aImplicit */,
10766 false /* aPassthrough */,
10767 false /* aTempEject */,
10768 pAtt->i_getNonRotational(),
10769 pAtt->i_getDiscard(),
10770 pAtt->i_getHotPluggable(),
10771 pAtt->i_getBandwidthGroup());
10772 if (FAILED(rc)) throw rc;
10773
10774 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10775 AssertComRCThrowRC(rc);
10776 mMediumAttachments->push_back(attachment);
10777 }
10778 }
10779 catch (HRESULT aRC) { rc = aRC; }
10780
10781 /* unlock all hard disks we locked when there is no VM */
10782 if (!aOnline)
10783 {
10784 ErrorInfoKeeper eik;
10785
10786 HRESULT rc1 = lockedMediaMap->Clear();
10787 AssertComRC(rc1);
10788 }
10789
10790 return rc;
10791}
10792
10793/**
10794 * Deletes implicit differencing hard disks created either by
10795 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10796 * mMediumAttachments.
10797 *
10798 * Note that to delete hard disks created by #attachDevice() this method is
10799 * called from #i_rollbackMedia() when the changes are rolled back.
10800 *
10801 * @note Locks this object and the media tree for writing.
10802 */
10803HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10804{
10805 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10806
10807 AutoCaller autoCaller(this);
10808 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10809
10810 AutoMultiWriteLock2 alock(this->lockHandle(),
10811 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10812
10813 /* We absolutely must have backed up state. */
10814 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10815
10816 /* Check if there are any implicitly created diff images. */
10817 bool fImplicitDiffs = false;
10818 for (MediumAttachmentList::const_iterator
10819 it = mMediumAttachments->begin();
10820 it != mMediumAttachments->end();
10821 ++it)
10822 {
10823 const ComObjPtr<MediumAttachment> &pAtt = *it;
10824 if (pAtt->i_isImplicit())
10825 {
10826 fImplicitDiffs = true;
10827 break;
10828 }
10829 }
10830 /* If there is nothing to do, leave early. This saves lots of image locking
10831 * effort. It also avoids a MachineStateChanged event without real reason.
10832 * This is important e.g. when loading a VM config, because there should be
10833 * no events. Otherwise API clients can become thoroughly confused for
10834 * inaccessible VMs (the code for loading VM configs uses this method for
10835 * cleanup if the config makes no sense), as they take such events as an
10836 * indication that the VM is alive, and they would force the VM config to
10837 * be reread, leading to an endless loop. */
10838 if (!fImplicitDiffs)
10839 return S_OK;
10840
10841 HRESULT rc = S_OK;
10842 MachineState_T oldState = mData->mMachineState;
10843
10844 /* will release the lock before the potentially lengthy operation,
10845 * so protect with the special state (unless already protected) */
10846 if ( oldState != MachineState_Snapshotting
10847 && oldState != MachineState_OnlineSnapshotting
10848 && oldState != MachineState_LiveSnapshotting
10849 && oldState != MachineState_RestoringSnapshot
10850 && oldState != MachineState_DeletingSnapshot
10851 && oldState != MachineState_DeletingSnapshotOnline
10852 && oldState != MachineState_DeletingSnapshotPaused
10853 )
10854 i_setMachineState(MachineState_SettingUp);
10855
10856 // use appropriate locked media map (online or offline)
10857 MediumLockListMap lockedMediaOffline;
10858 MediumLockListMap *lockedMediaMap;
10859 if (aOnline)
10860 lockedMediaMap = &mData->mSession.mLockedMedia;
10861 else
10862 lockedMediaMap = &lockedMediaOffline;
10863
10864 try
10865 {
10866 if (!aOnline)
10867 {
10868 /* lock all attached hard disks early to detect "in use"
10869 * situations before deleting actual diffs */
10870 for (MediumAttachmentList::const_iterator
10871 it = mMediumAttachments->begin();
10872 it != mMediumAttachments->end();
10873 ++it)
10874 {
10875 MediumAttachment *pAtt = *it;
10876 if (pAtt->i_getType() == DeviceType_HardDisk)
10877 {
10878 Medium *pMedium = pAtt->i_getMedium();
10879 Assert(pMedium);
10880
10881 MediumLockList *pMediumLockList(new MediumLockList());
10882 alock.release();
10883 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10884 NULL /* pToLockWrite */,
10885 false /* fMediumLockWriteAll */,
10886 NULL,
10887 *pMediumLockList);
10888 alock.acquire();
10889
10890 if (FAILED(rc))
10891 {
10892 delete pMediumLockList;
10893 throw rc;
10894 }
10895
10896 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10897 if (FAILED(rc))
10898 throw rc;
10899 }
10900 }
10901
10902 if (FAILED(rc))
10903 throw rc;
10904 } // end of offline
10905
10906 /* Lock lists are now up to date and include implicitly created media */
10907
10908 /* Go through remembered attachments and delete all implicitly created
10909 * diffs and fix up the attachment information */
10910 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10911 MediumAttachmentList implicitAtts;
10912 for (MediumAttachmentList::const_iterator
10913 it = mMediumAttachments->begin();
10914 it != mMediumAttachments->end();
10915 ++it)
10916 {
10917 ComObjPtr<MediumAttachment> pAtt = *it;
10918 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10919 if (pMedium.isNull())
10920 continue;
10921
10922 // Implicit attachments go on the list for deletion and back references are removed.
10923 if (pAtt->i_isImplicit())
10924 {
10925 /* Deassociate and mark for deletion */
10926 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10927 rc = pMedium->i_removeBackReference(mData->mUuid);
10928 if (FAILED(rc))
10929 throw rc;
10930 implicitAtts.push_back(pAtt);
10931 continue;
10932 }
10933
10934 /* Was this medium attached before? */
10935 if (!i_findAttachment(oldAtts, pMedium))
10936 {
10937 /* no: de-associate */
10938 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10939 rc = pMedium->i_removeBackReference(mData->mUuid);
10940 if (FAILED(rc))
10941 throw rc;
10942 continue;
10943 }
10944 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10945 }
10946
10947 /* If there are implicit attachments to delete, throw away the lock
10948 * map contents (which will unlock all media) since the medium
10949 * attachments will be rolled back. Below we need to completely
10950 * recreate the lock map anyway since it is infinitely complex to
10951 * do this incrementally (would need reconstructing each attachment
10952 * change, which would be extremely hairy). */
10953 if (implicitAtts.size() != 0)
10954 {
10955 ErrorInfoKeeper eik;
10956
10957 HRESULT rc1 = lockedMediaMap->Clear();
10958 AssertComRC(rc1);
10959 }
10960
10961 /* rollback hard disk changes */
10962 mMediumAttachments.rollback();
10963
10964 MultiResult mrc(S_OK);
10965
10966 // Delete unused implicit diffs.
10967 if (implicitAtts.size() != 0)
10968 {
10969 alock.release();
10970
10971 for (MediumAttachmentList::const_iterator
10972 it = implicitAtts.begin();
10973 it != implicitAtts.end();
10974 ++it)
10975 {
10976 // Remove medium associated with this attachment.
10977 ComObjPtr<MediumAttachment> pAtt = *it;
10978 Assert(pAtt);
10979 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10980 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10981 Assert(pMedium);
10982
10983 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10984 // continue on delete failure, just collect error messages
10985 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10986 pMedium->i_getLocationFull().c_str() ));
10987 mrc = rc;
10988 }
10989 // Clear the list of deleted implicit attachments now, while not
10990 // holding the lock, as it will ultimately trigger Medium::uninit()
10991 // calls which assume that the media tree lock isn't held.
10992 implicitAtts.clear();
10993
10994 alock.acquire();
10995
10996 /* if there is a VM recreate media lock map as mentioned above,
10997 * otherwise it is a waste of time and we leave things unlocked */
10998 if (aOnline)
10999 {
11000 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11001 /* must never be NULL, but better safe than sorry */
11002 if (!pMachine.isNull())
11003 {
11004 alock.release();
11005 rc = mData->mSession.mMachine->i_lockMedia();
11006 alock.acquire();
11007 if (FAILED(rc))
11008 throw rc;
11009 }
11010 }
11011 }
11012 }
11013 catch (HRESULT aRC) {rc = aRC;}
11014
11015 if (mData->mMachineState == MachineState_SettingUp)
11016 i_setMachineState(oldState);
11017
11018 /* unlock all hard disks we locked when there is no VM */
11019 if (!aOnline)
11020 {
11021 ErrorInfoKeeper eik;
11022
11023 HRESULT rc1 = lockedMediaMap->Clear();
11024 AssertComRC(rc1);
11025 }
11026
11027 return rc;
11028}
11029
11030
11031/**
11032 * Looks through the given list of media attachments for one with the given parameters
11033 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11034 * can be searched as well if needed.
11035 *
11036 * @param ll
11037 * @param aControllerName
11038 * @param aControllerPort
11039 * @param aDevice
11040 * @return
11041 */
11042MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11043 const Utf8Str &aControllerName,
11044 LONG aControllerPort,
11045 LONG aDevice)
11046{
11047 for (MediumAttachmentList::const_iterator
11048 it = ll.begin();
11049 it != ll.end();
11050 ++it)
11051 {
11052 MediumAttachment *pAttach = *it;
11053 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11054 return pAttach;
11055 }
11056
11057 return NULL;
11058}
11059
11060/**
11061 * Looks through the given list of media attachments for one with the given parameters
11062 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11063 * can be searched as well if needed.
11064 *
11065 * @param ll
11066 * @param pMedium
11067 * @return
11068 */
11069MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11070 ComObjPtr<Medium> pMedium)
11071{
11072 for (MediumAttachmentList::const_iterator
11073 it = ll.begin();
11074 it != ll.end();
11075 ++it)
11076 {
11077 MediumAttachment *pAttach = *it;
11078 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11079 if (pMediumThis == pMedium)
11080 return pAttach;
11081 }
11082
11083 return NULL;
11084}
11085
11086/**
11087 * Looks through the given list of media attachments for one with the given parameters
11088 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11089 * can be searched as well if needed.
11090 *
11091 * @param ll
11092 * @param id
11093 * @return
11094 */
11095MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11096 Guid &id)
11097{
11098 for (MediumAttachmentList::const_iterator
11099 it = ll.begin();
11100 it != ll.end();
11101 ++it)
11102 {
11103 MediumAttachment *pAttach = *it;
11104 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11105 if (pMediumThis->i_getId() == id)
11106 return pAttach;
11107 }
11108
11109 return NULL;
11110}
11111
11112/**
11113 * Main implementation for Machine::DetachDevice. This also gets called
11114 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11115 *
11116 * @param pAttach Medium attachment to detach.
11117 * @param writeLock Machine write lock which the caller must have locked once.
11118 * This may be released temporarily in here.
11119 * @param pSnapshot If NULL, then the detachment is for the current machine.
11120 * Otherwise this is for a SnapshotMachine, and this must be
11121 * its snapshot.
11122 * @return
11123 */
11124HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11125 AutoWriteLock &writeLock,
11126 Snapshot *pSnapshot)
11127{
11128 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11129 DeviceType_T mediumType = pAttach->i_getType();
11130
11131 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11132
11133 if (pAttach->i_isImplicit())
11134 {
11135 /* attempt to implicitly delete the implicitly created diff */
11136
11137 /// @todo move the implicit flag from MediumAttachment to Medium
11138 /// and forbid any hard disk operation when it is implicit. Or maybe
11139 /// a special media state for it to make it even more simple.
11140
11141 Assert(mMediumAttachments.isBackedUp());
11142
11143 /* will release the lock before the potentially lengthy operation, so
11144 * protect with the special state */
11145 MachineState_T oldState = mData->mMachineState;
11146 i_setMachineState(MachineState_SettingUp);
11147
11148 writeLock.release();
11149
11150 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11151 true /*aWait*/,
11152 false /*aNotify*/);
11153
11154 writeLock.acquire();
11155
11156 i_setMachineState(oldState);
11157
11158 if (FAILED(rc)) return rc;
11159 }
11160
11161 i_setModified(IsModified_Storage);
11162 mMediumAttachments.backup();
11163 mMediumAttachments->remove(pAttach);
11164
11165 if (!oldmedium.isNull())
11166 {
11167 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11168 if (pSnapshot)
11169 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11170 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11171 else if (mediumType != DeviceType_HardDisk)
11172 oldmedium->i_removeBackReference(mData->mUuid);
11173 }
11174
11175 return S_OK;
11176}
11177
11178/**
11179 * Goes thru all media of the given list and
11180 *
11181 * 1) calls i_detachDevice() on each of them for this machine and
11182 * 2) adds all Medium objects found in the process to the given list,
11183 * depending on cleanupMode.
11184 *
11185 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11186 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11187 * media to the list.
11188 *
11189 * This gets called from Machine::Unregister, both for the actual Machine and
11190 * the SnapshotMachine objects that might be found in the snapshots.
11191 *
11192 * Requires caller and locking. The machine lock must be passed in because it
11193 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11194 *
11195 * @param writeLock Machine lock from top-level caller; this gets passed to
11196 * i_detachDevice.
11197 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11198 * object if called for a SnapshotMachine.
11199 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11200 * added to llMedia; if Full, then all media get added;
11201 * otherwise no media get added.
11202 * @param llMedia Caller's list to receive Medium objects which got detached so
11203 * caller can close() them, depending on cleanupMode.
11204 * @return
11205 */
11206HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11207 Snapshot *pSnapshot,
11208 CleanupMode_T cleanupMode,
11209 MediaList &llMedia)
11210{
11211 Assert(isWriteLockOnCurrentThread());
11212
11213 HRESULT rc;
11214
11215 // make a temporary list because i_detachDevice invalidates iterators into
11216 // mMediumAttachments
11217 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11218
11219 for (MediumAttachmentList::iterator
11220 it = llAttachments2.begin();
11221 it != llAttachments2.end();
11222 ++it)
11223 {
11224 ComObjPtr<MediumAttachment> &pAttach = *it;
11225 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11226
11227 if (!pMedium.isNull())
11228 {
11229 AutoCaller mac(pMedium);
11230 if (FAILED(mac.rc())) return mac.rc();
11231 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11232 DeviceType_T devType = pMedium->i_getDeviceType();
11233 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11234 && devType == DeviceType_HardDisk)
11235 || (cleanupMode == CleanupMode_Full)
11236 )
11237 {
11238 llMedia.push_back(pMedium);
11239 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11240 /* Not allowed to keep this lock as below we need the parent
11241 * medium lock, and the lock order is parent to child. */
11242 lock.release();
11243 /*
11244 * Search for medias which are not attached to any machine, but
11245 * in the chain to an attached disk. Mediums are only consided
11246 * if they are:
11247 * - have only one child
11248 * - no references to any machines
11249 * - are of normal medium type
11250 */
11251 while (!pParent.isNull())
11252 {
11253 AutoCaller mac1(pParent);
11254 if (FAILED(mac1.rc())) return mac1.rc();
11255 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11256 if (pParent->i_getChildren().size() == 1)
11257 {
11258 if ( pParent->i_getMachineBackRefCount() == 0
11259 && pParent->i_getType() == MediumType_Normal
11260 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11261 llMedia.push_back(pParent);
11262 }
11263 else
11264 break;
11265 pParent = pParent->i_getParent();
11266 }
11267 }
11268 }
11269
11270 // real machine: then we need to use the proper method
11271 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11272
11273 if (FAILED(rc))
11274 return rc;
11275 }
11276
11277 return S_OK;
11278}
11279
11280/**
11281 * Perform deferred hard disk detachments.
11282 *
11283 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11284 * changed (not backed up).
11285 *
11286 * If @a aOnline is @c true then this method will also unlock the old hard
11287 * disks for which the new implicit diffs were created and will lock these new
11288 * diffs for writing.
11289 *
11290 * @param aOnline Whether the VM was online prior to this operation.
11291 *
11292 * @note Locks this object for writing!
11293 */
11294void Machine::i_commitMedia(bool aOnline /*= false*/)
11295{
11296 AutoCaller autoCaller(this);
11297 AssertComRCReturnVoid(autoCaller.rc());
11298
11299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11300
11301 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11302
11303 HRESULT rc = S_OK;
11304
11305 /* no attach/detach operations -- nothing to do */
11306 if (!mMediumAttachments.isBackedUp())
11307 return;
11308
11309 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11310 bool fMediaNeedsLocking = false;
11311
11312 /* enumerate new attachments */
11313 for (MediumAttachmentList::const_iterator
11314 it = mMediumAttachments->begin();
11315 it != mMediumAttachments->end();
11316 ++it)
11317 {
11318 MediumAttachment *pAttach = *it;
11319
11320 pAttach->i_commit();
11321
11322 Medium *pMedium = pAttach->i_getMedium();
11323 bool fImplicit = pAttach->i_isImplicit();
11324
11325 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11326 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11327 fImplicit));
11328
11329 /** @todo convert all this Machine-based voodoo to MediumAttachment
11330 * based commit logic. */
11331 if (fImplicit)
11332 {
11333 /* convert implicit attachment to normal */
11334 pAttach->i_setImplicit(false);
11335
11336 if ( aOnline
11337 && pMedium
11338 && pAttach->i_getType() == DeviceType_HardDisk
11339 )
11340 {
11341 /* update the appropriate lock list */
11342 MediumLockList *pMediumLockList;
11343 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11344 AssertComRC(rc);
11345 if (pMediumLockList)
11346 {
11347 /* unlock if there's a need to change the locking */
11348 if (!fMediaNeedsLocking)
11349 {
11350 rc = mData->mSession.mLockedMedia.Unlock();
11351 AssertComRC(rc);
11352 fMediaNeedsLocking = true;
11353 }
11354 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11355 AssertComRC(rc);
11356 rc = pMediumLockList->Append(pMedium, true);
11357 AssertComRC(rc);
11358 }
11359 }
11360
11361 continue;
11362 }
11363
11364 if (pMedium)
11365 {
11366 /* was this medium attached before? */
11367 for (MediumAttachmentList::iterator
11368 oldIt = oldAtts.begin();
11369 oldIt != oldAtts.end();
11370 ++oldIt)
11371 {
11372 MediumAttachment *pOldAttach = *oldIt;
11373 if (pOldAttach->i_getMedium() == pMedium)
11374 {
11375 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11376
11377 /* yes: remove from old to avoid de-association */
11378 oldAtts.erase(oldIt);
11379 break;
11380 }
11381 }
11382 }
11383 }
11384
11385 /* enumerate remaining old attachments and de-associate from the
11386 * current machine state */
11387 for (MediumAttachmentList::const_iterator
11388 it = oldAtts.begin();
11389 it != oldAtts.end();
11390 ++it)
11391 {
11392 MediumAttachment *pAttach = *it;
11393 Medium *pMedium = pAttach->i_getMedium();
11394
11395 /* Detach only hard disks, since DVD/floppy media is detached
11396 * instantly in MountMedium. */
11397 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11398 {
11399 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11400
11401 /* now de-associate from the current machine state */
11402 rc = pMedium->i_removeBackReference(mData->mUuid);
11403 AssertComRC(rc);
11404
11405 if (aOnline)
11406 {
11407 /* unlock since medium is not used anymore */
11408 MediumLockList *pMediumLockList;
11409 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11410 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11411 {
11412 /* this happens for online snapshots, there the attachment
11413 * is changing, but only to a diff image created under
11414 * the old one, so there is no separate lock list */
11415 Assert(!pMediumLockList);
11416 }
11417 else
11418 {
11419 AssertComRC(rc);
11420 if (pMediumLockList)
11421 {
11422 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11423 AssertComRC(rc);
11424 }
11425 }
11426 }
11427 }
11428 }
11429
11430 /* take media locks again so that the locking state is consistent */
11431 if (fMediaNeedsLocking)
11432 {
11433 Assert(aOnline);
11434 rc = mData->mSession.mLockedMedia.Lock();
11435 AssertComRC(rc);
11436 }
11437
11438 /* commit the hard disk changes */
11439 mMediumAttachments.commit();
11440
11441 if (i_isSessionMachine())
11442 {
11443 /*
11444 * Update the parent machine to point to the new owner.
11445 * This is necessary because the stored parent will point to the
11446 * session machine otherwise and cause crashes or errors later
11447 * when the session machine gets invalid.
11448 */
11449 /** @todo Change the MediumAttachment class to behave like any other
11450 * class in this regard by creating peer MediumAttachment
11451 * objects for session machines and share the data with the peer
11452 * machine.
11453 */
11454 for (MediumAttachmentList::const_iterator
11455 it = mMediumAttachments->begin();
11456 it != mMediumAttachments->end();
11457 ++it)
11458 (*it)->i_updateParentMachine(mPeer);
11459
11460 /* attach new data to the primary machine and reshare it */
11461 mPeer->mMediumAttachments.attach(mMediumAttachments);
11462 }
11463
11464 return;
11465}
11466
11467/**
11468 * Perform deferred deletion of implicitly created diffs.
11469 *
11470 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11471 * changed (not backed up).
11472 *
11473 * @note Locks this object for writing!
11474 */
11475void Machine::i_rollbackMedia()
11476{
11477 AutoCaller autoCaller(this);
11478 AssertComRCReturnVoid(autoCaller.rc());
11479
11480 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11481 LogFlowThisFunc(("Entering rollbackMedia\n"));
11482
11483 HRESULT rc = S_OK;
11484
11485 /* no attach/detach operations -- nothing to do */
11486 if (!mMediumAttachments.isBackedUp())
11487 return;
11488
11489 /* enumerate new attachments */
11490 for (MediumAttachmentList::const_iterator
11491 it = mMediumAttachments->begin();
11492 it != mMediumAttachments->end();
11493 ++it)
11494 {
11495 MediumAttachment *pAttach = *it;
11496 /* Fix up the backrefs for DVD/floppy media. */
11497 if (pAttach->i_getType() != DeviceType_HardDisk)
11498 {
11499 Medium *pMedium = pAttach->i_getMedium();
11500 if (pMedium)
11501 {
11502 rc = pMedium->i_removeBackReference(mData->mUuid);
11503 AssertComRC(rc);
11504 }
11505 }
11506
11507 (*it)->i_rollback();
11508
11509 pAttach = *it;
11510 /* Fix up the backrefs for DVD/floppy media. */
11511 if (pAttach->i_getType() != DeviceType_HardDisk)
11512 {
11513 Medium *pMedium = pAttach->i_getMedium();
11514 if (pMedium)
11515 {
11516 rc = pMedium->i_addBackReference(mData->mUuid);
11517 AssertComRC(rc);
11518 }
11519 }
11520 }
11521
11522 /** @todo convert all this Machine-based voodoo to MediumAttachment
11523 * based rollback logic. */
11524 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11525
11526 return;
11527}
11528
11529/**
11530 * Returns true if the settings file is located in the directory named exactly
11531 * as the machine; this means, among other things, that the machine directory
11532 * should be auto-renamed.
11533 *
11534 * @param aSettingsDir if not NULL, the full machine settings file directory
11535 * name will be assigned there.
11536 *
11537 * @note Doesn't lock anything.
11538 * @note Not thread safe (must be called from this object's lock).
11539 */
11540bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11541{
11542 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11543 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11544 if (aSettingsDir)
11545 *aSettingsDir = strMachineDirName;
11546 strMachineDirName.stripPath(); // vmname
11547 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11548 strConfigFileOnly.stripPath() // vmname.vbox
11549 .stripSuffix(); // vmname
11550 /** @todo hack, make somehow use of ComposeMachineFilename */
11551 if (mUserData->s.fDirectoryIncludesUUID)
11552 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11553
11554 AssertReturn(!strMachineDirName.isEmpty(), false);
11555 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11556
11557 return strMachineDirName == strConfigFileOnly;
11558}
11559
11560/**
11561 * Discards all changes to machine settings.
11562 *
11563 * @param aNotify Whether to notify the direct session about changes or not.
11564 *
11565 * @note Locks objects for writing!
11566 */
11567void Machine::i_rollback(bool aNotify)
11568{
11569 AutoCaller autoCaller(this);
11570 AssertComRCReturn(autoCaller.rc(), (void)0);
11571
11572 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11573
11574 if (!mStorageControllers.isNull())
11575 {
11576 if (mStorageControllers.isBackedUp())
11577 {
11578 /* unitialize all new devices (absent in the backed up list). */
11579 StorageControllerList *backedList = mStorageControllers.backedUpData();
11580 for (StorageControllerList::const_iterator
11581 it = mStorageControllers->begin();
11582 it != mStorageControllers->end();
11583 ++it)
11584 {
11585 if ( std::find(backedList->begin(), backedList->end(), *it)
11586 == backedList->end()
11587 )
11588 {
11589 (*it)->uninit();
11590 }
11591 }
11592
11593 /* restore the list */
11594 mStorageControllers.rollback();
11595 }
11596
11597 /* rollback any changes to devices after restoring the list */
11598 if (mData->flModifications & IsModified_Storage)
11599 {
11600 for (StorageControllerList::const_iterator
11601 it = mStorageControllers->begin();
11602 it != mStorageControllers->end();
11603 ++it)
11604 {
11605 (*it)->i_rollback();
11606 }
11607 }
11608 }
11609
11610 if (!mUSBControllers.isNull())
11611 {
11612 if (mUSBControllers.isBackedUp())
11613 {
11614 /* unitialize all new devices (absent in the backed up list). */
11615 USBControllerList *backedList = mUSBControllers.backedUpData();
11616 for (USBControllerList::const_iterator
11617 it = mUSBControllers->begin();
11618 it != mUSBControllers->end();
11619 ++it)
11620 {
11621 if ( std::find(backedList->begin(), backedList->end(), *it)
11622 == backedList->end()
11623 )
11624 {
11625 (*it)->uninit();
11626 }
11627 }
11628
11629 /* restore the list */
11630 mUSBControllers.rollback();
11631 }
11632
11633 /* rollback any changes to devices after restoring the list */
11634 if (mData->flModifications & IsModified_USB)
11635 {
11636 for (USBControllerList::const_iterator
11637 it = mUSBControllers->begin();
11638 it != mUSBControllers->end();
11639 ++it)
11640 {
11641 (*it)->i_rollback();
11642 }
11643 }
11644 }
11645
11646 mUserData.rollback();
11647
11648 mHWData.rollback();
11649
11650 if (mData->flModifications & IsModified_Storage)
11651 i_rollbackMedia();
11652
11653 if (mBIOSSettings)
11654 mBIOSSettings->i_rollback();
11655
11656 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11657 mRecordingSettings->i_rollback();
11658
11659 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11660 mVRDEServer->i_rollback();
11661
11662 if (mAudioAdapter)
11663 mAudioAdapter->i_rollback();
11664
11665 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11666 mUSBDeviceFilters->i_rollback();
11667
11668 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11669 mBandwidthControl->i_rollback();
11670
11671 if (!mHWData.isNull())
11672 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11673 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11674 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11675 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11676
11677 if (mData->flModifications & IsModified_NetworkAdapters)
11678 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11679 if ( mNetworkAdapters[slot]
11680 && mNetworkAdapters[slot]->i_isModified())
11681 {
11682 mNetworkAdapters[slot]->i_rollback();
11683 networkAdapters[slot] = mNetworkAdapters[slot];
11684 }
11685
11686 if (mData->flModifications & IsModified_SerialPorts)
11687 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11688 if ( mSerialPorts[slot]
11689 && mSerialPorts[slot]->i_isModified())
11690 {
11691 mSerialPorts[slot]->i_rollback();
11692 serialPorts[slot] = mSerialPorts[slot];
11693 }
11694
11695 if (mData->flModifications & IsModified_ParallelPorts)
11696 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11697 if ( mParallelPorts[slot]
11698 && mParallelPorts[slot]->i_isModified())
11699 {
11700 mParallelPorts[slot]->i_rollback();
11701 parallelPorts[slot] = mParallelPorts[slot];
11702 }
11703
11704 if (aNotify)
11705 {
11706 /* inform the direct session about changes */
11707
11708 ComObjPtr<Machine> that = this;
11709 uint32_t flModifications = mData->flModifications;
11710 alock.release();
11711
11712 if (flModifications & IsModified_SharedFolders)
11713 that->i_onSharedFolderChange();
11714
11715 if (flModifications & IsModified_VRDEServer)
11716 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11717 if (flModifications & IsModified_USB)
11718 that->i_onUSBControllerChange();
11719
11720 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11721 if (networkAdapters[slot])
11722 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11723 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11724 if (serialPorts[slot])
11725 that->i_onSerialPortChange(serialPorts[slot]);
11726 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11727 if (parallelPorts[slot])
11728 that->i_onParallelPortChange(parallelPorts[slot]);
11729
11730 if (flModifications & IsModified_Storage)
11731 {
11732 for (StorageControllerList::const_iterator
11733 it = mStorageControllers->begin();
11734 it != mStorageControllers->end();
11735 ++it)
11736 {
11737 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11738 }
11739 }
11740
11741
11742#if 0
11743 if (flModifications & IsModified_BandwidthControl)
11744 that->onBandwidthControlChange();
11745#endif
11746 }
11747}
11748
11749/**
11750 * Commits all the changes to machine settings.
11751 *
11752 * Note that this operation is supposed to never fail.
11753 *
11754 * @note Locks this object and children for writing.
11755 */
11756void Machine::i_commit()
11757{
11758 AutoCaller autoCaller(this);
11759 AssertComRCReturnVoid(autoCaller.rc());
11760
11761 AutoCaller peerCaller(mPeer);
11762 AssertComRCReturnVoid(peerCaller.rc());
11763
11764 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11765
11766 /*
11767 * use safe commit to ensure Snapshot machines (that share mUserData)
11768 * will still refer to a valid memory location
11769 */
11770 mUserData.commitCopy();
11771
11772 mHWData.commit();
11773
11774 if (mMediumAttachments.isBackedUp())
11775 i_commitMedia(Global::IsOnline(mData->mMachineState));
11776
11777 mBIOSSettings->i_commit();
11778 mRecordingSettings->i_commit();
11779 mVRDEServer->i_commit();
11780 mAudioAdapter->i_commit();
11781 mUSBDeviceFilters->i_commit();
11782 mBandwidthControl->i_commit();
11783
11784 /* Since mNetworkAdapters is a list which might have been changed (resized)
11785 * without using the Backupable<> template we need to handle the copying
11786 * of the list entries manually, including the creation of peers for the
11787 * new objects. */
11788 bool commitNetworkAdapters = false;
11789 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11790 if (mPeer)
11791 {
11792 /* commit everything, even the ones which will go away */
11793 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11794 mNetworkAdapters[slot]->i_commit();
11795 /* copy over the new entries, creating a peer and uninit the original */
11796 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11797 for (size_t slot = 0; slot < newSize; slot++)
11798 {
11799 /* look if this adapter has a peer device */
11800 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11801 if (!peer)
11802 {
11803 /* no peer means the adapter is a newly created one;
11804 * create a peer owning data this data share it with */
11805 peer.createObject();
11806 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11807 }
11808 mPeer->mNetworkAdapters[slot] = peer;
11809 }
11810 /* uninit any no longer needed network adapters */
11811 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11812 mNetworkAdapters[slot]->uninit();
11813 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11814 {
11815 if (mPeer->mNetworkAdapters[slot])
11816 mPeer->mNetworkAdapters[slot]->uninit();
11817 }
11818 /* Keep the original network adapter count until this point, so that
11819 * discarding a chipset type change will not lose settings. */
11820 mNetworkAdapters.resize(newSize);
11821 mPeer->mNetworkAdapters.resize(newSize);
11822 }
11823 else
11824 {
11825 /* we have no peer (our parent is the newly created machine);
11826 * just commit changes to the network adapters */
11827 commitNetworkAdapters = true;
11828 }
11829 if (commitNetworkAdapters)
11830 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11831 mNetworkAdapters[slot]->i_commit();
11832
11833 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11834 mSerialPorts[slot]->i_commit();
11835 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11836 mParallelPorts[slot]->i_commit();
11837
11838 bool commitStorageControllers = false;
11839
11840 if (mStorageControllers.isBackedUp())
11841 {
11842 mStorageControllers.commit();
11843
11844 if (mPeer)
11845 {
11846 /* Commit all changes to new controllers (this will reshare data with
11847 * peers for those who have peers) */
11848 StorageControllerList *newList = new StorageControllerList();
11849 for (StorageControllerList::const_iterator
11850 it = mStorageControllers->begin();
11851 it != mStorageControllers->end();
11852 ++it)
11853 {
11854 (*it)->i_commit();
11855
11856 /* look if this controller has a peer device */
11857 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11858 if (!peer)
11859 {
11860 /* no peer means the device is a newly created one;
11861 * create a peer owning data this device share it with */
11862 peer.createObject();
11863 peer->init(mPeer, *it, true /* aReshare */);
11864 }
11865 else
11866 {
11867 /* remove peer from the old list */
11868 mPeer->mStorageControllers->remove(peer);
11869 }
11870 /* and add it to the new list */
11871 newList->push_back(peer);
11872 }
11873
11874 /* uninit old peer's controllers that are left */
11875 for (StorageControllerList::const_iterator
11876 it = mPeer->mStorageControllers->begin();
11877 it != mPeer->mStorageControllers->end();
11878 ++it)
11879 {
11880 (*it)->uninit();
11881 }
11882
11883 /* attach new list of controllers to our peer */
11884 mPeer->mStorageControllers.attach(newList);
11885 }
11886 else
11887 {
11888 /* we have no peer (our parent is the newly created machine);
11889 * just commit changes to devices */
11890 commitStorageControllers = true;
11891 }
11892 }
11893 else
11894 {
11895 /* the list of controllers itself is not changed,
11896 * just commit changes to controllers themselves */
11897 commitStorageControllers = true;
11898 }
11899
11900 if (commitStorageControllers)
11901 {
11902 for (StorageControllerList::const_iterator
11903 it = mStorageControllers->begin();
11904 it != mStorageControllers->end();
11905 ++it)
11906 {
11907 (*it)->i_commit();
11908 }
11909 }
11910
11911 bool commitUSBControllers = false;
11912
11913 if (mUSBControllers.isBackedUp())
11914 {
11915 mUSBControllers.commit();
11916
11917 if (mPeer)
11918 {
11919 /* Commit all changes to new controllers (this will reshare data with
11920 * peers for those who have peers) */
11921 USBControllerList *newList = new USBControllerList();
11922 for (USBControllerList::const_iterator
11923 it = mUSBControllers->begin();
11924 it != mUSBControllers->end();
11925 ++it)
11926 {
11927 (*it)->i_commit();
11928
11929 /* look if this controller has a peer device */
11930 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11931 if (!peer)
11932 {
11933 /* no peer means the device is a newly created one;
11934 * create a peer owning data this device share it with */
11935 peer.createObject();
11936 peer->init(mPeer, *it, true /* aReshare */);
11937 }
11938 else
11939 {
11940 /* remove peer from the old list */
11941 mPeer->mUSBControllers->remove(peer);
11942 }
11943 /* and add it to the new list */
11944 newList->push_back(peer);
11945 }
11946
11947 /* uninit old peer's controllers that are left */
11948 for (USBControllerList::const_iterator
11949 it = mPeer->mUSBControllers->begin();
11950 it != mPeer->mUSBControllers->end();
11951 ++it)
11952 {
11953 (*it)->uninit();
11954 }
11955
11956 /* attach new list of controllers to our peer */
11957 mPeer->mUSBControllers.attach(newList);
11958 }
11959 else
11960 {
11961 /* we have no peer (our parent is the newly created machine);
11962 * just commit changes to devices */
11963 commitUSBControllers = true;
11964 }
11965 }
11966 else
11967 {
11968 /* the list of controllers itself is not changed,
11969 * just commit changes to controllers themselves */
11970 commitUSBControllers = true;
11971 }
11972
11973 if (commitUSBControllers)
11974 {
11975 for (USBControllerList::const_iterator
11976 it = mUSBControllers->begin();
11977 it != mUSBControllers->end();
11978 ++it)
11979 {
11980 (*it)->i_commit();
11981 }
11982 }
11983
11984 if (i_isSessionMachine())
11985 {
11986 /* attach new data to the primary machine and reshare it */
11987 mPeer->mUserData.attach(mUserData);
11988 mPeer->mHWData.attach(mHWData);
11989 /* mmMediumAttachments is reshared by fixupMedia */
11990 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11991 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11992 }
11993}
11994
11995/**
11996 * Copies all the hardware data from the given machine.
11997 *
11998 * Currently, only called when the VM is being restored from a snapshot. In
11999 * particular, this implies that the VM is not running during this method's
12000 * call.
12001 *
12002 * @note This method must be called from under this object's lock.
12003 *
12004 * @note This method doesn't call #i_commit(), so all data remains backed up and
12005 * unsaved.
12006 */
12007void Machine::i_copyFrom(Machine *aThat)
12008{
12009 AssertReturnVoid(!i_isSnapshotMachine());
12010 AssertReturnVoid(aThat->i_isSnapshotMachine());
12011
12012 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12013
12014 mHWData.assignCopy(aThat->mHWData);
12015
12016 // create copies of all shared folders (mHWData after attaching a copy
12017 // contains just references to original objects)
12018 for (HWData::SharedFolderList::iterator
12019 it = mHWData->mSharedFolders.begin();
12020 it != mHWData->mSharedFolders.end();
12021 ++it)
12022 {
12023 ComObjPtr<SharedFolder> folder;
12024 folder.createObject();
12025 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12026 AssertComRC(rc);
12027 *it = folder;
12028 }
12029
12030 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12031 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12032 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12033 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12034 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12035 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12036
12037 /* create private copies of all controllers */
12038 mStorageControllers.backup();
12039 mStorageControllers->clear();
12040 for (StorageControllerList::const_iterator
12041 it = aThat->mStorageControllers->begin();
12042 it != aThat->mStorageControllers->end();
12043 ++it)
12044 {
12045 ComObjPtr<StorageController> ctrl;
12046 ctrl.createObject();
12047 ctrl->initCopy(this, *it);
12048 mStorageControllers->push_back(ctrl);
12049 }
12050
12051 /* create private copies of all USB controllers */
12052 mUSBControllers.backup();
12053 mUSBControllers->clear();
12054 for (USBControllerList::const_iterator
12055 it = aThat->mUSBControllers->begin();
12056 it != aThat->mUSBControllers->end();
12057 ++it)
12058 {
12059 ComObjPtr<USBController> ctrl;
12060 ctrl.createObject();
12061 ctrl->initCopy(this, *it);
12062 mUSBControllers->push_back(ctrl);
12063 }
12064
12065 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12066 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12067 {
12068 if (mNetworkAdapters[slot].isNotNull())
12069 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12070 else
12071 {
12072 unconst(mNetworkAdapters[slot]).createObject();
12073 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12074 }
12075 }
12076 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12077 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12078 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12079 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12080}
12081
12082/**
12083 * Returns whether the given storage controller is hotplug capable.
12084 *
12085 * @returns true if the controller supports hotplugging
12086 * false otherwise.
12087 * @param enmCtrlType The controller type to check for.
12088 */
12089bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12090{
12091 ComPtr<ISystemProperties> systemProperties;
12092 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12093 if (FAILED(rc))
12094 return false;
12095
12096 BOOL aHotplugCapable = FALSE;
12097 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12098
12099 return RT_BOOL(aHotplugCapable);
12100}
12101
12102#ifdef VBOX_WITH_RESOURCE_USAGE_API
12103
12104void Machine::i_getDiskList(MediaList &list)
12105{
12106 for (MediumAttachmentList::const_iterator
12107 it = mMediumAttachments->begin();
12108 it != mMediumAttachments->end();
12109 ++it)
12110 {
12111 MediumAttachment *pAttach = *it;
12112 /* just in case */
12113 AssertContinue(pAttach);
12114
12115 AutoCaller localAutoCallerA(pAttach);
12116 if (FAILED(localAutoCallerA.rc())) continue;
12117
12118 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12119
12120 if (pAttach->i_getType() == DeviceType_HardDisk)
12121 list.push_back(pAttach->i_getMedium());
12122 }
12123}
12124
12125void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12126{
12127 AssertReturnVoid(isWriteLockOnCurrentThread());
12128 AssertPtrReturnVoid(aCollector);
12129
12130 pm::CollectorHAL *hal = aCollector->getHAL();
12131 /* Create sub metrics */
12132 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12133 "Percentage of processor time spent in user mode by the VM process.");
12134 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12135 "Percentage of processor time spent in kernel mode by the VM process.");
12136 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12137 "Size of resident portion of VM process in memory.");
12138 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12139 "Actual size of all VM disks combined.");
12140 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12141 "Network receive rate.");
12142 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12143 "Network transmit rate.");
12144 /* Create and register base metrics */
12145 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12146 cpuLoadUser, cpuLoadKernel);
12147 aCollector->registerBaseMetric(cpuLoad);
12148 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12149 ramUsageUsed);
12150 aCollector->registerBaseMetric(ramUsage);
12151 MediaList disks;
12152 i_getDiskList(disks);
12153 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12154 diskUsageUsed);
12155 aCollector->registerBaseMetric(diskUsage);
12156
12157 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12158 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12159 new pm::AggregateAvg()));
12160 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12161 new pm::AggregateMin()));
12162 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12163 new pm::AggregateMax()));
12164 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12166 new pm::AggregateAvg()));
12167 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12168 new pm::AggregateMin()));
12169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12170 new pm::AggregateMax()));
12171
12172 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12173 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12174 new pm::AggregateAvg()));
12175 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12176 new pm::AggregateMin()));
12177 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12178 new pm::AggregateMax()));
12179
12180 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12181 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12182 new pm::AggregateAvg()));
12183 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12184 new pm::AggregateMin()));
12185 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12186 new pm::AggregateMax()));
12187
12188
12189 /* Guest metrics collector */
12190 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12191 aCollector->registerGuest(mCollectorGuest);
12192 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12193
12194 /* Create sub metrics */
12195 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12196 "Percentage of processor time spent in user mode as seen by the guest.");
12197 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12198 "Percentage of processor time spent in kernel mode as seen by the guest.");
12199 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12200 "Percentage of processor time spent idling as seen by the guest.");
12201
12202 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12203 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12204 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12205 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12206 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12207 pm::SubMetric *guestMemCache = new pm::SubMetric(
12208 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12209
12210 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12211 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12212
12213 /* Create and register base metrics */
12214 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12215 machineNetRx, machineNetTx);
12216 aCollector->registerBaseMetric(machineNetRate);
12217
12218 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12219 guestLoadUser, guestLoadKernel, guestLoadIdle);
12220 aCollector->registerBaseMetric(guestCpuLoad);
12221
12222 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12223 guestMemTotal, guestMemFree,
12224 guestMemBalloon, guestMemShared,
12225 guestMemCache, guestPagedTotal);
12226 aCollector->registerBaseMetric(guestCpuMem);
12227
12228 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12229 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12230 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12231 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12232
12233 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12234 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12236 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12237
12238 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12239 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12247
12248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12252
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12257
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12262
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12267
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12272
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12277
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12282}
12283
12284void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12285{
12286 AssertReturnVoid(isWriteLockOnCurrentThread());
12287
12288 if (aCollector)
12289 {
12290 aCollector->unregisterMetricsFor(aMachine);
12291 aCollector->unregisterBaseMetricsFor(aMachine);
12292 }
12293}
12294
12295#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12296
12297
12298////////////////////////////////////////////////////////////////////////////////
12299
12300DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12301
12302HRESULT SessionMachine::FinalConstruct()
12303{
12304 LogFlowThisFunc(("\n"));
12305
12306 mClientToken = NULL;
12307
12308 return BaseFinalConstruct();
12309}
12310
12311void SessionMachine::FinalRelease()
12312{
12313 LogFlowThisFunc(("\n"));
12314
12315 Assert(!mClientToken);
12316 /* paranoia, should not hang around any more */
12317 if (mClientToken)
12318 {
12319 delete mClientToken;
12320 mClientToken = NULL;
12321 }
12322
12323 uninit(Uninit::Unexpected);
12324
12325 BaseFinalRelease();
12326}
12327
12328/**
12329 * @note Must be called only by Machine::LockMachine() from its own write lock.
12330 */
12331HRESULT SessionMachine::init(Machine *aMachine)
12332{
12333 LogFlowThisFuncEnter();
12334 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12335
12336 AssertReturn(aMachine, E_INVALIDARG);
12337
12338 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12339
12340 /* Enclose the state transition NotReady->InInit->Ready */
12341 AutoInitSpan autoInitSpan(this);
12342 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12343
12344 HRESULT rc = S_OK;
12345
12346 RT_ZERO(mAuthLibCtx);
12347
12348 /* create the machine client token */
12349 try
12350 {
12351 mClientToken = new ClientToken(aMachine, this);
12352 if (!mClientToken->isReady())
12353 {
12354 delete mClientToken;
12355 mClientToken = NULL;
12356 rc = E_FAIL;
12357 }
12358 }
12359 catch (std::bad_alloc &)
12360 {
12361 rc = E_OUTOFMEMORY;
12362 }
12363 if (FAILED(rc))
12364 return rc;
12365
12366 /* memorize the peer Machine */
12367 unconst(mPeer) = aMachine;
12368 /* share the parent pointer */
12369 unconst(mParent) = aMachine->mParent;
12370
12371 /* take the pointers to data to share */
12372 mData.share(aMachine->mData);
12373 mSSData.share(aMachine->mSSData);
12374
12375 mUserData.share(aMachine->mUserData);
12376 mHWData.share(aMachine->mHWData);
12377 mMediumAttachments.share(aMachine->mMediumAttachments);
12378
12379 mStorageControllers.allocate();
12380 for (StorageControllerList::const_iterator
12381 it = aMachine->mStorageControllers->begin();
12382 it != aMachine->mStorageControllers->end();
12383 ++it)
12384 {
12385 ComObjPtr<StorageController> ctl;
12386 ctl.createObject();
12387 ctl->init(this, *it);
12388 mStorageControllers->push_back(ctl);
12389 }
12390
12391 mUSBControllers.allocate();
12392 for (USBControllerList::const_iterator
12393 it = aMachine->mUSBControllers->begin();
12394 it != aMachine->mUSBControllers->end();
12395 ++it)
12396 {
12397 ComObjPtr<USBController> ctl;
12398 ctl.createObject();
12399 ctl->init(this, *it);
12400 mUSBControllers->push_back(ctl);
12401 }
12402
12403 unconst(mBIOSSettings).createObject();
12404 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12405 unconst(mRecordingSettings).createObject();
12406 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12407 /* create another VRDEServer object that will be mutable */
12408 unconst(mVRDEServer).createObject();
12409 mVRDEServer->init(this, aMachine->mVRDEServer);
12410 /* create another audio adapter object that will be mutable */
12411 unconst(mAudioAdapter).createObject();
12412 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12413 /* create a list of serial ports that will be mutable */
12414 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12415 {
12416 unconst(mSerialPorts[slot]).createObject();
12417 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12418 }
12419 /* create a list of parallel ports that will be mutable */
12420 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12421 {
12422 unconst(mParallelPorts[slot]).createObject();
12423 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12424 }
12425
12426 /* create another USB device filters object that will be mutable */
12427 unconst(mUSBDeviceFilters).createObject();
12428 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12429
12430 /* create a list of network adapters that will be mutable */
12431 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12432 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12433 {
12434 unconst(mNetworkAdapters[slot]).createObject();
12435 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12436 }
12437
12438 /* create another bandwidth control object that will be mutable */
12439 unconst(mBandwidthControl).createObject();
12440 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12441
12442 /* default is to delete saved state on Saved -> PoweredOff transition */
12443 mRemoveSavedState = true;
12444
12445 /* Confirm a successful initialization when it's the case */
12446 autoInitSpan.setSucceeded();
12447
12448 miNATNetworksStarted = 0;
12449
12450 LogFlowThisFuncLeave();
12451 return rc;
12452}
12453
12454/**
12455 * Uninitializes this session object. If the reason is other than
12456 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12457 * or the client watcher code.
12458 *
12459 * @param aReason uninitialization reason
12460 *
12461 * @note Locks mParent + this object for writing.
12462 */
12463void SessionMachine::uninit(Uninit::Reason aReason)
12464{
12465 LogFlowThisFuncEnter();
12466 LogFlowThisFunc(("reason=%d\n", aReason));
12467
12468 /*
12469 * Strongly reference ourselves to prevent this object deletion after
12470 * mData->mSession.mMachine.setNull() below (which can release the last
12471 * reference and call the destructor). Important: this must be done before
12472 * accessing any members (and before AutoUninitSpan that does it as well).
12473 * This self reference will be released as the very last step on return.
12474 */
12475 ComObjPtr<SessionMachine> selfRef;
12476 if (aReason != Uninit::Unexpected)
12477 selfRef = this;
12478
12479 /* Enclose the state transition Ready->InUninit->NotReady */
12480 AutoUninitSpan autoUninitSpan(this);
12481 if (autoUninitSpan.uninitDone())
12482 {
12483 LogFlowThisFunc(("Already uninitialized\n"));
12484 LogFlowThisFuncLeave();
12485 return;
12486 }
12487
12488 if (autoUninitSpan.initFailed())
12489 {
12490 /* We've been called by init() because it's failed. It's not really
12491 * necessary (nor it's safe) to perform the regular uninit sequence
12492 * below, the following is enough.
12493 */
12494 LogFlowThisFunc(("Initialization failed.\n"));
12495 /* destroy the machine client token */
12496 if (mClientToken)
12497 {
12498 delete mClientToken;
12499 mClientToken = NULL;
12500 }
12501 uninitDataAndChildObjects();
12502 mData.free();
12503 unconst(mParent) = NULL;
12504 unconst(mPeer) = NULL;
12505 LogFlowThisFuncLeave();
12506 return;
12507 }
12508
12509 MachineState_T lastState;
12510 {
12511 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12512 lastState = mData->mMachineState;
12513 }
12514 NOREF(lastState);
12515
12516#ifdef VBOX_WITH_USB
12517 // release all captured USB devices, but do this before requesting the locks below
12518 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12519 {
12520 /* Console::captureUSBDevices() is called in the VM process only after
12521 * setting the machine state to Starting or Restoring.
12522 * Console::detachAllUSBDevices() will be called upon successful
12523 * termination. So, we need to release USB devices only if there was
12524 * an abnormal termination of a running VM.
12525 *
12526 * This is identical to SessionMachine::DetachAllUSBDevices except
12527 * for the aAbnormal argument. */
12528 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12529 AssertComRC(rc);
12530 NOREF(rc);
12531
12532 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12533 if (service)
12534 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12535 }
12536#endif /* VBOX_WITH_USB */
12537
12538 // we need to lock this object in uninit() because the lock is shared
12539 // with mPeer (as well as data we modify below). mParent lock is needed
12540 // by several calls to it.
12541 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12542
12543#ifdef VBOX_WITH_RESOURCE_USAGE_API
12544 /*
12545 * It is safe to call Machine::i_unregisterMetrics() here because
12546 * PerformanceCollector::samplerCallback no longer accesses guest methods
12547 * holding the lock.
12548 */
12549 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12550 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12551 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12552 if (mCollectorGuest)
12553 {
12554 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12555 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12556 mCollectorGuest = NULL;
12557 }
12558#endif
12559
12560 if (aReason == Uninit::Abnormal)
12561 {
12562 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12563
12564 /* reset the state to Aborted */
12565 if (mData->mMachineState != MachineState_Aborted)
12566 i_setMachineState(MachineState_Aborted);
12567 }
12568
12569 // any machine settings modified?
12570 if (mData->flModifications)
12571 {
12572 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12573 i_rollback(false /* aNotify */);
12574 }
12575
12576 mData->mSession.mPID = NIL_RTPROCESS;
12577
12578 if (aReason == Uninit::Unexpected)
12579 {
12580 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12581 * client watcher thread to update the set of machines that have open
12582 * sessions. */
12583 mParent->i_updateClientWatcher();
12584 }
12585
12586 /* uninitialize all remote controls */
12587 if (mData->mSession.mRemoteControls.size())
12588 {
12589 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12590 mData->mSession.mRemoteControls.size()));
12591
12592 /* Always restart a the beginning, since the iterator is invalidated
12593 * by using erase(). */
12594 for (Data::Session::RemoteControlList::iterator
12595 it = mData->mSession.mRemoteControls.begin();
12596 it != mData->mSession.mRemoteControls.end();
12597 it = mData->mSession.mRemoteControls.begin())
12598 {
12599 ComPtr<IInternalSessionControl> pControl = *it;
12600 mData->mSession.mRemoteControls.erase(it);
12601 multilock.release();
12602 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12603 HRESULT rc = pControl->Uninitialize();
12604 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12605 if (FAILED(rc))
12606 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12607 multilock.acquire();
12608 }
12609 mData->mSession.mRemoteControls.clear();
12610 }
12611
12612 /* Remove all references to the NAT network service. The service will stop
12613 * if all references (also from other VMs) are removed. */
12614 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12615 {
12616 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12617 {
12618 BOOL enabled;
12619 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12620 if ( FAILED(hrc)
12621 || !enabled)
12622 continue;
12623
12624 NetworkAttachmentType_T type;
12625 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12626 if ( SUCCEEDED(hrc)
12627 && type == NetworkAttachmentType_NATNetwork)
12628 {
12629 Bstr name;
12630 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12631 if (SUCCEEDED(hrc))
12632 {
12633 multilock.release();
12634 Utf8Str strName(name);
12635 LogRel(("VM '%s' stops using NAT network '%s'\n",
12636 mUserData->s.strName.c_str(), strName.c_str()));
12637 mParent->i_natNetworkRefDec(strName);
12638 multilock.acquire();
12639 }
12640 }
12641 }
12642 }
12643
12644 /*
12645 * An expected uninitialization can come only from #i_checkForDeath().
12646 * Otherwise it means that something's gone really wrong (for example,
12647 * the Session implementation has released the VirtualBox reference
12648 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12649 * etc). However, it's also possible, that the client releases the IPC
12650 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12651 * but the VirtualBox release event comes first to the server process.
12652 * This case is practically possible, so we should not assert on an
12653 * unexpected uninit, just log a warning.
12654 */
12655
12656 if (aReason == Uninit::Unexpected)
12657 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12658
12659 if (aReason != Uninit::Normal)
12660 {
12661 mData->mSession.mDirectControl.setNull();
12662 }
12663 else
12664 {
12665 /* this must be null here (see #OnSessionEnd()) */
12666 Assert(mData->mSession.mDirectControl.isNull());
12667 Assert(mData->mSession.mState == SessionState_Unlocking);
12668 Assert(!mData->mSession.mProgress.isNull());
12669 }
12670 if (mData->mSession.mProgress)
12671 {
12672 if (aReason == Uninit::Normal)
12673 mData->mSession.mProgress->i_notifyComplete(S_OK);
12674 else
12675 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12676 COM_IIDOF(ISession),
12677 getComponentName(),
12678 tr("The VM session was aborted"));
12679 mData->mSession.mProgress.setNull();
12680 }
12681
12682 if (mConsoleTaskData.mProgress)
12683 {
12684 Assert(aReason == Uninit::Abnormal);
12685 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12686 COM_IIDOF(ISession),
12687 getComponentName(),
12688 tr("The VM session was aborted"));
12689 mConsoleTaskData.mProgress.setNull();
12690 }
12691
12692 /* remove the association between the peer machine and this session machine */
12693 Assert( (SessionMachine*)mData->mSession.mMachine == this
12694 || aReason == Uninit::Unexpected);
12695
12696 /* reset the rest of session data */
12697 mData->mSession.mLockType = LockType_Null;
12698 mData->mSession.mMachine.setNull();
12699 mData->mSession.mState = SessionState_Unlocked;
12700 mData->mSession.mName.setNull();
12701
12702 /* destroy the machine client token before leaving the exclusive lock */
12703 if (mClientToken)
12704 {
12705 delete mClientToken;
12706 mClientToken = NULL;
12707 }
12708
12709 /* fire an event */
12710 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12711
12712 uninitDataAndChildObjects();
12713
12714 /* free the essential data structure last */
12715 mData.free();
12716
12717 /* release the exclusive lock before setting the below two to NULL */
12718 multilock.release();
12719
12720 unconst(mParent) = NULL;
12721 unconst(mPeer) = NULL;
12722
12723 AuthLibUnload(&mAuthLibCtx);
12724
12725 LogFlowThisFuncLeave();
12726}
12727
12728// util::Lockable interface
12729////////////////////////////////////////////////////////////////////////////////
12730
12731/**
12732 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12733 * with the primary Machine instance (mPeer).
12734 */
12735RWLockHandle *SessionMachine::lockHandle() const
12736{
12737 AssertReturn(mPeer != NULL, NULL);
12738 return mPeer->lockHandle();
12739}
12740
12741// IInternalMachineControl methods
12742////////////////////////////////////////////////////////////////////////////////
12743
12744/**
12745 * Passes collected guest statistics to performance collector object
12746 */
12747HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12748 ULONG aCpuKernel, ULONG aCpuIdle,
12749 ULONG aMemTotal, ULONG aMemFree,
12750 ULONG aMemBalloon, ULONG aMemShared,
12751 ULONG aMemCache, ULONG aPageTotal,
12752 ULONG aAllocVMM, ULONG aFreeVMM,
12753 ULONG aBalloonedVMM, ULONG aSharedVMM,
12754 ULONG aVmNetRx, ULONG aVmNetTx)
12755{
12756#ifdef VBOX_WITH_RESOURCE_USAGE_API
12757 if (mCollectorGuest)
12758 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12759 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12760 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12761 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12762
12763 return S_OK;
12764#else
12765 NOREF(aValidStats);
12766 NOREF(aCpuUser);
12767 NOREF(aCpuKernel);
12768 NOREF(aCpuIdle);
12769 NOREF(aMemTotal);
12770 NOREF(aMemFree);
12771 NOREF(aMemBalloon);
12772 NOREF(aMemShared);
12773 NOREF(aMemCache);
12774 NOREF(aPageTotal);
12775 NOREF(aAllocVMM);
12776 NOREF(aFreeVMM);
12777 NOREF(aBalloonedVMM);
12778 NOREF(aSharedVMM);
12779 NOREF(aVmNetRx);
12780 NOREF(aVmNetTx);
12781 return E_NOTIMPL;
12782#endif
12783}
12784
12785////////////////////////////////////////////////////////////////////////////////
12786//
12787// SessionMachine task records
12788//
12789////////////////////////////////////////////////////////////////////////////////
12790
12791/**
12792 * Task record for saving the machine state.
12793 */
12794class SessionMachine::SaveStateTask
12795 : public Machine::Task
12796{
12797public:
12798 SaveStateTask(SessionMachine *m,
12799 Progress *p,
12800 const Utf8Str &t,
12801 Reason_T enmReason,
12802 const Utf8Str &strStateFilePath)
12803 : Task(m, p, t),
12804 m_enmReason(enmReason),
12805 m_strStateFilePath(strStateFilePath)
12806 {}
12807
12808private:
12809 void handler()
12810 {
12811 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12812 }
12813
12814 Reason_T m_enmReason;
12815 Utf8Str m_strStateFilePath;
12816
12817 friend class SessionMachine;
12818};
12819
12820/**
12821 * Task thread implementation for SessionMachine::SaveState(), called from
12822 * SessionMachine::taskHandler().
12823 *
12824 * @note Locks this object for writing.
12825 *
12826 * @param task
12827 * @return
12828 */
12829void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12830{
12831 LogFlowThisFuncEnter();
12832
12833 AutoCaller autoCaller(this);
12834 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12835 if (FAILED(autoCaller.rc()))
12836 {
12837 /* we might have been uninitialized because the session was accidentally
12838 * closed by the client, so don't assert */
12839 HRESULT rc = setError(E_FAIL,
12840 tr("The session has been accidentally closed"));
12841 task.m_pProgress->i_notifyComplete(rc);
12842 LogFlowThisFuncLeave();
12843 return;
12844 }
12845
12846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12847
12848 HRESULT rc = S_OK;
12849
12850 try
12851 {
12852 ComPtr<IInternalSessionControl> directControl;
12853 if (mData->mSession.mLockType == LockType_VM)
12854 directControl = mData->mSession.mDirectControl;
12855 if (directControl.isNull())
12856 throw setError(VBOX_E_INVALID_VM_STATE,
12857 tr("Trying to save state without a running VM"));
12858 alock.release();
12859 BOOL fSuspendedBySave;
12860 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12861 Assert(!fSuspendedBySave);
12862 alock.acquire();
12863
12864 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12865 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12866 throw E_FAIL);
12867
12868 if (SUCCEEDED(rc))
12869 {
12870 mSSData->strStateFilePath = task.m_strStateFilePath;
12871
12872 /* save all VM settings */
12873 rc = i_saveSettings(NULL);
12874 // no need to check whether VirtualBox.xml needs saving also since
12875 // we can't have a name change pending at this point
12876 }
12877 else
12878 {
12879 // On failure, set the state to the state we had at the beginning.
12880 i_setMachineState(task.m_machineStateBackup);
12881 i_updateMachineStateOnClient();
12882
12883 // Delete the saved state file (might have been already created).
12884 // No need to check whether this is shared with a snapshot here
12885 // because we certainly created a fresh saved state file here.
12886 RTFileDelete(task.m_strStateFilePath.c_str());
12887 }
12888 }
12889 catch (HRESULT aRC) { rc = aRC; }
12890
12891 task.m_pProgress->i_notifyComplete(rc);
12892
12893 LogFlowThisFuncLeave();
12894}
12895
12896/**
12897 * @note Locks this object for writing.
12898 */
12899HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12900{
12901 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12902}
12903
12904HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12905{
12906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12907
12908 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12909 if (FAILED(rc)) return rc;
12910
12911 if ( mData->mMachineState != MachineState_Running
12912 && mData->mMachineState != MachineState_Paused
12913 )
12914 return setError(VBOX_E_INVALID_VM_STATE,
12915 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12916 Global::stringifyMachineState(mData->mMachineState));
12917
12918 ComObjPtr<Progress> pProgress;
12919 pProgress.createObject();
12920 rc = pProgress->init(i_getVirtualBox(),
12921 static_cast<IMachine *>(this) /* aInitiator */,
12922 tr("Saving the execution state of the virtual machine"),
12923 FALSE /* aCancelable */);
12924 if (FAILED(rc))
12925 return rc;
12926
12927 Utf8Str strStateFilePath;
12928 i_composeSavedStateFilename(strStateFilePath);
12929
12930 /* create and start the task on a separate thread (note that it will not
12931 * start working until we release alock) */
12932 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12933 rc = pTask->createThread();
12934 if (FAILED(rc))
12935 return rc;
12936
12937 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12938 i_setMachineState(MachineState_Saving);
12939 i_updateMachineStateOnClient();
12940
12941 pProgress.queryInterfaceTo(aProgress.asOutParam());
12942
12943 return S_OK;
12944}
12945
12946/**
12947 * @note Locks this object for writing.
12948 */
12949HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12950{
12951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12952
12953 HRESULT rc = i_checkStateDependency(MutableStateDep);
12954 if (FAILED(rc)) return rc;
12955
12956 if ( mData->mMachineState != MachineState_PoweredOff
12957 && mData->mMachineState != MachineState_Teleported
12958 && mData->mMachineState != MachineState_Aborted
12959 )
12960 return setError(VBOX_E_INVALID_VM_STATE,
12961 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12962 Global::stringifyMachineState(mData->mMachineState));
12963
12964 com::Utf8Str stateFilePathFull;
12965 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12966 if (RT_FAILURE(vrc))
12967 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12968 tr("Invalid saved state file path '%s' (%Rrc)"),
12969 aSavedStateFile.c_str(),
12970 vrc);
12971
12972 mSSData->strStateFilePath = stateFilePathFull;
12973
12974 /* The below i_setMachineState() will detect the state transition and will
12975 * update the settings file */
12976
12977 return i_setMachineState(MachineState_Saved);
12978}
12979
12980/**
12981 * @note Locks this object for writing.
12982 */
12983HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12984{
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12988 if (FAILED(rc)) return rc;
12989
12990 if (mData->mMachineState != MachineState_Saved)
12991 return setError(VBOX_E_INVALID_VM_STATE,
12992 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12993 Global::stringifyMachineState(mData->mMachineState));
12994
12995 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12996
12997 /*
12998 * Saved -> PoweredOff transition will be detected in the SessionMachine
12999 * and properly handled.
13000 */
13001 rc = i_setMachineState(MachineState_PoweredOff);
13002 return rc;
13003}
13004
13005
13006/**
13007 * @note Locks the same as #i_setMachineState() does.
13008 */
13009HRESULT SessionMachine::updateState(MachineState_T aState)
13010{
13011 return i_setMachineState(aState);
13012}
13013
13014/**
13015 * @note Locks this object for writing.
13016 */
13017HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13018{
13019 IProgress *pProgress(aProgress);
13020
13021 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13022
13023 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13024
13025 if (mData->mSession.mState != SessionState_Locked)
13026 return VBOX_E_INVALID_OBJECT_STATE;
13027
13028 if (!mData->mSession.mProgress.isNull())
13029 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13030
13031 /* If we didn't reference the NAT network service yet, add a reference to
13032 * force a start */
13033 if (miNATNetworksStarted < 1)
13034 {
13035 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13036 {
13037 BOOL enabled;
13038 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13039 if ( FAILED(hrc)
13040 || !enabled)
13041 continue;
13042
13043 NetworkAttachmentType_T type;
13044 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13045 if ( SUCCEEDED(hrc)
13046 && type == NetworkAttachmentType_NATNetwork)
13047 {
13048 Bstr name;
13049 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13050 if (SUCCEEDED(hrc))
13051 {
13052 Utf8Str strName(name);
13053 LogRel(("VM '%s' starts using NAT network '%s'\n",
13054 mUserData->s.strName.c_str(), strName.c_str()));
13055 mPeer->lockHandle()->unlockWrite();
13056 mParent->i_natNetworkRefInc(strName);
13057#ifdef RT_LOCK_STRICT
13058 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13059#else
13060 mPeer->lockHandle()->lockWrite();
13061#endif
13062 }
13063 }
13064 }
13065 miNATNetworksStarted++;
13066 }
13067
13068 LogFlowThisFunc(("returns S_OK.\n"));
13069 return S_OK;
13070}
13071
13072/**
13073 * @note Locks this object for writing.
13074 */
13075HRESULT SessionMachine::endPowerUp(LONG aResult)
13076{
13077 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13078
13079 if (mData->mSession.mState != SessionState_Locked)
13080 return VBOX_E_INVALID_OBJECT_STATE;
13081
13082 /* Finalize the LaunchVMProcess progress object. */
13083 if (mData->mSession.mProgress)
13084 {
13085 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13086 mData->mSession.mProgress.setNull();
13087 }
13088
13089 if (SUCCEEDED((HRESULT)aResult))
13090 {
13091#ifdef VBOX_WITH_RESOURCE_USAGE_API
13092 /* The VM has been powered up successfully, so it makes sense
13093 * now to offer the performance metrics for a running machine
13094 * object. Doing it earlier wouldn't be safe. */
13095 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13096 mData->mSession.mPID);
13097#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13098 }
13099
13100 return S_OK;
13101}
13102
13103/**
13104 * @note Locks this object for writing.
13105 */
13106HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13107{
13108 LogFlowThisFuncEnter();
13109
13110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13111
13112 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13113 E_FAIL);
13114
13115 /* create a progress object to track operation completion */
13116 ComObjPtr<Progress> pProgress;
13117 pProgress.createObject();
13118 pProgress->init(i_getVirtualBox(),
13119 static_cast<IMachine *>(this) /* aInitiator */,
13120 tr("Stopping the virtual machine"),
13121 FALSE /* aCancelable */);
13122
13123 /* fill in the console task data */
13124 mConsoleTaskData.mLastState = mData->mMachineState;
13125 mConsoleTaskData.mProgress = pProgress;
13126
13127 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13128 i_setMachineState(MachineState_Stopping);
13129
13130 pProgress.queryInterfaceTo(aProgress.asOutParam());
13131
13132 return S_OK;
13133}
13134
13135/**
13136 * @note Locks this object for writing.
13137 */
13138HRESULT SessionMachine::endPoweringDown(LONG aResult,
13139 const com::Utf8Str &aErrMsg)
13140{
13141 LogFlowThisFuncEnter();
13142
13143 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13144
13145 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13146 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13147 && mConsoleTaskData.mLastState != MachineState_Null,
13148 E_FAIL);
13149
13150 /*
13151 * On failure, set the state to the state we had when BeginPoweringDown()
13152 * was called (this is expected by Console::PowerDown() and the associated
13153 * task). On success the VM process already changed the state to
13154 * MachineState_PoweredOff, so no need to do anything.
13155 */
13156 if (FAILED(aResult))
13157 i_setMachineState(mConsoleTaskData.mLastState);
13158
13159 /* notify the progress object about operation completion */
13160 Assert(mConsoleTaskData.mProgress);
13161 if (SUCCEEDED(aResult))
13162 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13163 else
13164 {
13165 if (aErrMsg.length())
13166 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13167 COM_IIDOF(ISession),
13168 getComponentName(),
13169 aErrMsg.c_str());
13170 else
13171 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13172 }
13173
13174 /* clear out the temporary saved state data */
13175 mConsoleTaskData.mLastState = MachineState_Null;
13176 mConsoleTaskData.mProgress.setNull();
13177
13178 LogFlowThisFuncLeave();
13179 return S_OK;
13180}
13181
13182
13183/**
13184 * Goes through the USB filters of the given machine to see if the given
13185 * device matches any filter or not.
13186 *
13187 * @note Locks the same as USBController::hasMatchingFilter() does.
13188 */
13189HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13190 BOOL *aMatched,
13191 ULONG *aMaskedInterfaces)
13192{
13193 LogFlowThisFunc(("\n"));
13194
13195#ifdef VBOX_WITH_USB
13196 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13197#else
13198 NOREF(aDevice);
13199 NOREF(aMaskedInterfaces);
13200 *aMatched = FALSE;
13201#endif
13202
13203 return S_OK;
13204}
13205
13206/**
13207 * @note Locks the same as Host::captureUSBDevice() does.
13208 */
13209HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13210{
13211 LogFlowThisFunc(("\n"));
13212
13213#ifdef VBOX_WITH_USB
13214 /* if captureDeviceForVM() fails, it must have set extended error info */
13215 clearError();
13216 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13217 if (FAILED(rc)) return rc;
13218
13219 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13220 AssertReturn(service, E_FAIL);
13221 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13222#else
13223 NOREF(aId);
13224 return E_NOTIMPL;
13225#endif
13226}
13227
13228/**
13229 * @note Locks the same as Host::detachUSBDevice() does.
13230 */
13231HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13232 BOOL aDone)
13233{
13234 LogFlowThisFunc(("\n"));
13235
13236#ifdef VBOX_WITH_USB
13237 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13238 AssertReturn(service, E_FAIL);
13239 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13240#else
13241 NOREF(aId);
13242 NOREF(aDone);
13243 return E_NOTIMPL;
13244#endif
13245}
13246
13247/**
13248 * Inserts all machine filters to the USB proxy service and then calls
13249 * Host::autoCaptureUSBDevices().
13250 *
13251 * Called by Console from the VM process upon VM startup.
13252 *
13253 * @note Locks what called methods lock.
13254 */
13255HRESULT SessionMachine::autoCaptureUSBDevices()
13256{
13257 LogFlowThisFunc(("\n"));
13258
13259#ifdef VBOX_WITH_USB
13260 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13261 AssertComRC(rc);
13262 NOREF(rc);
13263
13264 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13265 AssertReturn(service, E_FAIL);
13266 return service->autoCaptureDevicesForVM(this);
13267#else
13268 return S_OK;
13269#endif
13270}
13271
13272/**
13273 * Removes all machine filters from the USB proxy service and then calls
13274 * Host::detachAllUSBDevices().
13275 *
13276 * Called by Console from the VM process upon normal VM termination or by
13277 * SessionMachine::uninit() upon abnormal VM termination (from under the
13278 * Machine/SessionMachine lock).
13279 *
13280 * @note Locks what called methods lock.
13281 */
13282HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13283{
13284 LogFlowThisFunc(("\n"));
13285
13286#ifdef VBOX_WITH_USB
13287 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13288 AssertComRC(rc);
13289 NOREF(rc);
13290
13291 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13292 AssertReturn(service, E_FAIL);
13293 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13294#else
13295 NOREF(aDone);
13296 return S_OK;
13297#endif
13298}
13299
13300/**
13301 * @note Locks this object for writing.
13302 */
13303HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13304 ComPtr<IProgress> &aProgress)
13305{
13306 LogFlowThisFuncEnter();
13307
13308 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13309 /*
13310 * We don't assert below because it might happen that a non-direct session
13311 * informs us it is closed right after we've been uninitialized -- it's ok.
13312 */
13313
13314 /* get IInternalSessionControl interface */
13315 ComPtr<IInternalSessionControl> control(aSession);
13316
13317 ComAssertRet(!control.isNull(), E_INVALIDARG);
13318
13319 /* Creating a Progress object requires the VirtualBox lock, and
13320 * thus locking it here is required by the lock order rules. */
13321 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13322
13323 if (control == mData->mSession.mDirectControl)
13324 {
13325 /* The direct session is being normally closed by the client process
13326 * ----------------------------------------------------------------- */
13327
13328 /* go to the closing state (essential for all open*Session() calls and
13329 * for #i_checkForDeath()) */
13330 Assert(mData->mSession.mState == SessionState_Locked);
13331 mData->mSession.mState = SessionState_Unlocking;
13332
13333 /* set direct control to NULL to release the remote instance */
13334 mData->mSession.mDirectControl.setNull();
13335 LogFlowThisFunc(("Direct control is set to NULL\n"));
13336
13337 if (mData->mSession.mProgress)
13338 {
13339 /* finalize the progress, someone might wait if a frontend
13340 * closes the session before powering on the VM. */
13341 mData->mSession.mProgress->notifyComplete(E_FAIL,
13342 COM_IIDOF(ISession),
13343 getComponentName(),
13344 tr("The VM session was closed before any attempt to power it on"));
13345 mData->mSession.mProgress.setNull();
13346 }
13347
13348 /* Create the progress object the client will use to wait until
13349 * #i_checkForDeath() is called to uninitialize this session object after
13350 * it releases the IPC semaphore.
13351 * Note! Because we're "reusing" mProgress here, this must be a proxy
13352 * object just like for LaunchVMProcess. */
13353 Assert(mData->mSession.mProgress.isNull());
13354 ComObjPtr<ProgressProxy> progress;
13355 progress.createObject();
13356 ComPtr<IUnknown> pPeer(mPeer);
13357 progress->init(mParent, pPeer,
13358 Bstr(tr("Closing session")).raw(),
13359 FALSE /* aCancelable */);
13360 progress.queryInterfaceTo(aProgress.asOutParam());
13361 mData->mSession.mProgress = progress;
13362 }
13363 else
13364 {
13365 /* the remote session is being normally closed */
13366 bool found = false;
13367 for (Data::Session::RemoteControlList::iterator
13368 it = mData->mSession.mRemoteControls.begin();
13369 it != mData->mSession.mRemoteControls.end();
13370 ++it)
13371 {
13372 if (control == *it)
13373 {
13374 found = true;
13375 // This MUST be erase(it), not remove(*it) as the latter
13376 // triggers a very nasty use after free due to the place where
13377 // the value "lives".
13378 mData->mSession.mRemoteControls.erase(it);
13379 break;
13380 }
13381 }
13382 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13383 E_INVALIDARG);
13384 }
13385
13386 /* signal the client watcher thread, because the client is going away */
13387 mParent->i_updateClientWatcher();
13388
13389 LogFlowThisFuncLeave();
13390 return S_OK;
13391}
13392
13393HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13394{
13395#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13396 ULONG uID;
13397 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13398 if (RT_SUCCESS(rc))
13399 {
13400 if (aID)
13401 *aID = uID;
13402 return S_OK;
13403 }
13404 return E_FAIL;
13405#else
13406 RT_NOREF(aParms, aID);
13407 ReturnComNotImplemented();
13408#endif
13409}
13410
13411HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13412{
13413#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13414 return mParent->i_onClipboardAreaUnregister(aID);
13415#else
13416 RT_NOREF(aID);
13417 ReturnComNotImplemented();
13418#endif
13419}
13420
13421HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13422{
13423#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13424 return mParent->i_onClipboardAreaAttach(aID);
13425#else
13426 RT_NOREF(aID);
13427 ReturnComNotImplemented();
13428#endif
13429}
13430HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13431{
13432#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13433 return mParent->i_onClipboardAreaDetach(aID);
13434#else
13435 RT_NOREF(aID);
13436 ReturnComNotImplemented();
13437#endif
13438}
13439
13440HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13441{
13442#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13443 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13444 if (aID)
13445 *aID = uID;
13446 return S_OK;
13447#else
13448 RT_NOREF(aID);
13449 ReturnComNotImplemented();
13450#endif
13451}
13452
13453HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13454{
13455#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13456 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13457 if (aRefCount)
13458 *aRefCount = uRefCount;
13459 return S_OK;
13460#else
13461 RT_NOREF(aID, aRefCount);
13462 ReturnComNotImplemented();
13463#endif
13464}
13465
13466HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13467 std::vector<com::Utf8Str> &aValues,
13468 std::vector<LONG64> &aTimestamps,
13469 std::vector<com::Utf8Str> &aFlags)
13470{
13471 LogFlowThisFunc(("\n"));
13472
13473#ifdef VBOX_WITH_GUEST_PROPS
13474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13475
13476 size_t cEntries = mHWData->mGuestProperties.size();
13477 aNames.resize(cEntries);
13478 aValues.resize(cEntries);
13479 aTimestamps.resize(cEntries);
13480 aFlags.resize(cEntries);
13481
13482 size_t i = 0;
13483 for (HWData::GuestPropertyMap::const_iterator
13484 it = mHWData->mGuestProperties.begin();
13485 it != mHWData->mGuestProperties.end();
13486 ++it, ++i)
13487 {
13488 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13489 aNames[i] = it->first;
13490 aValues[i] = it->second.strValue;
13491 aTimestamps[i] = it->second.mTimestamp;
13492
13493 /* If it is NULL, keep it NULL. */
13494 if (it->second.mFlags)
13495 {
13496 GuestPropWriteFlags(it->second.mFlags, szFlags);
13497 aFlags[i] = szFlags;
13498 }
13499 else
13500 aFlags[i] = "";
13501 }
13502 return S_OK;
13503#else
13504 ReturnComNotImplemented();
13505#endif
13506}
13507
13508HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13509 const com::Utf8Str &aValue,
13510 LONG64 aTimestamp,
13511 const com::Utf8Str &aFlags)
13512{
13513 LogFlowThisFunc(("\n"));
13514
13515#ifdef VBOX_WITH_GUEST_PROPS
13516 try
13517 {
13518 /*
13519 * Convert input up front.
13520 */
13521 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13522 if (aFlags.length())
13523 {
13524 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13525 AssertRCReturn(vrc, E_INVALIDARG);
13526 }
13527
13528 /*
13529 * Now grab the object lock, validate the state and do the update.
13530 */
13531
13532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13533
13534 if (!Global::IsOnline(mData->mMachineState))
13535 {
13536 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13537 VBOX_E_INVALID_VM_STATE);
13538 }
13539
13540 i_setModified(IsModified_MachineData);
13541 mHWData.backup();
13542
13543 bool fDelete = !aValue.length();
13544 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13545 if (it != mHWData->mGuestProperties.end())
13546 {
13547 if (!fDelete)
13548 {
13549 it->second.strValue = aValue;
13550 it->second.mTimestamp = aTimestamp;
13551 it->second.mFlags = fFlags;
13552 }
13553 else
13554 mHWData->mGuestProperties.erase(it);
13555
13556 mData->mGuestPropertiesModified = TRUE;
13557 }
13558 else if (!fDelete)
13559 {
13560 HWData::GuestProperty prop;
13561 prop.strValue = aValue;
13562 prop.mTimestamp = aTimestamp;
13563 prop.mFlags = fFlags;
13564
13565 mHWData->mGuestProperties[aName] = prop;
13566 mData->mGuestPropertiesModified = TRUE;
13567 }
13568
13569 alock.release();
13570
13571 mParent->i_onGuestPropertyChange(mData->mUuid,
13572 Bstr(aName).raw(),
13573 Bstr(aValue).raw(),
13574 Bstr(aFlags).raw());
13575 }
13576 catch (...)
13577 {
13578 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13579 }
13580 return S_OK;
13581#else
13582 ReturnComNotImplemented();
13583#endif
13584}
13585
13586
13587HRESULT SessionMachine::lockMedia()
13588{
13589 AutoMultiWriteLock2 alock(this->lockHandle(),
13590 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13591
13592 AssertReturn( mData->mMachineState == MachineState_Starting
13593 || mData->mMachineState == MachineState_Restoring
13594 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13595
13596 clearError();
13597 alock.release();
13598 return i_lockMedia();
13599}
13600
13601HRESULT SessionMachine::unlockMedia()
13602{
13603 HRESULT hrc = i_unlockMedia();
13604 return hrc;
13605}
13606
13607HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13608 ComPtr<IMediumAttachment> &aNewAttachment)
13609{
13610 // request the host lock first, since might be calling Host methods for getting host drives;
13611 // next, protect the media tree all the while we're in here, as well as our member variables
13612 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13613 this->lockHandle(),
13614 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13615
13616 IMediumAttachment *iAttach = aAttachment;
13617 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13618
13619 Utf8Str ctrlName;
13620 LONG lPort;
13621 LONG lDevice;
13622 bool fTempEject;
13623 {
13624 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13625
13626 /* Need to query the details first, as the IMediumAttachment reference
13627 * might be to the original settings, which we are going to change. */
13628 ctrlName = pAttach->i_getControllerName();
13629 lPort = pAttach->i_getPort();
13630 lDevice = pAttach->i_getDevice();
13631 fTempEject = pAttach->i_getTempEject();
13632 }
13633
13634 if (!fTempEject)
13635 {
13636 /* Remember previously mounted medium. The medium before taking the
13637 * backup is not necessarily the same thing. */
13638 ComObjPtr<Medium> oldmedium;
13639 oldmedium = pAttach->i_getMedium();
13640
13641 i_setModified(IsModified_Storage);
13642 mMediumAttachments.backup();
13643
13644 // The backup operation makes the pAttach reference point to the
13645 // old settings. Re-get the correct reference.
13646 pAttach = i_findAttachment(*mMediumAttachments.data(),
13647 ctrlName,
13648 lPort,
13649 lDevice);
13650
13651 {
13652 AutoCaller autoAttachCaller(this);
13653 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13654
13655 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13656 if (!oldmedium.isNull())
13657 oldmedium->i_removeBackReference(mData->mUuid);
13658
13659 pAttach->i_updateMedium(NULL);
13660 pAttach->i_updateEjected();
13661 }
13662
13663 i_setModified(IsModified_Storage);
13664 }
13665 else
13666 {
13667 {
13668 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13669 pAttach->i_updateEjected();
13670 }
13671 }
13672
13673 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13674
13675 return S_OK;
13676}
13677
13678HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13679 com::Utf8Str &aResult)
13680{
13681 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13682
13683 HRESULT hr = S_OK;
13684
13685 if (!mAuthLibCtx.hAuthLibrary)
13686 {
13687 /* Load the external authentication library. */
13688 Bstr authLibrary;
13689 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13690
13691 Utf8Str filename = authLibrary;
13692
13693 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13694 if (RT_FAILURE(vrc))
13695 hr = setErrorBoth(E_FAIL, vrc,
13696 tr("Could not load the external authentication library '%s' (%Rrc)"),
13697 filename.c_str(), vrc);
13698 }
13699
13700 /* The auth library might need the machine lock. */
13701 alock.release();
13702
13703 if (FAILED(hr))
13704 return hr;
13705
13706 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13707 {
13708 enum VRDEAuthParams
13709 {
13710 parmUuid = 1,
13711 parmGuestJudgement,
13712 parmUser,
13713 parmPassword,
13714 parmDomain,
13715 parmClientId
13716 };
13717
13718 AuthResult result = AuthResultAccessDenied;
13719
13720 Guid uuid(aAuthParams[parmUuid]);
13721 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13722 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13723
13724 result = AuthLibAuthenticate(&mAuthLibCtx,
13725 uuid.raw(), guestJudgement,
13726 aAuthParams[parmUser].c_str(),
13727 aAuthParams[parmPassword].c_str(),
13728 aAuthParams[parmDomain].c_str(),
13729 u32ClientId);
13730
13731 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13732 size_t cbPassword = aAuthParams[parmPassword].length();
13733 if (cbPassword)
13734 {
13735 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13736 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13737 }
13738
13739 if (result == AuthResultAccessGranted)
13740 aResult = "granted";
13741 else
13742 aResult = "denied";
13743
13744 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13745 aAuthParams[parmUser].c_str(), aResult.c_str()));
13746 }
13747 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13748 {
13749 enum VRDEAuthDisconnectParams
13750 {
13751 parmUuid = 1,
13752 parmClientId
13753 };
13754
13755 Guid uuid(aAuthParams[parmUuid]);
13756 uint32_t u32ClientId = 0;
13757 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13758 }
13759 else
13760 {
13761 hr = E_INVALIDARG;
13762 }
13763
13764 return hr;
13765}
13766
13767// public methods only for internal purposes
13768/////////////////////////////////////////////////////////////////////////////
13769
13770#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13771/**
13772 * Called from the client watcher thread to check for expected or unexpected
13773 * death of the client process that has a direct session to this machine.
13774 *
13775 * On Win32 and on OS/2, this method is called only when we've got the
13776 * mutex (i.e. the client has either died or terminated normally) so it always
13777 * returns @c true (the client is terminated, the session machine is
13778 * uninitialized).
13779 *
13780 * On other platforms, the method returns @c true if the client process has
13781 * terminated normally or abnormally and the session machine was uninitialized,
13782 * and @c false if the client process is still alive.
13783 *
13784 * @note Locks this object for writing.
13785 */
13786bool SessionMachine::i_checkForDeath()
13787{
13788 Uninit::Reason reason;
13789 bool terminated = false;
13790
13791 /* Enclose autoCaller with a block because calling uninit() from under it
13792 * will deadlock. */
13793 {
13794 AutoCaller autoCaller(this);
13795 if (!autoCaller.isOk())
13796 {
13797 /* return true if not ready, to cause the client watcher to exclude
13798 * the corresponding session from watching */
13799 LogFlowThisFunc(("Already uninitialized!\n"));
13800 return true;
13801 }
13802
13803 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13804
13805 /* Determine the reason of death: if the session state is Closing here,
13806 * everything is fine. Otherwise it means that the client did not call
13807 * OnSessionEnd() before it released the IPC semaphore. This may happen
13808 * either because the client process has abnormally terminated, or
13809 * because it simply forgot to call ISession::Close() before exiting. We
13810 * threat the latter also as an abnormal termination (see
13811 * Session::uninit() for details). */
13812 reason = mData->mSession.mState == SessionState_Unlocking ?
13813 Uninit::Normal :
13814 Uninit::Abnormal;
13815
13816 if (mClientToken)
13817 terminated = mClientToken->release();
13818 } /* AutoCaller block */
13819
13820 if (terminated)
13821 uninit(reason);
13822
13823 return terminated;
13824}
13825
13826void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13827{
13828 LogFlowThisFunc(("\n"));
13829
13830 strTokenId.setNull();
13831
13832 AutoCaller autoCaller(this);
13833 AssertComRCReturnVoid(autoCaller.rc());
13834
13835 Assert(mClientToken);
13836 if (mClientToken)
13837 mClientToken->getId(strTokenId);
13838}
13839#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13840IToken *SessionMachine::i_getToken()
13841{
13842 LogFlowThisFunc(("\n"));
13843
13844 AutoCaller autoCaller(this);
13845 AssertComRCReturn(autoCaller.rc(), NULL);
13846
13847 Assert(mClientToken);
13848 if (mClientToken)
13849 return mClientToken->getToken();
13850 else
13851 return NULL;
13852}
13853#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13854
13855Machine::ClientToken *SessionMachine::i_getClientToken()
13856{
13857 LogFlowThisFunc(("\n"));
13858
13859 AutoCaller autoCaller(this);
13860 AssertComRCReturn(autoCaller.rc(), NULL);
13861
13862 return mClientToken;
13863}
13864
13865
13866/**
13867 * @note Locks this object for reading.
13868 */
13869HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13870{
13871 LogFlowThisFunc(("\n"));
13872
13873 AutoCaller autoCaller(this);
13874 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13875
13876 ComPtr<IInternalSessionControl> directControl;
13877 {
13878 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13879 if (mData->mSession.mLockType == LockType_VM)
13880 directControl = mData->mSession.mDirectControl;
13881 }
13882
13883 /* ignore notifications sent after #OnSessionEnd() is called */
13884 if (!directControl)
13885 return S_OK;
13886
13887 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13888}
13889
13890/**
13891 * @note Locks this object for reading.
13892 */
13893HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13894 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13895 IN_BSTR aGuestIp, LONG aGuestPort)
13896{
13897 LogFlowThisFunc(("\n"));
13898
13899 AutoCaller autoCaller(this);
13900 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13901
13902 ComPtr<IInternalSessionControl> directControl;
13903 {
13904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13905 if (mData->mSession.mLockType == LockType_VM)
13906 directControl = mData->mSession.mDirectControl;
13907 }
13908
13909 /* ignore notifications sent after #OnSessionEnd() is called */
13910 if (!directControl)
13911 return S_OK;
13912 /*
13913 * instead acting like callback we ask IVirtualBox deliver corresponding event
13914 */
13915
13916 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13917 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13918 return S_OK;
13919}
13920
13921/**
13922 * @note Locks this object for reading.
13923 */
13924HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13925{
13926 LogFlowThisFunc(("\n"));
13927
13928 AutoCaller autoCaller(this);
13929 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13930
13931 ComPtr<IInternalSessionControl> directControl;
13932 {
13933 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13934 if (mData->mSession.mLockType == LockType_VM)
13935 directControl = mData->mSession.mDirectControl;
13936 }
13937
13938 /* ignore notifications sent after #OnSessionEnd() is called */
13939 if (!directControl)
13940 return S_OK;
13941
13942 return directControl->OnAudioAdapterChange(audioAdapter);
13943}
13944
13945/**
13946 * @note Locks this object for reading.
13947 */
13948HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13949{
13950 LogFlowThisFunc(("\n"));
13951
13952 AutoCaller autoCaller(this);
13953 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13954
13955 ComPtr<IInternalSessionControl> directControl;
13956 {
13957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13958 if (mData->mSession.mLockType == LockType_VM)
13959 directControl = mData->mSession.mDirectControl;
13960 }
13961
13962 /* ignore notifications sent after #OnSessionEnd() is called */
13963 if (!directControl)
13964 return S_OK;
13965
13966 return directControl->OnSerialPortChange(serialPort);
13967}
13968
13969/**
13970 * @note Locks this object for reading.
13971 */
13972HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13973{
13974 LogFlowThisFunc(("\n"));
13975
13976 AutoCaller autoCaller(this);
13977 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13978
13979 ComPtr<IInternalSessionControl> directControl;
13980 {
13981 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13982 if (mData->mSession.mLockType == LockType_VM)
13983 directControl = mData->mSession.mDirectControl;
13984 }
13985
13986 /* ignore notifications sent after #OnSessionEnd() is called */
13987 if (!directControl)
13988 return S_OK;
13989
13990 return directControl->OnParallelPortChange(parallelPort);
13991}
13992
13993/**
13994 * @note Locks this object for reading.
13995 */
13996HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
13997{
13998 LogFlowThisFunc(("\n"));
13999
14000 AutoCaller autoCaller(this);
14001 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14002
14003 ComPtr<IInternalSessionControl> directControl;
14004 {
14005 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14006 if (mData->mSession.mLockType == LockType_VM)
14007 directControl = mData->mSession.mDirectControl;
14008 }
14009
14010 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14011
14012 /* ignore notifications sent after #OnSessionEnd() is called */
14013 if (!directControl)
14014 return S_OK;
14015
14016 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14017}
14018
14019/**
14020 * @note Locks this object for reading.
14021 */
14022HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14023{
14024 LogFlowThisFunc(("\n"));
14025
14026 AutoCaller autoCaller(this);
14027 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14028
14029 ComPtr<IInternalSessionControl> directControl;
14030 {
14031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14032 if (mData->mSession.mLockType == LockType_VM)
14033 directControl = mData->mSession.mDirectControl;
14034 }
14035
14036 mParent->i_onMediumChanged(aAttachment);
14037
14038 /* ignore notifications sent after #OnSessionEnd() is called */
14039 if (!directControl)
14040 return S_OK;
14041
14042 return directControl->OnMediumChange(aAttachment, aForce);
14043}
14044
14045HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14046{
14047 LogFlowThisFunc(("\n"));
14048
14049 AutoCaller autoCaller(this);
14050 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14051
14052 ComPtr<IInternalSessionControl> directControl;
14053 {
14054 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14055 if (mData->mSession.mLockType == LockType_VM)
14056 directControl = mData->mSession.mDirectControl;
14057 }
14058
14059 /* ignore notifications sent after #OnSessionEnd() is called */
14060 if (!directControl)
14061 return S_OK;
14062
14063 return directControl->OnVMProcessPriorityChange(aPriority);
14064}
14065
14066/**
14067 * @note Locks this object for reading.
14068 */
14069HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14070{
14071 LogFlowThisFunc(("\n"));
14072
14073 AutoCaller autoCaller(this);
14074 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14075
14076 ComPtr<IInternalSessionControl> directControl;
14077 {
14078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14079 if (mData->mSession.mLockType == LockType_VM)
14080 directControl = mData->mSession.mDirectControl;
14081 }
14082
14083 /* ignore notifications sent after #OnSessionEnd() is called */
14084 if (!directControl)
14085 return S_OK;
14086
14087 return directControl->OnCPUChange(aCPU, aRemove);
14088}
14089
14090HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14091{
14092 LogFlowThisFunc(("\n"));
14093
14094 AutoCaller autoCaller(this);
14095 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14096
14097 ComPtr<IInternalSessionControl> directControl;
14098 {
14099 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14100 if (mData->mSession.mLockType == LockType_VM)
14101 directControl = mData->mSession.mDirectControl;
14102 }
14103
14104 /* ignore notifications sent after #OnSessionEnd() is called */
14105 if (!directControl)
14106 return S_OK;
14107
14108 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14109}
14110
14111/**
14112 * @note Locks this object for reading.
14113 */
14114HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14115{
14116 LogFlowThisFunc(("\n"));
14117
14118 AutoCaller autoCaller(this);
14119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14120
14121 ComPtr<IInternalSessionControl> directControl;
14122 {
14123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14124 if (mData->mSession.mLockType == LockType_VM)
14125 directControl = mData->mSession.mDirectControl;
14126 }
14127
14128 /* ignore notifications sent after #OnSessionEnd() is called */
14129 if (!directControl)
14130 return S_OK;
14131
14132 return directControl->OnVRDEServerChange(aRestart);
14133}
14134
14135/**
14136 * @note Locks this object for reading.
14137 */
14138HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14139{
14140 LogFlowThisFunc(("\n"));
14141
14142 AutoCaller autoCaller(this);
14143 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14144
14145 ComPtr<IInternalSessionControl> directControl;
14146 {
14147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14148 if (mData->mSession.mLockType == LockType_VM)
14149 directControl = mData->mSession.mDirectControl;
14150 }
14151
14152 /* ignore notifications sent after #OnSessionEnd() is called */
14153 if (!directControl)
14154 return S_OK;
14155
14156 return directControl->OnRecordingChange(aEnable);
14157}
14158
14159/**
14160 * @note Locks this object for reading.
14161 */
14162HRESULT SessionMachine::i_onUSBControllerChange()
14163{
14164 LogFlowThisFunc(("\n"));
14165
14166 AutoCaller autoCaller(this);
14167 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14168
14169 ComPtr<IInternalSessionControl> directControl;
14170 {
14171 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14172 if (mData->mSession.mLockType == LockType_VM)
14173 directControl = mData->mSession.mDirectControl;
14174 }
14175
14176 /* ignore notifications sent after #OnSessionEnd() is called */
14177 if (!directControl)
14178 return S_OK;
14179
14180 return directControl->OnUSBControllerChange();
14181}
14182
14183/**
14184 * @note Locks this object for reading.
14185 */
14186HRESULT SessionMachine::i_onSharedFolderChange()
14187{
14188 LogFlowThisFunc(("\n"));
14189
14190 AutoCaller autoCaller(this);
14191 AssertComRCReturnRC(autoCaller.rc());
14192
14193 ComPtr<IInternalSessionControl> directControl;
14194 {
14195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14196 if (mData->mSession.mLockType == LockType_VM)
14197 directControl = mData->mSession.mDirectControl;
14198 }
14199
14200 /* ignore notifications sent after #OnSessionEnd() is called */
14201 if (!directControl)
14202 return S_OK;
14203
14204 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14205}
14206
14207/**
14208 * @note Locks this object for reading.
14209 */
14210HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14211{
14212 LogFlowThisFunc(("\n"));
14213
14214 AutoCaller autoCaller(this);
14215 AssertComRCReturnRC(autoCaller.rc());
14216
14217 ComPtr<IInternalSessionControl> directControl;
14218 {
14219 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14220 if (mData->mSession.mLockType == LockType_VM)
14221 directControl = mData->mSession.mDirectControl;
14222 }
14223
14224 /* ignore notifications sent after #OnSessionEnd() is called */
14225 if (!directControl)
14226 return S_OK;
14227
14228 return directControl->OnClipboardModeChange(aClipboardMode);
14229}
14230
14231/**
14232 * @note Locks this object for reading.
14233 */
14234HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14235{
14236 LogFlowThisFunc(("\n"));
14237
14238 AutoCaller autoCaller(this);
14239 AssertComRCReturnRC(autoCaller.rc());
14240
14241 ComPtr<IInternalSessionControl> directControl;
14242 {
14243 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14244 if (mData->mSession.mLockType == LockType_VM)
14245 directControl = mData->mSession.mDirectControl;
14246 }
14247
14248 /* ignore notifications sent after #OnSessionEnd() is called */
14249 if (!directControl)
14250 return S_OK;
14251
14252 return directControl->OnDnDModeChange(aDnDMode);
14253}
14254
14255/**
14256 * @note Locks this object for reading.
14257 */
14258HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14259{
14260 LogFlowThisFunc(("\n"));
14261
14262 AutoCaller autoCaller(this);
14263 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14264
14265 ComPtr<IInternalSessionControl> directControl;
14266 {
14267 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14268 if (mData->mSession.mLockType == LockType_VM)
14269 directControl = mData->mSession.mDirectControl;
14270 }
14271
14272 /* ignore notifications sent after #OnSessionEnd() is called */
14273 if (!directControl)
14274 return S_OK;
14275
14276 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14277}
14278
14279/**
14280 * @note Locks this object for reading.
14281 */
14282HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14283{
14284 LogFlowThisFunc(("\n"));
14285
14286 AutoCaller autoCaller(this);
14287 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14288
14289 ComPtr<IInternalSessionControl> directControl;
14290 {
14291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14292 if (mData->mSession.mLockType == LockType_VM)
14293 directControl = mData->mSession.mDirectControl;
14294 }
14295
14296 /* ignore notifications sent after #OnSessionEnd() is called */
14297 if (!directControl)
14298 return S_OK;
14299
14300 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14301}
14302
14303/**
14304 * Returns @c true if this machine's USB controller reports it has a matching
14305 * filter for the given USB device and @c false otherwise.
14306 *
14307 * @note locks this object for reading.
14308 */
14309bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14310{
14311 AutoCaller autoCaller(this);
14312 /* silently return if not ready -- this method may be called after the
14313 * direct machine session has been called */
14314 if (!autoCaller.isOk())
14315 return false;
14316
14317#ifdef VBOX_WITH_USB
14318 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14319
14320 switch (mData->mMachineState)
14321 {
14322 case MachineState_Starting:
14323 case MachineState_Restoring:
14324 case MachineState_TeleportingIn:
14325 case MachineState_Paused:
14326 case MachineState_Running:
14327 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14328 * elsewhere... */
14329 alock.release();
14330 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14331 default: break;
14332 }
14333#else
14334 NOREF(aDevice);
14335 NOREF(aMaskedIfs);
14336#endif
14337 return false;
14338}
14339
14340/**
14341 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14342 */
14343HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14344 IVirtualBoxErrorInfo *aError,
14345 ULONG aMaskedIfs,
14346 const com::Utf8Str &aCaptureFilename)
14347{
14348 LogFlowThisFunc(("\n"));
14349
14350 AutoCaller autoCaller(this);
14351
14352 /* This notification may happen after the machine object has been
14353 * uninitialized (the session was closed), so don't assert. */
14354 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14355
14356 ComPtr<IInternalSessionControl> directControl;
14357 {
14358 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14359 if (mData->mSession.mLockType == LockType_VM)
14360 directControl = mData->mSession.mDirectControl;
14361 }
14362
14363 /* fail on notifications sent after #OnSessionEnd() is called, it is
14364 * expected by the caller */
14365 if (!directControl)
14366 return E_FAIL;
14367
14368 /* No locks should be held at this point. */
14369 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14370 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14371
14372 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14373}
14374
14375/**
14376 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14377 */
14378HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14379 IVirtualBoxErrorInfo *aError)
14380{
14381 LogFlowThisFunc(("\n"));
14382
14383 AutoCaller autoCaller(this);
14384
14385 /* This notification may happen after the machine object has been
14386 * uninitialized (the session was closed), so don't assert. */
14387 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14388
14389 ComPtr<IInternalSessionControl> directControl;
14390 {
14391 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14392 if (mData->mSession.mLockType == LockType_VM)
14393 directControl = mData->mSession.mDirectControl;
14394 }
14395
14396 /* fail on notifications sent after #OnSessionEnd() is called, it is
14397 * expected by the caller */
14398 if (!directControl)
14399 return E_FAIL;
14400
14401 /* No locks should be held at this point. */
14402 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14403 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14404
14405 return directControl->OnUSBDeviceDetach(aId, aError);
14406}
14407
14408// protected methods
14409/////////////////////////////////////////////////////////////////////////////
14410
14411/**
14412 * Deletes the given file if it is no longer in use by either the current machine state
14413 * (if the machine is "saved") or any of the machine's snapshots.
14414 *
14415 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14416 * but is different for each SnapshotMachine. When calling this, the order of calling this
14417 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14418 * is therefore critical. I know, it's all rather messy.
14419 *
14420 * @param strStateFile
14421 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14422 * the test for whether the saved state file is in use.
14423 */
14424void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14425 Snapshot *pSnapshotToIgnore)
14426{
14427 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14428 if ( (strStateFile.isNotEmpty())
14429 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14430 )
14431 // ... and it must also not be shared with other snapshots
14432 if ( !mData->mFirstSnapshot
14433 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14434 // this checks the SnapshotMachine's state file paths
14435 )
14436 RTFileDelete(strStateFile.c_str());
14437}
14438
14439/**
14440 * Locks the attached media.
14441 *
14442 * All attached hard disks are locked for writing and DVD/floppy are locked for
14443 * reading. Parents of attached hard disks (if any) are locked for reading.
14444 *
14445 * This method also performs accessibility check of all media it locks: if some
14446 * media is inaccessible, the method will return a failure and a bunch of
14447 * extended error info objects per each inaccessible medium.
14448 *
14449 * Note that this method is atomic: if it returns a success, all media are
14450 * locked as described above; on failure no media is locked at all (all
14451 * succeeded individual locks will be undone).
14452 *
14453 * The caller is responsible for doing the necessary state sanity checks.
14454 *
14455 * The locks made by this method must be undone by calling #unlockMedia() when
14456 * no more needed.
14457 */
14458HRESULT SessionMachine::i_lockMedia()
14459{
14460 AutoCaller autoCaller(this);
14461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14462
14463 AutoMultiWriteLock2 alock(this->lockHandle(),
14464 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14465
14466 /* bail out if trying to lock things with already set up locking */
14467 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14468
14469 MultiResult mrc(S_OK);
14470
14471 /* Collect locking information for all medium objects attached to the VM. */
14472 for (MediumAttachmentList::const_iterator
14473 it = mMediumAttachments->begin();
14474 it != mMediumAttachments->end();
14475 ++it)
14476 {
14477 MediumAttachment *pAtt = *it;
14478 DeviceType_T devType = pAtt->i_getType();
14479 Medium *pMedium = pAtt->i_getMedium();
14480
14481 MediumLockList *pMediumLockList(new MediumLockList());
14482 // There can be attachments without a medium (floppy/dvd), and thus
14483 // it's impossible to create a medium lock list. It still makes sense
14484 // to have the empty medium lock list in the map in case a medium is
14485 // attached later.
14486 if (pMedium != NULL)
14487 {
14488 MediumType_T mediumType = pMedium->i_getType();
14489 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14490 || mediumType == MediumType_Shareable;
14491 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14492
14493 alock.release();
14494 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14495 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14496 false /* fMediumLockWriteAll */,
14497 NULL,
14498 *pMediumLockList);
14499 alock.acquire();
14500 if (FAILED(mrc))
14501 {
14502 delete pMediumLockList;
14503 mData->mSession.mLockedMedia.Clear();
14504 break;
14505 }
14506 }
14507
14508 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14509 if (FAILED(rc))
14510 {
14511 mData->mSession.mLockedMedia.Clear();
14512 mrc = setError(rc,
14513 tr("Collecting locking information for all attached media failed"));
14514 break;
14515 }
14516 }
14517
14518 if (SUCCEEDED(mrc))
14519 {
14520 /* Now lock all media. If this fails, nothing is locked. */
14521 alock.release();
14522 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14523 alock.acquire();
14524 if (FAILED(rc))
14525 {
14526 mrc = setError(rc,
14527 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14528 }
14529 }
14530
14531 return mrc;
14532}
14533
14534/**
14535 * Undoes the locks made by by #lockMedia().
14536 */
14537HRESULT SessionMachine::i_unlockMedia()
14538{
14539 AutoCaller autoCaller(this);
14540 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14541
14542 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14543
14544 /* we may be holding important error info on the current thread;
14545 * preserve it */
14546 ErrorInfoKeeper eik;
14547
14548 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14549 AssertComRC(rc);
14550 return rc;
14551}
14552
14553/**
14554 * Helper to change the machine state (reimplementation).
14555 *
14556 * @note Locks this object for writing.
14557 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14558 * it can cause crashes in random places due to unexpectedly committing
14559 * the current settings. The caller is responsible for that. The call
14560 * to saveStateSettings is fine, because this method does not commit.
14561 */
14562HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14563{
14564 LogFlowThisFuncEnter();
14565 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14566
14567 AutoCaller autoCaller(this);
14568 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14569
14570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14571
14572 MachineState_T oldMachineState = mData->mMachineState;
14573
14574 AssertMsgReturn(oldMachineState != aMachineState,
14575 ("oldMachineState=%s, aMachineState=%s\n",
14576 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14577 E_FAIL);
14578
14579 HRESULT rc = S_OK;
14580
14581 int stsFlags = 0;
14582 bool deleteSavedState = false;
14583
14584 /* detect some state transitions */
14585
14586 if ( ( oldMachineState == MachineState_Saved
14587 && aMachineState == MachineState_Restoring)
14588 || ( ( oldMachineState == MachineState_PoweredOff
14589 || oldMachineState == MachineState_Teleported
14590 || oldMachineState == MachineState_Aborted
14591 )
14592 && ( aMachineState == MachineState_TeleportingIn
14593 || aMachineState == MachineState_Starting
14594 )
14595 )
14596 )
14597 {
14598 /* The EMT thread is about to start */
14599
14600 /* Nothing to do here for now... */
14601
14602 /// @todo NEWMEDIA don't let mDVDDrive and other children
14603 /// change anything when in the Starting/Restoring state
14604 }
14605 else if ( ( oldMachineState == MachineState_Running
14606 || oldMachineState == MachineState_Paused
14607 || oldMachineState == MachineState_Teleporting
14608 || oldMachineState == MachineState_OnlineSnapshotting
14609 || oldMachineState == MachineState_LiveSnapshotting
14610 || oldMachineState == MachineState_Stuck
14611 || oldMachineState == MachineState_Starting
14612 || oldMachineState == MachineState_Stopping
14613 || oldMachineState == MachineState_Saving
14614 || oldMachineState == MachineState_Restoring
14615 || oldMachineState == MachineState_TeleportingPausedVM
14616 || oldMachineState == MachineState_TeleportingIn
14617 )
14618 && ( aMachineState == MachineState_PoweredOff
14619 || aMachineState == MachineState_Saved
14620 || aMachineState == MachineState_Teleported
14621 || aMachineState == MachineState_Aborted
14622 )
14623 )
14624 {
14625 /* The EMT thread has just stopped, unlock attached media. Note that as
14626 * opposed to locking that is done from Console, we do unlocking here
14627 * because the VM process may have aborted before having a chance to
14628 * properly unlock all media it locked. */
14629
14630 unlockMedia();
14631 }
14632
14633 if (oldMachineState == MachineState_Restoring)
14634 {
14635 if (aMachineState != MachineState_Saved)
14636 {
14637 /*
14638 * delete the saved state file once the machine has finished
14639 * restoring from it (note that Console sets the state from
14640 * Restoring to Saved if the VM couldn't restore successfully,
14641 * to give the user an ability to fix an error and retry --
14642 * we keep the saved state file in this case)
14643 */
14644 deleteSavedState = true;
14645 }
14646 }
14647 else if ( oldMachineState == MachineState_Saved
14648 && ( aMachineState == MachineState_PoweredOff
14649 || aMachineState == MachineState_Aborted
14650 || aMachineState == MachineState_Teleported
14651 )
14652 )
14653 {
14654 /*
14655 * delete the saved state after SessionMachine::ForgetSavedState() is called
14656 * or if the VM process (owning a direct VM session) crashed while the
14657 * VM was Saved
14658 */
14659
14660 /// @todo (dmik)
14661 // Not sure that deleting the saved state file just because of the
14662 // client death before it attempted to restore the VM is a good
14663 // thing. But when it crashes we need to go to the Aborted state
14664 // which cannot have the saved state file associated... The only
14665 // way to fix this is to make the Aborted condition not a VM state
14666 // but a bool flag: i.e., when a crash occurs, set it to true and
14667 // change the state to PoweredOff or Saved depending on the
14668 // saved state presence.
14669
14670 deleteSavedState = true;
14671 mData->mCurrentStateModified = TRUE;
14672 stsFlags |= SaveSTS_CurStateModified;
14673 }
14674
14675 if ( aMachineState == MachineState_Starting
14676 || aMachineState == MachineState_Restoring
14677 || aMachineState == MachineState_TeleportingIn
14678 )
14679 {
14680 /* set the current state modified flag to indicate that the current
14681 * state is no more identical to the state in the
14682 * current snapshot */
14683 if (!mData->mCurrentSnapshot.isNull())
14684 {
14685 mData->mCurrentStateModified = TRUE;
14686 stsFlags |= SaveSTS_CurStateModified;
14687 }
14688 }
14689
14690 if (deleteSavedState)
14691 {
14692 if (mRemoveSavedState)
14693 {
14694 Assert(!mSSData->strStateFilePath.isEmpty());
14695
14696 // it is safe to delete the saved state file if ...
14697 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14698 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14699 // ... none of the snapshots share the saved state file
14700 )
14701 RTFileDelete(mSSData->strStateFilePath.c_str());
14702 }
14703
14704 mSSData->strStateFilePath.setNull();
14705 stsFlags |= SaveSTS_StateFilePath;
14706 }
14707
14708 /* redirect to the underlying peer machine */
14709 mPeer->i_setMachineState(aMachineState);
14710
14711 if ( oldMachineState != MachineState_RestoringSnapshot
14712 && ( aMachineState == MachineState_PoweredOff
14713 || aMachineState == MachineState_Teleported
14714 || aMachineState == MachineState_Aborted
14715 || aMachineState == MachineState_Saved))
14716 {
14717 /* the machine has stopped execution
14718 * (or the saved state file was adopted) */
14719 stsFlags |= SaveSTS_StateTimeStamp;
14720 }
14721
14722 if ( ( oldMachineState == MachineState_PoweredOff
14723 || oldMachineState == MachineState_Aborted
14724 || oldMachineState == MachineState_Teleported
14725 )
14726 && aMachineState == MachineState_Saved)
14727 {
14728 /* the saved state file was adopted */
14729 Assert(!mSSData->strStateFilePath.isEmpty());
14730 stsFlags |= SaveSTS_StateFilePath;
14731 }
14732
14733#ifdef VBOX_WITH_GUEST_PROPS
14734 if ( aMachineState == MachineState_PoweredOff
14735 || aMachineState == MachineState_Aborted
14736 || aMachineState == MachineState_Teleported)
14737 {
14738 /* Make sure any transient guest properties get removed from the
14739 * property store on shutdown. */
14740 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14741
14742 /* remove it from the settings representation */
14743 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14744 for (settings::GuestPropertiesList::iterator
14745 it = llGuestProperties.begin();
14746 it != llGuestProperties.end();
14747 /*nothing*/)
14748 {
14749 const settings::GuestProperty &prop = *it;
14750 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14751 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14752 {
14753 it = llGuestProperties.erase(it);
14754 fNeedsSaving = true;
14755 }
14756 else
14757 {
14758 ++it;
14759 }
14760 }
14761
14762 /* Additionally remove it from the HWData representation. Required to
14763 * keep everything in sync, as this is what the API keeps using. */
14764 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14765 for (HWData::GuestPropertyMap::iterator
14766 it = llHWGuestProperties.begin();
14767 it != llHWGuestProperties.end();
14768 /*nothing*/)
14769 {
14770 uint32_t fFlags = it->second.mFlags;
14771 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14772 {
14773 /* iterator where we need to continue after the erase call
14774 * (C++03 is a fact still, and it doesn't return the iterator
14775 * which would allow continuing) */
14776 HWData::GuestPropertyMap::iterator it2 = it;
14777 ++it2;
14778 llHWGuestProperties.erase(it);
14779 it = it2;
14780 fNeedsSaving = true;
14781 }
14782 else
14783 {
14784 ++it;
14785 }
14786 }
14787
14788 if (fNeedsSaving)
14789 {
14790 mData->mCurrentStateModified = TRUE;
14791 stsFlags |= SaveSTS_CurStateModified;
14792 }
14793 }
14794#endif /* VBOX_WITH_GUEST_PROPS */
14795
14796 rc = i_saveStateSettings(stsFlags);
14797
14798 if ( ( oldMachineState != MachineState_PoweredOff
14799 && oldMachineState != MachineState_Aborted
14800 && oldMachineState != MachineState_Teleported
14801 )
14802 && ( aMachineState == MachineState_PoweredOff
14803 || aMachineState == MachineState_Aborted
14804 || aMachineState == MachineState_Teleported
14805 )
14806 )
14807 {
14808 /* we've been shut down for any reason */
14809 /* no special action so far */
14810 }
14811
14812 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14813 LogFlowThisFuncLeave();
14814 return rc;
14815}
14816
14817/**
14818 * Sends the current machine state value to the VM process.
14819 *
14820 * @note Locks this object for reading, then calls a client process.
14821 */
14822HRESULT SessionMachine::i_updateMachineStateOnClient()
14823{
14824 AutoCaller autoCaller(this);
14825 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14826
14827 ComPtr<IInternalSessionControl> directControl;
14828 {
14829 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14830 AssertReturn(!!mData, E_FAIL);
14831 if (mData->mSession.mLockType == LockType_VM)
14832 directControl = mData->mSession.mDirectControl;
14833
14834 /* directControl may be already set to NULL here in #OnSessionEnd()
14835 * called too early by the direct session process while there is still
14836 * some operation (like deleting the snapshot) in progress. The client
14837 * process in this case is waiting inside Session::close() for the
14838 * "end session" process object to complete, while #uninit() called by
14839 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14840 * operation to complete. For now, we accept this inconsistent behavior
14841 * and simply do nothing here. */
14842
14843 if (mData->mSession.mState == SessionState_Unlocking)
14844 return S_OK;
14845 }
14846
14847 /* ignore notifications sent after #OnSessionEnd() is called */
14848 if (!directControl)
14849 return S_OK;
14850
14851 return directControl->UpdateMachineState(mData->mMachineState);
14852}
14853
14854
14855/*static*/
14856HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14857{
14858 va_list args;
14859 va_start(args, pcszMsg);
14860 HRESULT rc = setErrorInternal(aResultCode,
14861 getStaticClassIID(),
14862 getStaticComponentName(),
14863 Utf8Str(pcszMsg, args),
14864 false /* aWarning */,
14865 true /* aLogIt */);
14866 va_end(args);
14867 return rc;
14868}
14869
14870
14871HRESULT Machine::updateState(MachineState_T aState)
14872{
14873 NOREF(aState);
14874 ReturnComNotImplemented();
14875}
14876
14877HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14878{
14879 NOREF(aProgress);
14880 ReturnComNotImplemented();
14881}
14882
14883HRESULT Machine::endPowerUp(LONG aResult)
14884{
14885 NOREF(aResult);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14890{
14891 NOREF(aProgress);
14892 ReturnComNotImplemented();
14893}
14894
14895HRESULT Machine::endPoweringDown(LONG aResult,
14896 const com::Utf8Str &aErrMsg)
14897{
14898 NOREF(aResult);
14899 NOREF(aErrMsg);
14900 ReturnComNotImplemented();
14901}
14902
14903HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14904 BOOL *aMatched,
14905 ULONG *aMaskedInterfaces)
14906{
14907 NOREF(aDevice);
14908 NOREF(aMatched);
14909 NOREF(aMaskedInterfaces);
14910 ReturnComNotImplemented();
14911
14912}
14913
14914HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14915{
14916 NOREF(aId); NOREF(aCaptureFilename);
14917 ReturnComNotImplemented();
14918}
14919
14920HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14921 BOOL aDone)
14922{
14923 NOREF(aId);
14924 NOREF(aDone);
14925 ReturnComNotImplemented();
14926}
14927
14928HRESULT Machine::autoCaptureUSBDevices()
14929{
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14934{
14935 NOREF(aDone);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14940 ComPtr<IProgress> &aProgress)
14941{
14942 NOREF(aSession);
14943 NOREF(aProgress);
14944 ReturnComNotImplemented();
14945}
14946
14947HRESULT Machine::finishOnlineMergeMedium()
14948{
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14953{
14954 RT_NOREF(aParms, aID);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14959{
14960 RT_NOREF(aID);
14961 ReturnComNotImplemented();
14962}
14963
14964HRESULT Machine::clipboardAreaAttach(ULONG aID)
14965{
14966 RT_NOREF(aID);
14967 ReturnComNotImplemented();
14968}
14969HRESULT Machine::clipboardAreaDetach(ULONG aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14976{
14977 RT_NOREF(aID);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14982{
14983 RT_NOREF(aID, aRefCount);
14984 ReturnComNotImplemented();
14985}
14986
14987HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14988 std::vector<com::Utf8Str> &aValues,
14989 std::vector<LONG64> &aTimestamps,
14990 std::vector<com::Utf8Str> &aFlags)
14991{
14992 NOREF(aNames);
14993 NOREF(aValues);
14994 NOREF(aTimestamps);
14995 NOREF(aFlags);
14996 ReturnComNotImplemented();
14997}
14998
14999HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15000 const com::Utf8Str &aValue,
15001 LONG64 aTimestamp,
15002 const com::Utf8Str &aFlags)
15003{
15004 NOREF(aName);
15005 NOREF(aValue);
15006 NOREF(aTimestamp);
15007 NOREF(aFlags);
15008 ReturnComNotImplemented();
15009}
15010
15011HRESULT Machine::lockMedia()
15012{
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::unlockMedia()
15017{
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15022 ComPtr<IMediumAttachment> &aNewAttachment)
15023{
15024 NOREF(aAttachment);
15025 NOREF(aNewAttachment);
15026 ReturnComNotImplemented();
15027}
15028
15029HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15030 ULONG aCpuUser,
15031 ULONG aCpuKernel,
15032 ULONG aCpuIdle,
15033 ULONG aMemTotal,
15034 ULONG aMemFree,
15035 ULONG aMemBalloon,
15036 ULONG aMemShared,
15037 ULONG aMemCache,
15038 ULONG aPagedTotal,
15039 ULONG aMemAllocTotal,
15040 ULONG aMemFreeTotal,
15041 ULONG aMemBalloonTotal,
15042 ULONG aMemSharedTotal,
15043 ULONG aVmNetRx,
15044 ULONG aVmNetTx)
15045{
15046 NOREF(aValidStats);
15047 NOREF(aCpuUser);
15048 NOREF(aCpuKernel);
15049 NOREF(aCpuIdle);
15050 NOREF(aMemTotal);
15051 NOREF(aMemFree);
15052 NOREF(aMemBalloon);
15053 NOREF(aMemShared);
15054 NOREF(aMemCache);
15055 NOREF(aPagedTotal);
15056 NOREF(aMemAllocTotal);
15057 NOREF(aMemFreeTotal);
15058 NOREF(aMemBalloonTotal);
15059 NOREF(aMemSharedTotal);
15060 NOREF(aVmNetRx);
15061 NOREF(aVmNetTx);
15062 ReturnComNotImplemented();
15063}
15064
15065HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15066 com::Utf8Str &aResult)
15067{
15068 NOREF(aAuthParams);
15069 NOREF(aResult);
15070 ReturnComNotImplemented();
15071}
15072
15073com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15074{
15075 com::Utf8Str strControllerName = "Unknown";
15076 switch (aBusType)
15077 {
15078 case StorageBus_IDE:
15079 {
15080 strControllerName = "IDE";
15081 break;
15082 }
15083 case StorageBus_SATA:
15084 {
15085 strControllerName = "SATA";
15086 break;
15087 }
15088 case StorageBus_SCSI:
15089 {
15090 strControllerName = "SCSI";
15091 break;
15092 }
15093 case StorageBus_Floppy:
15094 {
15095 strControllerName = "Floppy";
15096 break;
15097 }
15098 case StorageBus_SAS:
15099 {
15100 strControllerName = "SAS";
15101 break;
15102 }
15103 case StorageBus_USB:
15104 {
15105 strControllerName = "USB";
15106 break;
15107 }
15108 default:
15109 break;
15110 }
15111 return strControllerName;
15112}
15113
15114HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15115{
15116 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15117
15118 AutoCaller autoCaller(this);
15119 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15120
15121 HRESULT rc = S_OK;
15122
15123 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15124 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15125 rc = getUSBDeviceFilters(usbDeviceFilters);
15126 if (FAILED(rc)) return rc;
15127
15128 NOREF(aFlags);
15129 com::Utf8Str osTypeId;
15130 ComObjPtr<GuestOSType> osType = NULL;
15131
15132 /* Get the guest os type as a string from the VB. */
15133 rc = getOSTypeId(osTypeId);
15134 if (FAILED(rc)) return rc;
15135
15136 /* Get the os type obj that coresponds, can be used to get
15137 * the defaults for this guest OS. */
15138 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15139 if (FAILED(rc)) return rc;
15140
15141 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15142
15143 /* Let the OS type select 64-bit ness. */
15144 mHWData->mLongMode = osType->i_is64Bit()
15145 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15146
15147 /* Let the OS type enable the X2APIC */
15148 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15149
15150 /* This one covers IOAPICEnabled. */
15151 mBIOSSettings->i_applyDefaults(osType);
15152
15153 /* Initialize default record settings. */
15154 mRecordingSettings->i_applyDefaults();
15155
15156 /* Initialize default BIOS settings here */
15157 /* Hardware virtualization must be ON by default */
15158 mHWData->mAPIC = true;
15159 mHWData->mHWVirtExEnabled = true;
15160
15161 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15162 if (FAILED(rc)) return rc;
15163
15164 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15165 if (FAILED(rc)) return rc;
15166
15167 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15168 if (FAILED(rc)) return rc;
15169
15170 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15171 if (FAILED(rc)) return rc;
15172
15173 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15174 if (FAILED(rc)) return rc;
15175
15176 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15177 if (FAILED(rc)) return rc;
15178
15179 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15180 if (FAILED(rc)) return rc;
15181
15182 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15183 if (FAILED(rc)) return rc;
15184
15185 BOOL mRTCUseUTC;
15186 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15187 if (FAILED(rc)) return rc;
15188
15189 setRTCUseUTC(mRTCUseUTC);
15190 if (FAILED(rc)) return rc;
15191
15192 /* the setter does more than just the assignment, so use it */
15193 ChipsetType_T enmChipsetType;
15194 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15195 if (FAILED(rc)) return rc;
15196
15197 rc = COMSETTER(ChipsetType)(enmChipsetType);
15198 if (FAILED(rc)) return rc;
15199
15200 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15201 if (FAILED(rc)) return rc;
15202
15203 /* Apply network adapters defaults */
15204 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15205 mNetworkAdapters[slot]->i_applyDefaults(osType);
15206
15207 /* Apply serial port defaults */
15208 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15209 mSerialPorts[slot]->i_applyDefaults(osType);
15210
15211 /* Apply parallel port defaults - not OS dependent*/
15212 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15213 mParallelPorts[slot]->i_applyDefaults();
15214
15215 /* Audio stuff. */
15216 AudioControllerType_T audioController;
15217 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15218 if (FAILED(rc)) return rc;
15219
15220 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15221 if (FAILED(rc)) return rc;
15222
15223 AudioCodecType_T audioCodec;
15224 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15225 if (FAILED(rc)) return rc;
15226
15227 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15228 if (FAILED(rc)) return rc;
15229
15230 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15231 if (FAILED(rc)) return rc;
15232
15233 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15234 if (FAILED(rc)) return rc;
15235
15236 /* Storage Controllers */
15237 StorageControllerType_T hdStorageControllerType;
15238 StorageBus_T hdStorageBusType;
15239 StorageControllerType_T dvdStorageControllerType;
15240 StorageBus_T dvdStorageBusType;
15241 BOOL recommendedFloppy;
15242 ComPtr<IStorageController> floppyController;
15243 ComPtr<IStorageController> hdController;
15244 ComPtr<IStorageController> dvdController;
15245 Utf8Str strFloppyName, strDVDName, strHDName;
15246
15247 /* GUI auto generates controller names using bus type. Do the same*/
15248 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15249
15250 /* Floppy recommended? add one. */
15251 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15252 if (FAILED(rc)) return rc;
15253 if (recommendedFloppy)
15254 {
15255 rc = addStorageController(strFloppyName,
15256 StorageBus_Floppy,
15257 floppyController);
15258 if (FAILED(rc)) return rc;
15259 }
15260
15261 /* Setup one DVD storage controller. */
15262 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15263 if (FAILED(rc)) return rc;
15264
15265 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15266 if (FAILED(rc)) return rc;
15267
15268 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15269
15270 rc = addStorageController(strDVDName,
15271 dvdStorageBusType,
15272 dvdController);
15273 if (FAILED(rc)) return rc;
15274
15275 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15276 if (FAILED(rc)) return rc;
15277
15278 /* Setup one HDD storage controller. */
15279 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15280 if (FAILED(rc)) return rc;
15281
15282 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15283 if (FAILED(rc)) return rc;
15284
15285 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15286
15287 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15288 {
15289 rc = addStorageController(strHDName,
15290 hdStorageBusType,
15291 hdController);
15292 if (FAILED(rc)) return rc;
15293
15294 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15295 if (FAILED(rc)) return rc;
15296 }
15297 else
15298 {
15299 /* The HD controller is the same as DVD: */
15300 hdController = dvdController;
15301 }
15302
15303 /* Limit the AHCI port count if it's used because windows has trouble with
15304 * too many ports and other guest (OS X in particular) may take extra long
15305 * boot: */
15306
15307 // pParent = static_cast<Medium*>(aP)
15308 IStorageController *temp = hdController;
15309 ComObjPtr<StorageController> storageController;
15310 storageController = static_cast<StorageController *>(temp);
15311
15312 // tempHDController = aHDController;
15313 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15314 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15315 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15316 storageController->COMSETTER(PortCount)(1);
15317
15318 /* USB stuff */
15319
15320 bool ohciEnabled = false;
15321
15322 ComPtr<IUSBController> usbController;
15323 BOOL recommendedUSB3;
15324 BOOL recommendedUSB;
15325 BOOL usbProxyAvailable;
15326
15327 getUSBProxyAvailable(&usbProxyAvailable);
15328 if (FAILED(rc)) return rc;
15329
15330 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15331 if (FAILED(rc)) return rc;
15332 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15333 if (FAILED(rc)) return rc;
15334
15335 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15336 {
15337#ifdef VBOX_WITH_EXTPACK
15338 /* USB 3.0 is only available if the proper ExtPack is installed. */
15339 ExtPackManager *aManager = mParent->i_getExtPackManager();
15340 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15341 {
15342 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15343 if (FAILED(rc)) return rc;
15344
15345 /* xHci includes OHCI */
15346 ohciEnabled = true;
15347 }
15348#endif
15349 }
15350 if ( !ohciEnabled
15351 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15352 {
15353 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15354 if (FAILED(rc)) return rc;
15355 ohciEnabled = true;
15356
15357#ifdef VBOX_WITH_EXTPACK
15358 /* USB 2.0 is only available if the proper ExtPack is installed.
15359 * Note. Configuring EHCI here and providing messages about
15360 * the missing extpack isn't exactly clean, but it is a
15361 * necessary evil to patch over legacy compatability issues
15362 * introduced by the new distribution model. */
15363 ExtPackManager *manager = mParent->i_getExtPackManager();
15364 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15365 {
15366 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15367 if (FAILED(rc)) return rc;
15368 }
15369#endif
15370 }
15371
15372 /* Set recommended human interface device types: */
15373 BOOL recommendedUSBHID;
15374 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15375 if (FAILED(rc)) return rc;
15376
15377 if (recommendedUSBHID)
15378 {
15379 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15380 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15381 if (!ohciEnabled && !usbDeviceFilters.isNull())
15382 {
15383 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15384 if (FAILED(rc)) return rc;
15385 }
15386 }
15387
15388 BOOL recommendedUSBTablet;
15389 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15390 if (FAILED(rc)) return rc;
15391
15392 if (recommendedUSBTablet)
15393 {
15394 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15395 if (!ohciEnabled && !usbDeviceFilters.isNull())
15396 {
15397 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15398 if (FAILED(rc)) return rc;
15399 }
15400 }
15401 return S_OK;
15402}
15403
15404/* This isn't handled entirely by the wrapper generator yet. */
15405#ifdef VBOX_WITH_XPCOM
15406NS_DECL_CLASSINFO(SessionMachine)
15407NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15408
15409NS_DECL_CLASSINFO(SnapshotMachine)
15410NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15411#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