VirtualBox

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

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

Main/Machine+BIOSSettings: bare bones NVRAM logic, many parts missing (no snapshot handling, no move VM handling, no remove VM handling).
Main/Settings: adaptions to store NVRAM config in the settings file
Frontends/VBoxManage: support enabling and showing state

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.8 KB
Line 
1/* $Id: MachineImpl.cpp 81087 2019-09-30 18:55:28Z 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.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#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 /** @todo quick workaround for hang with Win10 guest when 2d accel
1894 * is enabled when non-VBoxVGA graphics is configured. */
1895 if (mHWData->mGraphicsControllerType == GraphicsControllerType_VBoxVGA)
1896 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1897 else
1898 *aAccelerate2DVideoEnabled = FALSE;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 HRESULT rc = i_checkStateDependency(MutableStateDep);
1908 if (FAILED(rc)) return rc;
1909
1910 /** @todo check validity! */
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aMonitorCount = mHWData->mMonitorCount;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1928{
1929 /* make sure monitor count is a sensible number */
1930 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1931 return setError(E_INVALIDARG,
1932 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1933 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1934
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 HRESULT rc = i_checkStateDependency(MutableStateDep);
1938 if (FAILED(rc)) return rc;
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mMonitorCount = aMonitorCount;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1948{
1949 /* mBIOSSettings is constant during life time, no need to lock */
1950 aBIOSSettings = mBIOSSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1956{
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 aRecordingSettings = mRecordingSettings;
1960
1961 return S_OK;
1962}
1963
1964HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1965{
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 switch (aProperty)
1969 {
1970 case CPUPropertyType_PAE:
1971 *aValue = mHWData->mPAEEnabled;
1972 break;
1973
1974 case CPUPropertyType_LongMode:
1975 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1976 *aValue = TRUE;
1977 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1978 *aValue = FALSE;
1979#if HC_ARCH_BITS == 64
1980 else
1981 *aValue = TRUE;
1982#else
1983 else
1984 {
1985 *aValue = FALSE;
1986
1987 ComObjPtr<GuestOSType> pGuestOSType;
1988 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1989 pGuestOSType);
1990 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1991 {
1992 if (pGuestOSType->i_is64Bit())
1993 {
1994 ComObjPtr<Host> pHost = mParent->i_host();
1995 alock.release();
1996
1997 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1998 if (FAILED(hrc2))
1999 *aValue = FALSE;
2000 }
2001 }
2002 }
2003#endif
2004 break;
2005
2006 case CPUPropertyType_TripleFaultReset:
2007 *aValue = mHWData->mTripleFaultReset;
2008 break;
2009
2010 case CPUPropertyType_APIC:
2011 *aValue = mHWData->mAPIC;
2012 break;
2013
2014 case CPUPropertyType_X2APIC:
2015 *aValue = mHWData->mX2APIC;
2016 break;
2017
2018 case CPUPropertyType_IBPBOnVMExit:
2019 *aValue = mHWData->mIBPBOnVMExit;
2020 break;
2021
2022 case CPUPropertyType_IBPBOnVMEntry:
2023 *aValue = mHWData->mIBPBOnVMEntry;
2024 break;
2025
2026 case CPUPropertyType_SpecCtrl:
2027 *aValue = mHWData->mSpecCtrl;
2028 break;
2029
2030 case CPUPropertyType_SpecCtrlByHost:
2031 *aValue = mHWData->mSpecCtrlByHost;
2032 break;
2033
2034 case CPUPropertyType_HWVirt:
2035 *aValue = mHWData->mNestedHWVirt;
2036 break;
2037
2038 case CPUPropertyType_L1DFlushOnEMTScheduling:
2039 *aValue = mHWData->mL1DFlushOnSched;
2040 break;
2041
2042 case CPUPropertyType_L1DFlushOnVMEntry:
2043 *aValue = mHWData->mL1DFlushOnVMEntry;
2044 break;
2045
2046 case CPUPropertyType_MDSClearOnEMTScheduling:
2047 *aValue = mHWData->mMDSClearOnSched;
2048 break;
2049
2050 case CPUPropertyType_MDSClearOnVMEntry:
2051 *aValue = mHWData->mMDSClearOnVMEntry;
2052 break;
2053
2054 default:
2055 return E_INVALIDARG;
2056 }
2057 return S_OK;
2058}
2059
2060HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2061{
2062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 HRESULT rc = i_checkStateDependency(MutableStateDep);
2065 if (FAILED(rc)) return rc;
2066
2067 switch (aProperty)
2068 {
2069 case CPUPropertyType_PAE:
2070 i_setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mPAEEnabled = !!aValue;
2073 break;
2074
2075 case CPUPropertyType_LongMode:
2076 i_setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2079 break;
2080
2081 case CPUPropertyType_TripleFaultReset:
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mTripleFaultReset = !!aValue;
2085 break;
2086
2087 case CPUPropertyType_APIC:
2088 if (mHWData->mX2APIC)
2089 aValue = TRUE;
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mAPIC = !!aValue;
2093 break;
2094
2095 case CPUPropertyType_X2APIC:
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mX2APIC = !!aValue;
2099 if (aValue)
2100 mHWData->mAPIC = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_IBPBOnVMExit:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mIBPBOnVMExit = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_IBPBOnVMEntry:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mIBPBOnVMEntry = !!aValue;
2113 break;
2114
2115 case CPUPropertyType_SpecCtrl:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mSpecCtrl = !!aValue;
2119 break;
2120
2121 case CPUPropertyType_SpecCtrlByHost:
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mSpecCtrlByHost = !!aValue;
2125 break;
2126
2127 case CPUPropertyType_HWVirt:
2128 i_setModified(IsModified_MachineData);
2129 mHWData.backup();
2130 mHWData->mNestedHWVirt = !!aValue;
2131 break;
2132
2133 case CPUPropertyType_L1DFlushOnEMTScheduling:
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mL1DFlushOnSched = !!aValue;
2137 break;
2138
2139 case CPUPropertyType_L1DFlushOnVMEntry:
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mL1DFlushOnVMEntry = !!aValue;
2143 break;
2144
2145 case CPUPropertyType_MDSClearOnEMTScheduling:
2146 i_setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mMDSClearOnSched = !!aValue;
2149 break;
2150
2151 case CPUPropertyType_MDSClearOnVMEntry:
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mMDSClearOnVMEntry = !!aValue;
2155 break;
2156
2157 default:
2158 return E_INVALIDARG;
2159 }
2160 return S_OK;
2161}
2162
2163HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2164 ULONG *aValEcx, ULONG *aValEdx)
2165{
2166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2167 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2168 {
2169 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2170 it != mHWData->mCpuIdLeafList.end();
2171 ++it)
2172 {
2173 if (aOrdinal == 0)
2174 {
2175 const settings::CpuIdLeaf &rLeaf= *it;
2176 *aIdx = rLeaf.idx;
2177 *aSubIdx = rLeaf.idxSub;
2178 *aValEax = rLeaf.uEax;
2179 *aValEbx = rLeaf.uEbx;
2180 *aValEcx = rLeaf.uEcx;
2181 *aValEdx = rLeaf.uEdx;
2182 return S_OK;
2183 }
2184 aOrdinal--;
2185 }
2186 }
2187 return E_INVALIDARG;
2188}
2189
2190HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2191{
2192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 /*
2195 * Search the list.
2196 */
2197 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2198 {
2199 const settings::CpuIdLeaf &rLeaf= *it;
2200 if ( rLeaf.idx == aIdx
2201 && ( aSubIdx == UINT32_MAX
2202 || rLeaf.idxSub == aSubIdx) )
2203 {
2204 *aValEax = rLeaf.uEax;
2205 *aValEbx = rLeaf.uEbx;
2206 *aValEcx = rLeaf.uEcx;
2207 *aValEdx = rLeaf.uEdx;
2208 return S_OK;
2209 }
2210 }
2211
2212 return E_INVALIDARG;
2213}
2214
2215
2216HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2217{
2218 /*
2219 * Validate input before taking locks and checking state.
2220 */
2221 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2222 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2223 if ( aIdx >= UINT32_C(0x20)
2224 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2225 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2226 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2227
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229 HRESULT rc = i_checkStateDependency(MutableStateDep);
2230 if (FAILED(rc)) return rc;
2231
2232 /*
2233 * Impose a maximum number of leaves.
2234 */
2235 if (mHWData->mCpuIdLeafList.size() > 256)
2236 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2237
2238 /*
2239 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2240 */
2241 i_setModified(IsModified_MachineData);
2242 mHWData.backup();
2243
2244 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2245 {
2246 settings::CpuIdLeaf &rLeaf= *it;
2247 if ( rLeaf.idx == aIdx
2248 && ( aSubIdx == UINT32_MAX
2249 || rLeaf.idxSub == aSubIdx) )
2250 it = mHWData->mCpuIdLeafList.erase(it);
2251 else
2252 ++it;
2253 }
2254
2255 settings::CpuIdLeaf NewLeaf;
2256 NewLeaf.idx = aIdx;
2257 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2258 NewLeaf.uEax = aValEax;
2259 NewLeaf.uEbx = aValEbx;
2260 NewLeaf.uEcx = aValEcx;
2261 NewLeaf.uEdx = aValEdx;
2262 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2263 return S_OK;
2264}
2265
2266HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2267{
2268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2269
2270 HRESULT rc = i_checkStateDependency(MutableStateDep);
2271 if (FAILED(rc)) return rc;
2272
2273 /*
2274 * Do the removal.
2275 */
2276 bool fModified = mHWData.isBackedUp();
2277 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2278 {
2279 settings::CpuIdLeaf &rLeaf= *it;
2280 if ( rLeaf.idx == aIdx
2281 && ( aSubIdx == UINT32_MAX
2282 || rLeaf.idxSub == aSubIdx) )
2283 {
2284 if (!fModified)
2285 {
2286 fModified = true;
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 // Start from the beginning, since mHWData.backup() creates
2290 // a new list, causing iterator mixup. This makes sure that
2291 // the settings are not unnecessarily marked as modified,
2292 // at the price of extra list walking.
2293 it = mHWData->mCpuIdLeafList.begin();
2294 }
2295 else
2296 it = mHWData->mCpuIdLeafList.erase(it);
2297 }
2298 else
2299 ++it;
2300 }
2301
2302 return S_OK;
2303}
2304
2305HRESULT Machine::removeAllCPUIDLeaves()
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 if (mHWData->mCpuIdLeafList.size() > 0)
2313 {
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316
2317 mHWData->mCpuIdLeafList.clear();
2318 }
2319
2320 return S_OK;
2321}
2322HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2323{
2324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2325
2326 switch(aProperty)
2327 {
2328 case HWVirtExPropertyType_Enabled:
2329 *aValue = mHWData->mHWVirtExEnabled;
2330 break;
2331
2332 case HWVirtExPropertyType_VPID:
2333 *aValue = mHWData->mHWVirtExVPIDEnabled;
2334 break;
2335
2336 case HWVirtExPropertyType_NestedPaging:
2337 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2338 break;
2339
2340 case HWVirtExPropertyType_UnrestrictedExecution:
2341 *aValue = mHWData->mHWVirtExUXEnabled;
2342 break;
2343
2344 case HWVirtExPropertyType_LargePages:
2345 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2346#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2347 *aValue = FALSE;
2348#endif
2349 break;
2350
2351 case HWVirtExPropertyType_Force:
2352 *aValue = mHWData->mHWVirtExForceEnabled;
2353 break;
2354
2355 case HWVirtExPropertyType_UseNativeApi:
2356 *aValue = mHWData->mHWVirtExUseNativeApi;
2357 break;
2358
2359 default:
2360 return E_INVALIDARG;
2361 }
2362 return S_OK;
2363}
2364
2365HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2366{
2367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 HRESULT rc = i_checkStateDependency(MutableStateDep);
2370 if (FAILED(rc)) return rc;
2371
2372 switch (aProperty)
2373 {
2374 case HWVirtExPropertyType_Enabled:
2375 i_setModified(IsModified_MachineData);
2376 mHWData.backup();
2377 mHWData->mHWVirtExEnabled = !!aValue;
2378 break;
2379
2380 case HWVirtExPropertyType_VPID:
2381 i_setModified(IsModified_MachineData);
2382 mHWData.backup();
2383 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2384 break;
2385
2386 case HWVirtExPropertyType_NestedPaging:
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2390 break;
2391
2392 case HWVirtExPropertyType_UnrestrictedExecution:
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 mHWData->mHWVirtExUXEnabled = !!aValue;
2396 break;
2397
2398 case HWVirtExPropertyType_LargePages:
2399 i_setModified(IsModified_MachineData);
2400 mHWData.backup();
2401 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2402 break;
2403
2404 case HWVirtExPropertyType_Force:
2405 i_setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mHWVirtExForceEnabled = !!aValue;
2408 break;
2409
2410 case HWVirtExPropertyType_UseNativeApi:
2411 i_setModified(IsModified_MachineData);
2412 mHWData.backup();
2413 mHWData->mHWVirtExUseNativeApi = !!aValue;
2414 break;
2415
2416 default:
2417 return E_INVALIDARG;
2418 }
2419
2420 return S_OK;
2421}
2422
2423HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2424{
2425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2428
2429 return S_OK;
2430}
2431
2432HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2433{
2434 /** @todo (r=dmik):
2435 * 1. Allow to change the name of the snapshot folder containing snapshots
2436 * 2. Rename the folder on disk instead of just changing the property
2437 * value (to be smart and not to leave garbage). Note that it cannot be
2438 * done here because the change may be rolled back. Thus, the right
2439 * place is #saveSettings().
2440 */
2441
2442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 HRESULT rc = i_checkStateDependency(MutableStateDep);
2445 if (FAILED(rc)) return rc;
2446
2447 if (!mData->mCurrentSnapshot.isNull())
2448 return setError(E_FAIL,
2449 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2450
2451 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2452
2453 if (strSnapshotFolder.isEmpty())
2454 strSnapshotFolder = "Snapshots";
2455 int vrc = i_calculateFullPath(strSnapshotFolder,
2456 strSnapshotFolder);
2457 if (RT_FAILURE(vrc))
2458 return setErrorBoth(E_FAIL, vrc,
2459 tr("Invalid snapshot folder '%s' (%Rrc)"),
2460 strSnapshotFolder.c_str(), vrc);
2461
2462 i_setModified(IsModified_MachineData);
2463 mUserData.backup();
2464
2465 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2466
2467 return S_OK;
2468}
2469
2470HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2471{
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 aMediumAttachments.resize(mMediumAttachments->size());
2475 size_t i = 0;
2476 for (MediumAttachmentList::const_iterator
2477 it = mMediumAttachments->begin();
2478 it != mMediumAttachments->end();
2479 ++it, ++i)
2480 aMediumAttachments[i] = *it;
2481
2482 return S_OK;
2483}
2484
2485HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2486{
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 Assert(!!mVRDEServer);
2490
2491 aVRDEServer = mVRDEServer;
2492
2493 return S_OK;
2494}
2495
2496HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2497{
2498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2499
2500 aAudioAdapter = mAudioAdapter;
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2506{
2507#ifdef VBOX_WITH_VUSB
2508 clearError();
2509 MultiResult rc(S_OK);
2510
2511# ifdef VBOX_WITH_USB
2512 rc = mParent->i_host()->i_checkUSBProxyService();
2513 if (FAILED(rc)) return rc;
2514# endif
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 aUSBControllers.resize(mUSBControllers->size());
2519 size_t i = 0;
2520 for (USBControllerList::const_iterator
2521 it = mUSBControllers->begin();
2522 it != mUSBControllers->end();
2523 ++it, ++i)
2524 aUSBControllers[i] = *it;
2525
2526 return S_OK;
2527#else
2528 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2529 * extended error info to indicate that USB is simply not available
2530 * (w/o treating it as a failure), for example, as in OSE */
2531 NOREF(aUSBControllers);
2532 ReturnComNotImplemented();
2533#endif /* VBOX_WITH_VUSB */
2534}
2535
2536HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2537{
2538#ifdef VBOX_WITH_VUSB
2539 clearError();
2540 MultiResult rc(S_OK);
2541
2542# ifdef VBOX_WITH_USB
2543 rc = mParent->i_host()->i_checkUSBProxyService();
2544 if (FAILED(rc)) return rc;
2545# endif
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 aUSBDeviceFilters = mUSBDeviceFilters;
2550 return rc;
2551#else
2552 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2553 * extended error info to indicate that USB is simply not available
2554 * (w/o treating it as a failure), for example, as in OSE */
2555 NOREF(aUSBDeviceFilters);
2556 ReturnComNotImplemented();
2557#endif /* VBOX_WITH_VUSB */
2558}
2559
2560HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2561{
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 aSettingsFilePath = mData->m_strConfigFileFull;
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2570{
2571 RT_NOREF(aSettingsFilePath);
2572 ReturnComNotImplemented();
2573}
2574
2575HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2576{
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2580 if (FAILED(rc)) return rc;
2581
2582 if (!mData->pMachineConfigFile->fileExists())
2583 // this is a new machine, and no config file exists yet:
2584 *aSettingsModified = TRUE;
2585 else
2586 *aSettingsModified = (mData->flModifications != 0);
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 *aSessionState = mData->mSession.mState;
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 aSessionName = mData->mSession.mName;
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2610{
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 *aSessionPID = mData->mSession.mPID;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getState(MachineState_T *aState)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 *aState = mData->mMachineState;
2623 Assert(mData->mMachineState != MachineState_Null);
2624
2625 return S_OK;
2626}
2627
2628HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2629{
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aStateFilePath = mSSData->strStateFilePath;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 i_getLogFolder(aLogFolder);
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 aCurrentSnapshot = mData->mCurrentSnapshot;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2669 ? 0
2670 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 /* Note: for machines with no snapshots, we always return FALSE
2680 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2681 * reasons :) */
2682
2683 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2684 ? FALSE
2685 : mData->mCurrentStateModified;
2686
2687 return S_OK;
2688}
2689
2690HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2691{
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 aSharedFolders.resize(mHWData->mSharedFolders.size());
2695 size_t i = 0;
2696 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2697 it = mHWData->mSharedFolders.begin();
2698 it != mHWData->mSharedFolders.end();
2699 ++it, ++i)
2700 aSharedFolders[i] = *it;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 *aClipboardMode = mHWData->mClipboardMode;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2715{
2716 HRESULT rc = S_OK;
2717
2718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 alock.release();
2721 rc = i_onClipboardModeChange(aClipboardMode);
2722 alock.acquire();
2723 if (FAILED(rc)) return rc;
2724
2725 i_setModified(IsModified_MachineData);
2726 mHWData.backup();
2727 mHWData->mClipboardMode = aClipboardMode;
2728
2729 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2730 if (Global::IsOnline(mData->mMachineState))
2731 i_saveSettings(NULL);
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aDnDMode = mHWData->mDnDMode;
2741
2742 return S_OK;
2743}
2744
2745HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2746{
2747 HRESULT rc = S_OK;
2748
2749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 alock.release();
2752 rc = i_onDnDModeChange(aDnDMode);
2753
2754 alock.acquire();
2755 if (FAILED(rc)) return rc;
2756
2757 i_setModified(IsModified_MachineData);
2758 mHWData.backup();
2759 mHWData->mDnDMode = aDnDMode;
2760
2761 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2762 if (Global::IsOnline(mData->mMachineState))
2763 i_saveSettings(NULL);
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aStorageControllers.resize(mStorageControllers->size());
2773 size_t i = 0;
2774 for (StorageControllerList::const_iterator
2775 it = mStorageControllers->begin();
2776 it != mStorageControllers->end();
2777 ++it, ++i)
2778 aStorageControllers[i] = *it;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aEnabled = mUserData->s.fTeleporterEnabled;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2793{
2794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 /* Only allow it to be set to true when PoweredOff or Aborted.
2797 (Clearing it is always permitted.) */
2798 if ( aTeleporterEnabled
2799 && mData->mRegistered
2800 && ( !i_isSessionMachine()
2801 || ( mData->mMachineState != MachineState_PoweredOff
2802 && mData->mMachineState != MachineState_Teleported
2803 && mData->mMachineState != MachineState_Aborted
2804 )
2805 )
2806 )
2807 return setError(VBOX_E_INVALID_VM_STATE,
2808 tr("The machine is not powered off (state is %s)"),
2809 Global::stringifyMachineState(mData->mMachineState));
2810
2811 i_setModified(IsModified_MachineData);
2812 mUserData.backup();
2813 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2828{
2829 if (aTeleporterPort >= _64K)
2830 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2831
2832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2835 if (FAILED(rc)) return rc;
2836
2837 i_setModified(IsModified_MachineData);
2838 mUserData.backup();
2839 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2858 if (FAILED(rc)) return rc;
2859
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2871
2872 return S_OK;
2873}
2874
2875HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2876{
2877 /*
2878 * Hash the password first.
2879 */
2880 com::Utf8Str aT = aTeleporterPassword;
2881
2882 if (!aT.isEmpty())
2883 {
2884 if (VBoxIsPasswordHashed(&aT))
2885 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2886 VBoxHashPassword(&aT);
2887 }
2888
2889 /*
2890 * Do the update.
2891 */
2892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2893 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2894 if (SUCCEEDED(hrc))
2895 {
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.strTeleporterPassword = aT;
2899 }
2900
2901 return hrc;
2902}
2903
2904HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2914{
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 /* Only allow it to be set to true when PoweredOff or Aborted.
2918 (Clearing it is always permitted.) */
2919 if ( aRTCUseUTC
2920 && mData->mRegistered
2921 && ( !i_isSessionMachine()
2922 || ( mData->mMachineState != MachineState_PoweredOff
2923 && mData->mMachineState != MachineState_Teleported
2924 && mData->mMachineState != MachineState_Aborted
2925 )
2926 )
2927 )
2928 return setError(VBOX_E_INVALID_VM_STATE,
2929 tr("The machine is not powered off (state is %s)"),
2930 Global::stringifyMachineState(mData->mMachineState));
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mHWData.backup();
2957 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965
2966 *aIOCacheSize = mHWData->mIOCacheSize;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 HRESULT rc = i_checkStateDependency(MutableStateDep);
2976 if (FAILED(rc)) return rc;
2977
2978 i_setModified(IsModified_MachineData);
2979 mHWData.backup();
2980 mHWData->mIOCacheSize = aIOCacheSize;
2981
2982 return S_OK;
2983}
2984
2985
2986/**
2987 * @note Locks objects!
2988 */
2989HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2990 LockType_T aLockType)
2991{
2992 /* check the session state */
2993 SessionState_T state;
2994 HRESULT rc = aSession->COMGETTER(State)(&state);
2995 if (FAILED(rc)) return rc;
2996
2997 if (state != SessionState_Unlocked)
2998 return setError(VBOX_E_INVALID_OBJECT_STATE,
2999 tr("The given session is busy"));
3000
3001 // get the client's IInternalSessionControl interface
3002 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3003 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3004 E_INVALIDARG);
3005
3006 // session name (only used in some code paths)
3007 Utf8Str strSessionName;
3008
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 if (!mData->mRegistered)
3012 return setError(E_UNEXPECTED,
3013 tr("The machine '%s' is not registered"),
3014 mUserData->s.strName.c_str());
3015
3016 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3017
3018 SessionState_T oldState = mData->mSession.mState;
3019 /* Hack: in case the session is closing and there is a progress object
3020 * which allows waiting for the session to be closed, take the opportunity
3021 * and do a limited wait (max. 1 second). This helps a lot when the system
3022 * is busy and thus session closing can take a little while. */
3023 if ( mData->mSession.mState == SessionState_Unlocking
3024 && mData->mSession.mProgress)
3025 {
3026 alock.release();
3027 mData->mSession.mProgress->WaitForCompletion(1000);
3028 alock.acquire();
3029 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3030 }
3031
3032 // try again now
3033 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3034 // (i.e. session machine exists)
3035 && (aLockType == LockType_Shared) // caller wants a shared link to the
3036 // existing session that holds the write lock:
3037 )
3038 {
3039 // OK, share the session... we are now dealing with three processes:
3040 // 1) VBoxSVC (where this code runs);
3041 // 2) process C: the caller's client process (who wants a shared session);
3042 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3043
3044 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3045 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3046 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3047 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3048 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3049
3050 /*
3051 * Release the lock before calling the client process. It's safe here
3052 * since the only thing to do after we get the lock again is to add
3053 * the remote control to the list (which doesn't directly influence
3054 * anything).
3055 */
3056 alock.release();
3057
3058 // get the console of the session holding the write lock (this is a remote call)
3059 ComPtr<IConsole> pConsoleW;
3060 if (mData->mSession.mLockType == LockType_VM)
3061 {
3062 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3063 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3064 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3065 if (FAILED(rc))
3066 // the failure may occur w/o any error info (from RPC), so provide one
3067 return setError(VBOX_E_VM_ERROR,
3068 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3069 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3070 }
3071
3072 // share the session machine and W's console with the caller's session
3073 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3074 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3075 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3076
3077 if (FAILED(rc))
3078 // the failure may occur w/o any error info (from RPC), so provide one
3079 return setError(VBOX_E_VM_ERROR,
3080 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3081 alock.acquire();
3082
3083 // need to revalidate the state after acquiring the lock again
3084 if (mData->mSession.mState != SessionState_Locked)
3085 {
3086 pSessionControl->Uninitialize();
3087 return setError(VBOX_E_INVALID_SESSION_STATE,
3088 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3089 mUserData->s.strName.c_str());
3090 }
3091
3092 // add the caller's session to the list
3093 mData->mSession.mRemoteControls.push_back(pSessionControl);
3094 }
3095 else if ( mData->mSession.mState == SessionState_Locked
3096 || mData->mSession.mState == SessionState_Unlocking
3097 )
3098 {
3099 // sharing not permitted, or machine still unlocking:
3100 return setError(VBOX_E_INVALID_OBJECT_STATE,
3101 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3102 mUserData->s.strName.c_str());
3103 }
3104 else
3105 {
3106 // machine is not locked: then write-lock the machine (create the session machine)
3107
3108 // must not be busy
3109 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3110
3111 // get the caller's session PID
3112 RTPROCESS pid = NIL_RTPROCESS;
3113 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3114 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3115 Assert(pid != NIL_RTPROCESS);
3116
3117 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3118
3119 if (fLaunchingVMProcess)
3120 {
3121 if (mData->mSession.mPID == NIL_RTPROCESS)
3122 {
3123 // two or more clients racing for a lock, the one which set the
3124 // session state to Spawning will win, the others will get an
3125 // error as we can't decide here if waiting a little would help
3126 // (only for shared locks this would avoid an error)
3127 return setError(VBOX_E_INVALID_OBJECT_STATE,
3128 tr("The machine '%s' already has a lock request pending"),
3129 mUserData->s.strName.c_str());
3130 }
3131
3132 // this machine is awaiting for a spawning session to be opened:
3133 // then the calling process must be the one that got started by
3134 // LaunchVMProcess()
3135
3136 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3137 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3138
3139#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3140 /* Hardened windows builds spawns three processes when a VM is
3141 launched, the 3rd one is the one that will end up here. */
3142 RTPROCESS ppid;
3143 int rc = RTProcQueryParent(pid, &ppid);
3144 if (RT_SUCCESS(rc))
3145 rc = RTProcQueryParent(ppid, &ppid);
3146 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3147 || rc == VERR_ACCESS_DENIED)
3148 {
3149 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3150 mData->mSession.mPID = pid;
3151 }
3152#endif
3153
3154 if (mData->mSession.mPID != pid)
3155 return setError(E_ACCESSDENIED,
3156 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3157 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3158 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3159 }
3160
3161 // create the mutable SessionMachine from the current machine
3162 ComObjPtr<SessionMachine> sessionMachine;
3163 sessionMachine.createObject();
3164 rc = sessionMachine->init(this);
3165 AssertComRC(rc);
3166
3167 /* NOTE: doing return from this function after this point but
3168 * before the end is forbidden since it may call SessionMachine::uninit()
3169 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3170 * lock while still holding the Machine lock in alock so that a deadlock
3171 * is possible due to the wrong lock order. */
3172
3173 if (SUCCEEDED(rc))
3174 {
3175 /*
3176 * Set the session state to Spawning to protect against subsequent
3177 * attempts to open a session and to unregister the machine after
3178 * we release the lock.
3179 */
3180 SessionState_T origState = mData->mSession.mState;
3181 mData->mSession.mState = SessionState_Spawning;
3182
3183#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3184 /* Get the client token ID to be passed to the client process */
3185 Utf8Str strTokenId;
3186 sessionMachine->i_getTokenId(strTokenId);
3187 Assert(!strTokenId.isEmpty());
3188#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3189 /* Get the client token to be passed to the client process */
3190 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3191 /* The token is now "owned" by pToken, fix refcount */
3192 if (!pToken.isNull())
3193 pToken->Release();
3194#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3195
3196 /*
3197 * Release the lock before calling the client process -- it will call
3198 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3199 * because the state is Spawning, so that LaunchVMProcess() and
3200 * LockMachine() calls will fail. This method, called before we
3201 * acquire the lock again, will fail because of the wrong PID.
3202 *
3203 * Note that mData->mSession.mRemoteControls accessed outside
3204 * the lock may not be modified when state is Spawning, so it's safe.
3205 */
3206 alock.release();
3207
3208 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3209#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3210 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3211#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3212 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3213 /* Now the token is owned by the client process. */
3214 pToken.setNull();
3215#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3216 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3217
3218 /* The failure may occur w/o any error info (from RPC), so provide one */
3219 if (FAILED(rc))
3220 setError(VBOX_E_VM_ERROR,
3221 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3222
3223 // get session name, either to remember or to compare against
3224 // the already known session name.
3225 {
3226 Bstr bstrSessionName;
3227 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3228 if (SUCCEEDED(rc2))
3229 strSessionName = bstrSessionName;
3230 }
3231
3232 if ( SUCCEEDED(rc)
3233 && fLaunchingVMProcess
3234 )
3235 {
3236 /* complete the remote session initialization */
3237
3238 /* get the console from the direct session */
3239 ComPtr<IConsole> console;
3240 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3241 ComAssertComRC(rc);
3242
3243 if (SUCCEEDED(rc) && !console)
3244 {
3245 ComAssert(!!console);
3246 rc = E_FAIL;
3247 }
3248
3249 /* assign machine & console to the remote session */
3250 if (SUCCEEDED(rc))
3251 {
3252 /*
3253 * after LaunchVMProcess(), the first and the only
3254 * entry in remoteControls is that remote session
3255 */
3256 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3257 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3258 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3259
3260 /* The failure may occur w/o any error info (from RPC), so provide one */
3261 if (FAILED(rc))
3262 setError(VBOX_E_VM_ERROR,
3263 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3264 }
3265
3266 if (FAILED(rc))
3267 pSessionControl->Uninitialize();
3268 }
3269
3270 /* acquire the lock again */
3271 alock.acquire();
3272
3273 /* Restore the session state */
3274 mData->mSession.mState = origState;
3275 }
3276
3277 // finalize spawning anyway (this is why we don't return on errors above)
3278 if (fLaunchingVMProcess)
3279 {
3280 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3281 /* Note that the progress object is finalized later */
3282 /** @todo Consider checking mData->mSession.mProgress for cancellation
3283 * around here. */
3284
3285 /* We don't reset mSession.mPID here because it is necessary for
3286 * SessionMachine::uninit() to reap the child process later. */
3287
3288 if (FAILED(rc))
3289 {
3290 /* Close the remote session, remove the remote control from the list
3291 * and reset session state to Closed (@note keep the code in sync
3292 * with the relevant part in checkForSpawnFailure()). */
3293
3294 Assert(mData->mSession.mRemoteControls.size() == 1);
3295 if (mData->mSession.mRemoteControls.size() == 1)
3296 {
3297 ErrorInfoKeeper eik;
3298 mData->mSession.mRemoteControls.front()->Uninitialize();
3299 }
3300
3301 mData->mSession.mRemoteControls.clear();
3302 mData->mSession.mState = SessionState_Unlocked;
3303 }
3304 }
3305 else
3306 {
3307 /* memorize PID of the directly opened session */
3308 if (SUCCEEDED(rc))
3309 mData->mSession.mPID = pid;
3310 }
3311
3312 if (SUCCEEDED(rc))
3313 {
3314 mData->mSession.mLockType = aLockType;
3315 /* memorize the direct session control and cache IUnknown for it */
3316 mData->mSession.mDirectControl = pSessionControl;
3317 mData->mSession.mState = SessionState_Locked;
3318 if (!fLaunchingVMProcess)
3319 mData->mSession.mName = strSessionName;
3320 /* associate the SessionMachine with this Machine */
3321 mData->mSession.mMachine = sessionMachine;
3322
3323 /* request an IUnknown pointer early from the remote party for later
3324 * identity checks (it will be internally cached within mDirectControl
3325 * at least on XPCOM) */
3326 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3327 NOREF(unk);
3328 }
3329
3330 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3331 * would break the lock order */
3332 alock.release();
3333
3334 /* uninitialize the created session machine on failure */
3335 if (FAILED(rc))
3336 sessionMachine->uninit();
3337 }
3338
3339 if (SUCCEEDED(rc))
3340 {
3341 /*
3342 * tell the client watcher thread to update the set of
3343 * machines that have open sessions
3344 */
3345 mParent->i_updateClientWatcher();
3346
3347 if (oldState != SessionState_Locked)
3348 /* fire an event */
3349 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3350 }
3351
3352 return rc;
3353}
3354
3355/**
3356 * @note Locks objects!
3357 */
3358HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3359 const com::Utf8Str &aName,
3360 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3361 ComPtr<IProgress> &aProgress)
3362{
3363 Utf8Str strFrontend(aName);
3364 /* "emergencystop" doesn't need the session, so skip the checks/interface
3365 * retrieval. This code doesn't quite fit in here, but introducing a
3366 * special API method would be even more effort, and would require explicit
3367 * support by every API client. It's better to hide the feature a bit. */
3368 if (strFrontend != "emergencystop")
3369 CheckComArgNotNull(aSession);
3370
3371 HRESULT rc = S_OK;
3372 if (strFrontend.isEmpty())
3373 {
3374 Bstr bstrFrontend;
3375 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3376 if (FAILED(rc))
3377 return rc;
3378 strFrontend = bstrFrontend;
3379 if (strFrontend.isEmpty())
3380 {
3381 ComPtr<ISystemProperties> systemProperties;
3382 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3383 if (FAILED(rc))
3384 return rc;
3385 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3386 if (FAILED(rc))
3387 return rc;
3388 strFrontend = bstrFrontend;
3389 }
3390 /* paranoia - emergencystop is not a valid default */
3391 if (strFrontend == "emergencystop")
3392 strFrontend = Utf8Str::Empty;
3393 }
3394 /* default frontend: Qt GUI */
3395 if (strFrontend.isEmpty())
3396 strFrontend = "GUI/Qt";
3397
3398 if (strFrontend != "emergencystop")
3399 {
3400 /* check the session state */
3401 SessionState_T state;
3402 rc = aSession->COMGETTER(State)(&state);
3403 if (FAILED(rc))
3404 return rc;
3405
3406 if (state != SessionState_Unlocked)
3407 return setError(VBOX_E_INVALID_OBJECT_STATE,
3408 tr("The given session is busy"));
3409
3410 /* get the IInternalSessionControl interface */
3411 ComPtr<IInternalSessionControl> control(aSession);
3412 ComAssertMsgRet(!control.isNull(),
3413 ("No IInternalSessionControl interface"),
3414 E_INVALIDARG);
3415
3416 /* get the teleporter enable state for the progress object init. */
3417 BOOL fTeleporterEnabled;
3418 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3419 if (FAILED(rc))
3420 return rc;
3421
3422 /* create a progress object */
3423 ComObjPtr<ProgressProxy> progress;
3424 progress.createObject();
3425 rc = progress->init(mParent,
3426 static_cast<IMachine*>(this),
3427 Bstr(tr("Starting VM")).raw(),
3428 TRUE /* aCancelable */,
3429 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3430 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3431 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3432 2 /* uFirstOperationWeight */,
3433 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3434
3435 if (SUCCEEDED(rc))
3436 {
3437 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3438 if (SUCCEEDED(rc))
3439 {
3440 aProgress = progress;
3441
3442 /* signal the client watcher thread */
3443 mParent->i_updateClientWatcher();
3444
3445 /* fire an event */
3446 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3447 }
3448 }
3449 }
3450 else
3451 {
3452 /* no progress object - either instant success or failure */
3453 aProgress = NULL;
3454
3455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3456
3457 if (mData->mSession.mState != SessionState_Locked)
3458 return setError(VBOX_E_INVALID_OBJECT_STATE,
3459 tr("The machine '%s' is not locked by a session"),
3460 mUserData->s.strName.c_str());
3461
3462 /* must have a VM process associated - do not kill normal API clients
3463 * with an open session */
3464 if (!Global::IsOnline(mData->mMachineState))
3465 return setError(VBOX_E_INVALID_OBJECT_STATE,
3466 tr("The machine '%s' does not have a VM process"),
3467 mUserData->s.strName.c_str());
3468
3469 /* forcibly terminate the VM process */
3470 if (mData->mSession.mPID != NIL_RTPROCESS)
3471 RTProcTerminate(mData->mSession.mPID);
3472
3473 /* signal the client watcher thread, as most likely the client has
3474 * been terminated */
3475 mParent->i_updateClientWatcher();
3476 }
3477
3478 return rc;
3479}
3480
3481HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3482{
3483 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3484 return setError(E_INVALIDARG,
3485 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3486 aPosition, SchemaDefs::MaxBootPosition);
3487
3488 if (aDevice == DeviceType_USB)
3489 return setError(E_NOTIMPL,
3490 tr("Booting from USB device is currently not supported"));
3491
3492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3493
3494 HRESULT rc = i_checkStateDependency(MutableStateDep);
3495 if (FAILED(rc)) return rc;
3496
3497 i_setModified(IsModified_MachineData);
3498 mHWData.backup();
3499 mHWData->mBootOrder[aPosition - 1] = aDevice;
3500
3501 return S_OK;
3502}
3503
3504HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3505{
3506 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3507 return setError(E_INVALIDARG,
3508 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3509 aPosition, SchemaDefs::MaxBootPosition);
3510
3511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3512
3513 *aDevice = mHWData->mBootOrder[aPosition - 1];
3514
3515 return S_OK;
3516}
3517
3518HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3519 LONG aControllerPort,
3520 LONG aDevice,
3521 DeviceType_T aType,
3522 const ComPtr<IMedium> &aMedium)
3523{
3524 IMedium *aM = aMedium;
3525 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3526 aName.c_str(), aControllerPort, aDevice, aType, aM));
3527
3528 // request the host lock first, since might be calling Host methods for getting host drives;
3529 // next, protect the media tree all the while we're in here, as well as our member variables
3530 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3531 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3532
3533 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3534 if (FAILED(rc)) return rc;
3535
3536 /// @todo NEWMEDIA implicit machine registration
3537 if (!mData->mRegistered)
3538 return setError(VBOX_E_INVALID_OBJECT_STATE,
3539 tr("Cannot attach storage devices to an unregistered machine"));
3540
3541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3542
3543 /* Check for an existing controller. */
3544 ComObjPtr<StorageController> ctl;
3545 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3546 if (FAILED(rc)) return rc;
3547
3548 StorageControllerType_T ctrlType;
3549 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3550 if (FAILED(rc))
3551 return setError(E_FAIL,
3552 tr("Could not get type of controller '%s'"),
3553 aName.c_str());
3554
3555 bool fSilent = false;
3556 Utf8Str strReconfig;
3557
3558 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3559 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3560 if ( mData->mMachineState == MachineState_Paused
3561 && strReconfig == "1")
3562 fSilent = true;
3563
3564 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3565 bool fHotplug = false;
3566 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3567 fHotplug = true;
3568
3569 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3570 return setError(VBOX_E_INVALID_VM_STATE,
3571 tr("Controller '%s' does not support hotplugging"),
3572 aName.c_str());
3573
3574 // check that the port and device are not out of range
3575 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3576 if (FAILED(rc)) return rc;
3577
3578 /* check if the device slot is already busy */
3579 MediumAttachment *pAttachTemp;
3580 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3581 aName,
3582 aControllerPort,
3583 aDevice)))
3584 {
3585 Medium *pMedium = pAttachTemp->i_getMedium();
3586 if (pMedium)
3587 {
3588 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3589 return setError(VBOX_E_OBJECT_IN_USE,
3590 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3591 pMedium->i_getLocationFull().c_str(),
3592 aControllerPort,
3593 aDevice,
3594 aName.c_str());
3595 }
3596 else
3597 return setError(VBOX_E_OBJECT_IN_USE,
3598 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3599 aControllerPort, aDevice, aName.c_str());
3600 }
3601
3602 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3603 if (aMedium && medium.isNull())
3604 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3605
3606 AutoCaller mediumCaller(medium);
3607 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3608
3609 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3610
3611 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3612 && !medium.isNull()
3613 )
3614 return setError(VBOX_E_OBJECT_IN_USE,
3615 tr("Medium '%s' is already attached to this virtual machine"),
3616 medium->i_getLocationFull().c_str());
3617
3618 if (!medium.isNull())
3619 {
3620 MediumType_T mtype = medium->i_getType();
3621 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3622 // For DVDs it's not written to the config file, so needs no global config
3623 // version bump. For floppies it's a new attribute "type", which is ignored
3624 // by older VirtualBox version, so needs no global config version bump either.
3625 // For hard disks this type is not accepted.
3626 if (mtype == MediumType_MultiAttach)
3627 {
3628 // This type is new with VirtualBox 4.0 and therefore requires settings
3629 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3630 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3631 // two reasons: The medium type is a property of the media registry tree, which
3632 // can reside in the global config file (for pre-4.0 media); we would therefore
3633 // possibly need to bump the global config version. We don't want to do that though
3634 // because that might make downgrading to pre-4.0 impossible.
3635 // As a result, we can only use these two new types if the medium is NOT in the
3636 // global registry:
3637 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3638 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3639 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3640 )
3641 return setError(VBOX_E_INVALID_OBJECT_STATE,
3642 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3643 "to machines that were created with VirtualBox 4.0 or later"),
3644 medium->i_getLocationFull().c_str());
3645 }
3646 }
3647
3648 bool fIndirect = false;
3649 if (!medium.isNull())
3650 fIndirect = medium->i_isReadOnly();
3651 bool associate = true;
3652
3653 do
3654 {
3655 if ( aType == DeviceType_HardDisk
3656 && mMediumAttachments.isBackedUp())
3657 {
3658 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3659
3660 /* check if the medium was attached to the VM before we started
3661 * changing attachments in which case the attachment just needs to
3662 * be restored */
3663 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3664 {
3665 AssertReturn(!fIndirect, E_FAIL);
3666
3667 /* see if it's the same bus/channel/device */
3668 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3669 {
3670 /* the simplest case: restore the whole attachment
3671 * and return, nothing else to do */
3672 mMediumAttachments->push_back(pAttachTemp);
3673
3674 /* Reattach the medium to the VM. */
3675 if (fHotplug || fSilent)
3676 {
3677 mediumLock.release();
3678 treeLock.release();
3679 alock.release();
3680
3681 MediumLockList *pMediumLockList(new MediumLockList());
3682
3683 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3684 medium /* pToLockWrite */,
3685 false /* fMediumLockWriteAll */,
3686 NULL,
3687 *pMediumLockList);
3688 alock.acquire();
3689 if (FAILED(rc))
3690 delete pMediumLockList;
3691 else
3692 {
3693 mData->mSession.mLockedMedia.Unlock();
3694 alock.release();
3695 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3696 mData->mSession.mLockedMedia.Lock();
3697 alock.acquire();
3698 }
3699 alock.release();
3700
3701 if (SUCCEEDED(rc))
3702 {
3703 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3704 /* Remove lock list in case of error. */
3705 if (FAILED(rc))
3706 {
3707 mData->mSession.mLockedMedia.Unlock();
3708 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3709 mData->mSession.mLockedMedia.Lock();
3710 }
3711 }
3712 }
3713
3714 return S_OK;
3715 }
3716
3717 /* bus/channel/device differ; we need a new attachment object,
3718 * but don't try to associate it again */
3719 associate = false;
3720 break;
3721 }
3722 }
3723
3724 /* go further only if the attachment is to be indirect */
3725 if (!fIndirect)
3726 break;
3727
3728 /* perform the so called smart attachment logic for indirect
3729 * attachments. Note that smart attachment is only applicable to base
3730 * hard disks. */
3731
3732 if (medium->i_getParent().isNull())
3733 {
3734 /* first, investigate the backup copy of the current hard disk
3735 * attachments to make it possible to re-attach existing diffs to
3736 * another device slot w/o losing their contents */
3737 if (mMediumAttachments.isBackedUp())
3738 {
3739 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3740
3741 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3742 uint32_t foundLevel = 0;
3743
3744 for (MediumAttachmentList::const_iterator
3745 it = oldAtts.begin();
3746 it != oldAtts.end();
3747 ++it)
3748 {
3749 uint32_t level = 0;
3750 MediumAttachment *pAttach = *it;
3751 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3752 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3753 if (pMedium.isNull())
3754 continue;
3755
3756 if (pMedium->i_getBase(&level) == medium)
3757 {
3758 /* skip the hard disk if its currently attached (we
3759 * cannot attach the same hard disk twice) */
3760 if (i_findAttachment(*mMediumAttachments.data(),
3761 pMedium))
3762 continue;
3763
3764 /* matched device, channel and bus (i.e. attached to the
3765 * same place) will win and immediately stop the search;
3766 * otherwise the attachment that has the youngest
3767 * descendant of medium will be used
3768 */
3769 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3770 {
3771 /* the simplest case: restore the whole attachment
3772 * and return, nothing else to do */
3773 mMediumAttachments->push_back(*it);
3774
3775 /* Reattach the medium to the VM. */
3776 if (fHotplug || fSilent)
3777 {
3778 mediumLock.release();
3779 treeLock.release();
3780 alock.release();
3781
3782 MediumLockList *pMediumLockList(new MediumLockList());
3783
3784 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3785 medium /* pToLockWrite */,
3786 false /* fMediumLockWriteAll */,
3787 NULL,
3788 *pMediumLockList);
3789 alock.acquire();
3790 if (FAILED(rc))
3791 delete pMediumLockList;
3792 else
3793 {
3794 mData->mSession.mLockedMedia.Unlock();
3795 alock.release();
3796 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3797 mData->mSession.mLockedMedia.Lock();
3798 alock.acquire();
3799 }
3800 alock.release();
3801
3802 if (SUCCEEDED(rc))
3803 {
3804 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3805 /* Remove lock list in case of error. */
3806 if (FAILED(rc))
3807 {
3808 mData->mSession.mLockedMedia.Unlock();
3809 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3810 mData->mSession.mLockedMedia.Lock();
3811 }
3812 }
3813 }
3814
3815 return S_OK;
3816 }
3817 else if ( foundIt == oldAtts.end()
3818 || level > foundLevel /* prefer younger */
3819 )
3820 {
3821 foundIt = it;
3822 foundLevel = level;
3823 }
3824 }
3825 }
3826
3827 if (foundIt != oldAtts.end())
3828 {
3829 /* use the previously attached hard disk */
3830 medium = (*foundIt)->i_getMedium();
3831 mediumCaller.attach(medium);
3832 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3833 mediumLock.attach(medium);
3834 /* not implicit, doesn't require association with this VM */
3835 fIndirect = false;
3836 associate = false;
3837 /* go right to the MediumAttachment creation */
3838 break;
3839 }
3840 }
3841
3842 /* must give up the medium lock and medium tree lock as below we
3843 * go over snapshots, which needs a lock with higher lock order. */
3844 mediumLock.release();
3845 treeLock.release();
3846
3847 /* then, search through snapshots for the best diff in the given
3848 * hard disk's chain to base the new diff on */
3849
3850 ComObjPtr<Medium> base;
3851 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3852 while (snap)
3853 {
3854 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3855
3856 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3857
3858 MediumAttachment *pAttachFound = NULL;
3859 uint32_t foundLevel = 0;
3860
3861 for (MediumAttachmentList::const_iterator
3862 it = snapAtts.begin();
3863 it != snapAtts.end();
3864 ++it)
3865 {
3866 MediumAttachment *pAttach = *it;
3867 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3868 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3869 if (pMedium.isNull())
3870 continue;
3871
3872 uint32_t level = 0;
3873 if (pMedium->i_getBase(&level) == medium)
3874 {
3875 /* matched device, channel and bus (i.e. attached to the
3876 * same place) will win and immediately stop the search;
3877 * otherwise the attachment that has the youngest
3878 * descendant of medium will be used
3879 */
3880 if ( pAttach->i_getDevice() == aDevice
3881 && pAttach->i_getPort() == aControllerPort
3882 && pAttach->i_getControllerName() == aName
3883 )
3884 {
3885 pAttachFound = pAttach;
3886 break;
3887 }
3888 else if ( !pAttachFound
3889 || level > foundLevel /* prefer younger */
3890 )
3891 {
3892 pAttachFound = pAttach;
3893 foundLevel = level;
3894 }
3895 }
3896 }
3897
3898 if (pAttachFound)
3899 {
3900 base = pAttachFound->i_getMedium();
3901 break;
3902 }
3903
3904 snap = snap->i_getParent();
3905 }
3906
3907 /* re-lock medium tree and the medium, as we need it below */
3908 treeLock.acquire();
3909 mediumLock.acquire();
3910
3911 /* found a suitable diff, use it as a base */
3912 if (!base.isNull())
3913 {
3914 medium = base;
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 }
3919 }
3920
3921 Utf8Str strFullSnapshotFolder;
3922 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3923
3924 ComObjPtr<Medium> diff;
3925 diff.createObject();
3926 // store this diff in the same registry as the parent
3927 Guid uuidRegistryParent;
3928 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3929 {
3930 // parent image has no registry: this can happen if we're attaching a new immutable
3931 // image that has not yet been attached (medium then points to the base and we're
3932 // creating the diff image for the immutable, and the parent is not yet registered);
3933 // put the parent in the machine registry then
3934 mediumLock.release();
3935 treeLock.release();
3936 alock.release();
3937 i_addMediumToRegistry(medium);
3938 alock.acquire();
3939 treeLock.acquire();
3940 mediumLock.acquire();
3941 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3942 }
3943 rc = diff->init(mParent,
3944 medium->i_getPreferredDiffFormat(),
3945 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3946 uuidRegistryParent,
3947 DeviceType_HardDisk);
3948 if (FAILED(rc)) return rc;
3949
3950 /* Apply the normal locking logic to the entire chain. */
3951 MediumLockList *pMediumLockList(new MediumLockList());
3952 mediumLock.release();
3953 treeLock.release();
3954 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3955 diff /* pToLockWrite */,
3956 false /* fMediumLockWriteAll */,
3957 medium,
3958 *pMediumLockList);
3959 treeLock.acquire();
3960 mediumLock.acquire();
3961 if (SUCCEEDED(rc))
3962 {
3963 mediumLock.release();
3964 treeLock.release();
3965 rc = pMediumLockList->Lock();
3966 treeLock.acquire();
3967 mediumLock.acquire();
3968 if (FAILED(rc))
3969 setError(rc,
3970 tr("Could not lock medium when creating diff '%s'"),
3971 diff->i_getLocationFull().c_str());
3972 else
3973 {
3974 /* will release the lock before the potentially lengthy
3975 * operation, so protect with the special state */
3976 MachineState_T oldState = mData->mMachineState;
3977 i_setMachineState(MachineState_SettingUp);
3978
3979 mediumLock.release();
3980 treeLock.release();
3981 alock.release();
3982
3983 rc = medium->i_createDiffStorage(diff,
3984 medium->i_getPreferredDiffVariant(),
3985 pMediumLockList,
3986 NULL /* aProgress */,
3987 true /* aWait */,
3988 false /* aNotify */);
3989
3990 alock.acquire();
3991 treeLock.acquire();
3992 mediumLock.acquire();
3993
3994 i_setMachineState(oldState);
3995 }
3996 }
3997
3998 /* Unlock the media and free the associated memory. */
3999 delete pMediumLockList;
4000
4001 if (FAILED(rc)) return rc;
4002
4003 /* use the created diff for the actual attachment */
4004 medium = diff;
4005 mediumCaller.attach(medium);
4006 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4007 mediumLock.attach(medium);
4008 }
4009 while (0);
4010
4011 ComObjPtr<MediumAttachment> attachment;
4012 attachment.createObject();
4013 rc = attachment->init(this,
4014 medium,
4015 aName,
4016 aControllerPort,
4017 aDevice,
4018 aType,
4019 fIndirect,
4020 false /* fPassthrough */,
4021 false /* fTempEject */,
4022 false /* fNonRotational */,
4023 false /* fDiscard */,
4024 fHotplug /* fHotPluggable */,
4025 Utf8Str::Empty);
4026 if (FAILED(rc)) return rc;
4027
4028 if (associate && !medium.isNull())
4029 {
4030 // as the last step, associate the medium to the VM
4031 rc = medium->i_addBackReference(mData->mUuid);
4032 // here we can fail because of Deleting, or being in process of creating a Diff
4033 if (FAILED(rc)) return rc;
4034
4035 mediumLock.release();
4036 treeLock.release();
4037 alock.release();
4038 i_addMediumToRegistry(medium);
4039 alock.acquire();
4040 treeLock.acquire();
4041 mediumLock.acquire();
4042 }
4043
4044 /* success: finally remember the attachment */
4045 i_setModified(IsModified_Storage);
4046 mMediumAttachments.backup();
4047 mMediumAttachments->push_back(attachment);
4048
4049 mediumLock.release();
4050 treeLock.release();
4051 alock.release();
4052
4053 if (fHotplug || fSilent)
4054 {
4055 if (!medium.isNull())
4056 {
4057 MediumLockList *pMediumLockList(new MediumLockList());
4058
4059 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4060 medium /* pToLockWrite */,
4061 false /* fMediumLockWriteAll */,
4062 NULL,
4063 *pMediumLockList);
4064 alock.acquire();
4065 if (FAILED(rc))
4066 delete pMediumLockList;
4067 else
4068 {
4069 mData->mSession.mLockedMedia.Unlock();
4070 alock.release();
4071 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4072 mData->mSession.mLockedMedia.Lock();
4073 alock.acquire();
4074 }
4075 alock.release();
4076 }
4077
4078 if (SUCCEEDED(rc))
4079 {
4080 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4081 /* Remove lock list in case of error. */
4082 if (FAILED(rc))
4083 {
4084 mData->mSession.mLockedMedia.Unlock();
4085 mData->mSession.mLockedMedia.Remove(attachment);
4086 mData->mSession.mLockedMedia.Lock();
4087 }
4088 }
4089 }
4090
4091 /* Save modified registries, but skip this machine as it's the caller's
4092 * job to save its settings like all other settings changes. */
4093 mParent->i_unmarkRegistryModified(i_getId());
4094 mParent->i_saveModifiedRegistries();
4095
4096 if (SUCCEEDED(rc))
4097 {
4098 if (fIndirect && medium != aM)
4099 mParent->i_onMediumConfigChanged(medium);
4100 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4101 }
4102
4103 return rc;
4104}
4105
4106HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4107 LONG aDevice)
4108{
4109 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4110 aName.c_str(), aControllerPort, aDevice));
4111
4112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4113
4114 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4115 if (FAILED(rc)) return rc;
4116
4117 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4118
4119 /* Check for an existing controller. */
4120 ComObjPtr<StorageController> ctl;
4121 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4122 if (FAILED(rc)) return rc;
4123
4124 StorageControllerType_T ctrlType;
4125 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4126 if (FAILED(rc))
4127 return setError(E_FAIL,
4128 tr("Could not get type of controller '%s'"),
4129 aName.c_str());
4130
4131 bool fSilent = false;
4132 Utf8Str strReconfig;
4133
4134 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4135 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4136 if ( mData->mMachineState == MachineState_Paused
4137 && strReconfig == "1")
4138 fSilent = true;
4139
4140 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4141 bool fHotplug = false;
4142 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4143 fHotplug = true;
4144
4145 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4146 return setError(VBOX_E_INVALID_VM_STATE,
4147 tr("Controller '%s' does not support hotplugging"),
4148 aName.c_str());
4149
4150 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4151 aName,
4152 aControllerPort,
4153 aDevice);
4154 if (!pAttach)
4155 return setError(VBOX_E_OBJECT_NOT_FOUND,
4156 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4157 aDevice, aControllerPort, aName.c_str());
4158
4159 if (fHotplug && !pAttach->i_getHotPluggable())
4160 return setError(VBOX_E_NOT_SUPPORTED,
4161 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4162 aDevice, aControllerPort, aName.c_str());
4163
4164 /*
4165 * The VM has to detach the device before we delete any implicit diffs.
4166 * If this fails we can roll back without loosing data.
4167 */
4168 if (fHotplug || fSilent)
4169 {
4170 alock.release();
4171 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4172 alock.acquire();
4173 }
4174 if (FAILED(rc)) return rc;
4175
4176 /* If we are here everything went well and we can delete the implicit now. */
4177 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4178
4179 alock.release();
4180
4181 /* Save modified registries, but skip this machine as it's the caller's
4182 * job to save its settings like all other settings changes. */
4183 mParent->i_unmarkRegistryModified(i_getId());
4184 mParent->i_saveModifiedRegistries();
4185
4186 if (SUCCEEDED(rc))
4187 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4188
4189 return rc;
4190}
4191
4192HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4193 LONG aDevice, BOOL aPassthrough)
4194{
4195 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4196 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4197
4198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4199
4200 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4201 if (FAILED(rc)) return rc;
4202
4203 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4204
4205 /* Check for an existing controller. */
4206 ComObjPtr<StorageController> ctl;
4207 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4208 if (FAILED(rc)) return rc;
4209
4210 StorageControllerType_T ctrlType;
4211 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4212 if (FAILED(rc))
4213 return setError(E_FAIL,
4214 tr("Could not get type of controller '%s'"),
4215 aName.c_str());
4216
4217 bool fSilent = false;
4218 Utf8Str strReconfig;
4219
4220 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4221 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4222 if ( mData->mMachineState == MachineState_Paused
4223 && strReconfig == "1")
4224 fSilent = true;
4225
4226 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4227 bool fHotplug = false;
4228 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4229 fHotplug = true;
4230
4231 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4232 return setError(VBOX_E_INVALID_VM_STATE,
4233 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4234 aName.c_str());
4235
4236 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4237 aName,
4238 aControllerPort,
4239 aDevice);
4240 if (!pAttach)
4241 return setError(VBOX_E_OBJECT_NOT_FOUND,
4242 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4243 aDevice, aControllerPort, aName.c_str());
4244
4245
4246 i_setModified(IsModified_Storage);
4247 mMediumAttachments.backup();
4248
4249 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4250
4251 if (pAttach->i_getType() != DeviceType_DVD)
4252 return setError(E_INVALIDARG,
4253 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4254 aDevice, aControllerPort, aName.c_str());
4255
4256 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4257
4258 pAttach->i_updatePassthrough(!!aPassthrough);
4259
4260 attLock.release();
4261 alock.release();
4262 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4263 if (SUCCEEDED(rc) && fValueChanged)
4264 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4265
4266 return rc;
4267}
4268
4269HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4270 LONG aDevice, BOOL aTemporaryEject)
4271{
4272
4273 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4274 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4275
4276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4277
4278 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4279 if (FAILED(rc)) return rc;
4280
4281 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4282 aName,
4283 aControllerPort,
4284 aDevice);
4285 if (!pAttach)
4286 return setError(VBOX_E_OBJECT_NOT_FOUND,
4287 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4288 aDevice, aControllerPort, aName.c_str());
4289
4290
4291 i_setModified(IsModified_Storage);
4292 mMediumAttachments.backup();
4293
4294 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4295
4296 if (pAttach->i_getType() != DeviceType_DVD)
4297 return setError(E_INVALIDARG,
4298 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4299 aDevice, aControllerPort, aName.c_str());
4300 pAttach->i_updateTempEject(!!aTemporaryEject);
4301
4302 return S_OK;
4303}
4304
4305HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4306 LONG aDevice, BOOL aNonRotational)
4307{
4308
4309 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4310 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4311
4312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4313
4314 HRESULT rc = i_checkStateDependency(MutableStateDep);
4315 if (FAILED(rc)) return rc;
4316
4317 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4318
4319 if (Global::IsOnlineOrTransient(mData->mMachineState))
4320 return setError(VBOX_E_INVALID_VM_STATE,
4321 tr("Invalid machine state: %s"),
4322 Global::stringifyMachineState(mData->mMachineState));
4323
4324 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4325 aName,
4326 aControllerPort,
4327 aDevice);
4328 if (!pAttach)
4329 return setError(VBOX_E_OBJECT_NOT_FOUND,
4330 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4331 aDevice, aControllerPort, aName.c_str());
4332
4333
4334 i_setModified(IsModified_Storage);
4335 mMediumAttachments.backup();
4336
4337 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4338
4339 if (pAttach->i_getType() != DeviceType_HardDisk)
4340 return setError(E_INVALIDARG,
4341 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"),
4342 aDevice, aControllerPort, aName.c_str());
4343 pAttach->i_updateNonRotational(!!aNonRotational);
4344
4345 return S_OK;
4346}
4347
4348HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4349 LONG aDevice, BOOL aDiscard)
4350{
4351
4352 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4353 aName.c_str(), aControllerPort, aDevice, aDiscard));
4354
4355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4356
4357 HRESULT rc = i_checkStateDependency(MutableStateDep);
4358 if (FAILED(rc)) return rc;
4359
4360 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4361
4362 if (Global::IsOnlineOrTransient(mData->mMachineState))
4363 return setError(VBOX_E_INVALID_VM_STATE,
4364 tr("Invalid machine state: %s"),
4365 Global::stringifyMachineState(mData->mMachineState));
4366
4367 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4368 aName,
4369 aControllerPort,
4370 aDevice);
4371 if (!pAttach)
4372 return setError(VBOX_E_OBJECT_NOT_FOUND,
4373 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4374 aDevice, aControllerPort, aName.c_str());
4375
4376
4377 i_setModified(IsModified_Storage);
4378 mMediumAttachments.backup();
4379
4380 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4381
4382 if (pAttach->i_getType() != DeviceType_HardDisk)
4383 return setError(E_INVALIDARG,
4384 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"),
4385 aDevice, aControllerPort, aName.c_str());
4386 pAttach->i_updateDiscard(!!aDiscard);
4387
4388 return S_OK;
4389}
4390
4391HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4392 LONG aDevice, BOOL aHotPluggable)
4393{
4394 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4395 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4396
4397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4398
4399 HRESULT rc = i_checkStateDependency(MutableStateDep);
4400 if (FAILED(rc)) return rc;
4401
4402 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4410 aName,
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418 /* Check for an existing controller. */
4419 ComObjPtr<StorageController> ctl;
4420 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4421 if (FAILED(rc)) return rc;
4422
4423 StorageControllerType_T ctrlType;
4424 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4425 if (FAILED(rc))
4426 return setError(E_FAIL,
4427 tr("Could not get type of controller '%s'"),
4428 aName.c_str());
4429
4430 if (!i_isControllerHotplugCapable(ctrlType))
4431 return setError(VBOX_E_NOT_SUPPORTED,
4432 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4433 aName.c_str());
4434
4435 i_setModified(IsModified_Storage);
4436 mMediumAttachments.backup();
4437
4438 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4439
4440 if (pAttach->i_getType() == DeviceType_Floppy)
4441 return setError(E_INVALIDARG,
4442 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"),
4443 aDevice, aControllerPort, aName.c_str());
4444 pAttach->i_updateHotPluggable(!!aHotPluggable);
4445
4446 return S_OK;
4447}
4448
4449HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4450 LONG aDevice)
4451{
4452 int rc = S_OK;
4453 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4454 aName.c_str(), aControllerPort, aDevice));
4455
4456 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4457
4458 return rc;
4459}
4460
4461HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4462 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4463{
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4465 aName.c_str(), aControllerPort, aDevice));
4466
4467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4468
4469 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4470 if (FAILED(rc)) return rc;
4471
4472 if (Global::IsOnlineOrTransient(mData->mMachineState))
4473 return setError(VBOX_E_INVALID_VM_STATE,
4474 tr("Invalid machine state: %s"),
4475 Global::stringifyMachineState(mData->mMachineState));
4476
4477 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4478 aName,
4479 aControllerPort,
4480 aDevice);
4481 if (!pAttach)
4482 return setError(VBOX_E_OBJECT_NOT_FOUND,
4483 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4484 aDevice, aControllerPort, aName.c_str());
4485
4486
4487 i_setModified(IsModified_Storage);
4488 mMediumAttachments.backup();
4489
4490 IBandwidthGroup *iB = aBandwidthGroup;
4491 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4492 if (aBandwidthGroup && group.isNull())
4493 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4494
4495 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4496
4497 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4498 if (strBandwidthGroupOld.isNotEmpty())
4499 {
4500 /* Get the bandwidth group object and release it - this must not fail. */
4501 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4502 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4503 Assert(SUCCEEDED(rc));
4504
4505 pBandwidthGroupOld->i_release();
4506 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4507 }
4508
4509 if (!group.isNull())
4510 {
4511 group->i_reference();
4512 pAttach->i_updateBandwidthGroup(group->i_getName());
4513 }
4514
4515 return S_OK;
4516}
4517
4518HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4519 LONG aControllerPort,
4520 LONG aDevice,
4521 DeviceType_T aType)
4522{
4523 HRESULT rc = S_OK;
4524
4525 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4526 aName.c_str(), aControllerPort, aDevice, aType));
4527
4528 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4529
4530 return rc;
4531}
4532
4533
4534HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4535 LONG aControllerPort,
4536 LONG aDevice,
4537 BOOL aForce)
4538{
4539 int rc = S_OK;
4540 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4541 aName.c_str(), aControllerPort, aForce));
4542
4543 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4544
4545 return rc;
4546}
4547
4548HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4549 LONG aControllerPort,
4550 LONG aDevice,
4551 const ComPtr<IMedium> &aMedium,
4552 BOOL aForce)
4553{
4554 int rc = S_OK;
4555 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4556 aName.c_str(), aControllerPort, aDevice, aForce));
4557
4558 // request the host lock first, since might be calling Host methods for getting host drives;
4559 // next, protect the media tree all the while we're in here, as well as our member variables
4560 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4561 this->lockHandle(),
4562 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4563
4564 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4565 aName,
4566 aControllerPort,
4567 aDevice);
4568 if (pAttach.isNull())
4569 return setError(VBOX_E_OBJECT_NOT_FOUND,
4570 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4571 aDevice, aControllerPort, aName.c_str());
4572
4573 /* Remember previously mounted medium. The medium before taking the
4574 * backup is not necessarily the same thing. */
4575 ComObjPtr<Medium> oldmedium;
4576 oldmedium = pAttach->i_getMedium();
4577
4578 IMedium *iM = aMedium;
4579 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4580 if (aMedium && pMedium.isNull())
4581 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4582
4583 AutoCaller mediumCaller(pMedium);
4584 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4585
4586 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4587 if (pMedium)
4588 {
4589 DeviceType_T mediumType = pAttach->i_getType();
4590 switch (mediumType)
4591 {
4592 case DeviceType_DVD:
4593 case DeviceType_Floppy:
4594 break;
4595
4596 default:
4597 return setError(VBOX_E_INVALID_OBJECT_STATE,
4598 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4599 aControllerPort,
4600 aDevice,
4601 aName.c_str());
4602 }
4603 }
4604
4605 i_setModified(IsModified_Storage);
4606 mMediumAttachments.backup();
4607
4608 {
4609 // The backup operation makes the pAttach reference point to the
4610 // old settings. Re-get the correct reference.
4611 pAttach = i_findAttachment(*mMediumAttachments.data(),
4612 aName,
4613 aControllerPort,
4614 aDevice);
4615 if (!oldmedium.isNull())
4616 oldmedium->i_removeBackReference(mData->mUuid);
4617 if (!pMedium.isNull())
4618 {
4619 pMedium->i_addBackReference(mData->mUuid);
4620
4621 mediumLock.release();
4622 multiLock.release();
4623 i_addMediumToRegistry(pMedium);
4624 multiLock.acquire();
4625 mediumLock.acquire();
4626 }
4627
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629 pAttach->i_updateMedium(pMedium);
4630 }
4631
4632 i_setModified(IsModified_Storage);
4633
4634 mediumLock.release();
4635 multiLock.release();
4636 rc = i_onMediumChange(pAttach, aForce);
4637 multiLock.acquire();
4638 mediumLock.acquire();
4639
4640 /* On error roll back this change only. */
4641 if (FAILED(rc))
4642 {
4643 if (!pMedium.isNull())
4644 pMedium->i_removeBackReference(mData->mUuid);
4645 pAttach = i_findAttachment(*mMediumAttachments.data(),
4646 aName,
4647 aControllerPort,
4648 aDevice);
4649 /* If the attachment is gone in the meantime, bail out. */
4650 if (pAttach.isNull())
4651 return rc;
4652 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4653 if (!oldmedium.isNull())
4654 oldmedium->i_addBackReference(mData->mUuid);
4655 pAttach->i_updateMedium(oldmedium);
4656 }
4657
4658 mediumLock.release();
4659 multiLock.release();
4660
4661 /* Save modified registries, but skip this machine as it's the caller's
4662 * job to save its settings like all other settings changes. */
4663 mParent->i_unmarkRegistryModified(i_getId());
4664 mParent->i_saveModifiedRegistries();
4665
4666 return rc;
4667}
4668HRESULT Machine::getMedium(const com::Utf8Str &aName,
4669 LONG aControllerPort,
4670 LONG aDevice,
4671 ComPtr<IMedium> &aMedium)
4672{
4673 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4674 aName.c_str(), aControllerPort, aDevice));
4675
4676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4677
4678 aMedium = NULL;
4679
4680 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4681 aName,
4682 aControllerPort,
4683 aDevice);
4684 if (pAttach.isNull())
4685 return setError(VBOX_E_OBJECT_NOT_FOUND,
4686 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4687 aDevice, aControllerPort, aName.c_str());
4688
4689 aMedium = pAttach->i_getMedium();
4690
4691 return S_OK;
4692}
4693
4694HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4695{
4696
4697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4698
4699 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4700
4701 return S_OK;
4702}
4703
4704HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4705{
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4709
4710 return S_OK;
4711}
4712
4713HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4714{
4715 /* Do not assert if slot is out of range, just return the advertised
4716 status. testdriver/vbox.py triggers this in logVmInfo. */
4717 if (aSlot >= mNetworkAdapters.size())
4718 return setError(E_INVALIDARG,
4719 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4720 aSlot, mNetworkAdapters.size());
4721
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4725
4726 return S_OK;
4727}
4728
4729HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4730{
4731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4734 size_t i = 0;
4735 for (settings::StringsMap::const_iterator
4736 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4737 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4738 ++it, ++i)
4739 aKeys[i] = it->first;
4740
4741 return S_OK;
4742}
4743
4744 /**
4745 * @note Locks this object for reading.
4746 */
4747HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4748 com::Utf8Str &aValue)
4749{
4750 /* start with nothing found */
4751 aValue = "";
4752
4753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4754
4755 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4756 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4757 // found:
4758 aValue = it->second; // source is a Utf8Str
4759
4760 /* return the result to caller (may be empty) */
4761 return S_OK;
4762}
4763
4764 /**
4765 * @note Locks mParent for writing + this object for writing.
4766 */
4767HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4768{
4769 /* Because control characters in aKey have caused problems in the settings
4770 * they are rejected unless the key should be deleted. */
4771 if (!aValue.isEmpty())
4772 {
4773 for (size_t i = 0; i < aKey.length(); ++i)
4774 {
4775 char ch = aKey[i];
4776 if (RTLocCIsCntrl(ch))
4777 return E_INVALIDARG;
4778 }
4779 }
4780
4781 Utf8Str strOldValue; // empty
4782
4783 // locking note: we only hold the read lock briefly to look up the old value,
4784 // then release it and call the onExtraCanChange callbacks. There is a small
4785 // chance of a race insofar as the callback might be called twice if two callers
4786 // change the same key at the same time, but that's a much better solution
4787 // than the deadlock we had here before. The actual changing of the extradata
4788 // is then performed under the write lock and race-free.
4789
4790 // look up the old value first; if nothing has changed then we need not do anything
4791 {
4792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4793
4794 // For snapshots don't even think about allowing changes, extradata
4795 // is global for a machine, so there is nothing snapshot specific.
4796 if (i_isSnapshotMachine())
4797 return setError(VBOX_E_INVALID_VM_STATE,
4798 tr("Cannot set extradata for a snapshot"));
4799
4800 // check if the right IMachine instance is used
4801 if (mData->mRegistered && !i_isSessionMachine())
4802 return setError(VBOX_E_INVALID_VM_STATE,
4803 tr("Cannot set extradata for an immutable machine"));
4804
4805 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4806 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4807 strOldValue = it->second;
4808 }
4809
4810 bool fChanged;
4811 if ((fChanged = (strOldValue != aValue)))
4812 {
4813 // ask for permission from all listeners outside the locks;
4814 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4815 // lock to copy the list of callbacks to invoke
4816 Bstr error;
4817 Bstr bstrValue(aValue);
4818
4819 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4820 {
4821 const char *sep = error.isEmpty() ? "" : ": ";
4822 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4823 return setError(E_ACCESSDENIED,
4824 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4825 aKey.c_str(),
4826 aValue.c_str(),
4827 sep,
4828 error.raw());
4829 }
4830
4831 // data is changing and change not vetoed: then write it out under the lock
4832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 if (aValue.isEmpty())
4835 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4836 else
4837 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4838 // creates a new key if needed
4839
4840 bool fNeedsGlobalSaveSettings = false;
4841 // This saving of settings is tricky: there is no "old state" for the
4842 // extradata items at all (unlike all other settings), so the old/new
4843 // settings comparison would give a wrong result!
4844 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4845
4846 if (fNeedsGlobalSaveSettings)
4847 {
4848 // save the global settings; for that we should hold only the VirtualBox lock
4849 alock.release();
4850 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4851 mParent->i_saveSettings();
4852 }
4853 }
4854
4855 // fire notification outside the lock
4856 if (fChanged)
4857 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4858
4859 return S_OK;
4860}
4861
4862HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4863{
4864 aProgress = NULL;
4865 NOREF(aSettingsFilePath);
4866 ReturnComNotImplemented();
4867}
4868
4869HRESULT Machine::saveSettings()
4870{
4871 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /* the settings file path may never be null */
4877 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4878
4879 /* save all VM data excluding snapshots */
4880 bool fNeedsGlobalSaveSettings = false;
4881 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4882 mlock.release();
4883
4884 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4885 {
4886 // save the global settings; for that we should hold only the VirtualBox lock
4887 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4888 rc = mParent->i_saveSettings();
4889 }
4890
4891 return rc;
4892}
4893
4894
4895HRESULT Machine::discardSettings()
4896{
4897 /*
4898 * We need to take the machine list lock here as well as the machine one
4899 * or we'll get into trouble should any media stuff require rolling back.
4900 *
4901 * Details:
4902 *
4903 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4904 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4905 * 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]
4906 * 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
4907 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4908 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4909 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4910 * 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
4911 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4912 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4913 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4914 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4915 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4916 * 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]
4917 * 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] (*)
4918 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4919 * 0:005> k
4920 * # Child-SP RetAddr Call Site
4921 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4922 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4923 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4924 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4925 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4926 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4927 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4928 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4929 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4930 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4931 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4932 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4933 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4934 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4935 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4936 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4937 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4938 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4939 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4940 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4941 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4942 *
4943 */
4944 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4946
4947 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4948 if (FAILED(rc)) return rc;
4949
4950 /*
4951 * during this rollback, the session will be notified if data has
4952 * been actually changed
4953 */
4954 i_rollback(true /* aNotify */);
4955
4956 return S_OK;
4957}
4958
4959/** @note Locks objects! */
4960HRESULT Machine::unregister(AutoCaller &autoCaller,
4961 CleanupMode_T aCleanupMode,
4962 std::vector<ComPtr<IMedium> > &aMedia)
4963{
4964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 Guid id(i_getId());
4967
4968 if (mData->mSession.mState != SessionState_Unlocked)
4969 return setError(VBOX_E_INVALID_OBJECT_STATE,
4970 tr("Cannot unregister the machine '%s' while it is locked"),
4971 mUserData->s.strName.c_str());
4972
4973 // wait for state dependents to drop to zero
4974 i_ensureNoStateDependencies();
4975
4976 if (!mData->mAccessible)
4977 {
4978 // inaccessible maschines can only be unregistered; uninitialize ourselves
4979 // here because currently there may be no unregistered that are inaccessible
4980 // (this state combination is not supported). Note releasing the caller and
4981 // leaving the lock before calling uninit()
4982 alock.release();
4983 autoCaller.release();
4984
4985 uninit();
4986
4987 mParent->i_unregisterMachine(this, id);
4988 // calls VirtualBox::i_saveSettings()
4989
4990 return S_OK;
4991 }
4992
4993 HRESULT rc = S_OK;
4994
4995 /// @todo r=klaus this is stupid... why is the saved state always deleted?
4996 // discard saved state
4997 if (mData->mMachineState == MachineState_Saved)
4998 {
4999 // add the saved state file to the list of files the caller should delete
5000 Assert(!mSSData->strStateFilePath.isEmpty());
5001 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5002
5003 mSSData->strStateFilePath.setNull();
5004
5005 // unconditionally set the machine state to powered off, we now
5006 // know no session has locked the machine
5007 mData->mMachineState = MachineState_PoweredOff;
5008 }
5009
5010 size_t cSnapshots = 0;
5011 if (mData->mFirstSnapshot)
5012 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5013 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5014 // fail now before we start detaching media
5015 return setError(VBOX_E_INVALID_OBJECT_STATE,
5016 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5017 mUserData->s.strName.c_str(), cSnapshots);
5018
5019 // This list collects the medium objects from all medium attachments
5020 // which we will detach from the machine and its snapshots, in a specific
5021 // order which allows for closing all media without getting "media in use"
5022 // errors, simply by going through the list from the front to the back:
5023 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5024 // and must be closed before the parent media from the snapshots, or closing the parents
5025 // will fail because they still have children);
5026 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5027 // the root ("first") snapshot of the machine.
5028 MediaList llMedia;
5029
5030 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5031 && mMediumAttachments->size()
5032 )
5033 {
5034 // we have media attachments: detach them all and add the Medium objects to our list
5035 if (aCleanupMode != CleanupMode_UnregisterOnly)
5036 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5037 else
5038 return setError(VBOX_E_INVALID_OBJECT_STATE,
5039 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5040 mUserData->s.strName.c_str(), mMediumAttachments->size());
5041 }
5042
5043 if (cSnapshots)
5044 {
5045 // add the media from the medium attachments of the snapshots to llMedia
5046 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5047 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5048 // into the children first
5049
5050 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5051 MachineState_T oldState = mData->mMachineState;
5052 mData->mMachineState = MachineState_DeletingSnapshot;
5053
5054 // make a copy of the first snapshot so the refcount does not drop to 0
5055 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5056 // because of the AutoCaller voodoo)
5057 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5058
5059 // GO!
5060 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5061
5062 mData->mMachineState = oldState;
5063 }
5064
5065 if (FAILED(rc))
5066 {
5067 i_rollbackMedia();
5068 return rc;
5069 }
5070
5071 // commit all the media changes made above
5072 i_commitMedia();
5073
5074 mData->mRegistered = false;
5075
5076 // machine lock no longer needed
5077 alock.release();
5078
5079 // return media to caller
5080 aMedia.resize(llMedia.size());
5081 size_t i = 0;
5082 for (MediaList::const_iterator
5083 it = llMedia.begin();
5084 it != llMedia.end();
5085 ++it, ++i)
5086 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5087
5088 mParent->i_unregisterMachine(this, id);
5089 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5090
5091 return S_OK;
5092}
5093
5094/**
5095 * Task record for deleting a machine config.
5096 */
5097class Machine::DeleteConfigTask
5098 : public Machine::Task
5099{
5100public:
5101 DeleteConfigTask(Machine *m,
5102 Progress *p,
5103 const Utf8Str &t,
5104 const RTCList<ComPtr<IMedium> > &llMediums,
5105 const StringsList &llFilesToDelete)
5106 : Task(m, p, t),
5107 m_llMediums(llMediums),
5108 m_llFilesToDelete(llFilesToDelete)
5109 {}
5110
5111private:
5112 void handler()
5113 {
5114 try
5115 {
5116 m_pMachine->i_deleteConfigHandler(*this);
5117 }
5118 catch (...)
5119 {
5120 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5121 }
5122 }
5123
5124 RTCList<ComPtr<IMedium> > m_llMediums;
5125 StringsList m_llFilesToDelete;
5126
5127 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5128};
5129
5130/**
5131 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5132 * SessionMachine::taskHandler().
5133 *
5134 * @note Locks this object for writing.
5135 *
5136 * @param task
5137 * @return
5138 */
5139void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5140{
5141 LogFlowThisFuncEnter();
5142
5143 AutoCaller autoCaller(this);
5144 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5145 if (FAILED(autoCaller.rc()))
5146 {
5147 /* we might have been uninitialized because the session was accidentally
5148 * closed by the client, so don't assert */
5149 HRESULT rc = setError(E_FAIL,
5150 tr("The session has been accidentally closed"));
5151 task.m_pProgress->i_notifyComplete(rc);
5152 LogFlowThisFuncLeave();
5153 return;
5154 }
5155
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HRESULT rc = S_OK;
5159
5160 try
5161 {
5162 ULONG uLogHistoryCount = 3;
5163 ComPtr<ISystemProperties> systemProperties;
5164 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5165 if (FAILED(rc)) throw rc;
5166
5167 if (!systemProperties.isNull())
5168 {
5169 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5170 if (FAILED(rc)) throw rc;
5171 }
5172
5173 MachineState_T oldState = mData->mMachineState;
5174 i_setMachineState(MachineState_SettingUp);
5175 alock.release();
5176 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5177 {
5178 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5179 {
5180 AutoCaller mac(pMedium);
5181 if (FAILED(mac.rc())) throw mac.rc();
5182 Utf8Str strLocation = pMedium->i_getLocationFull();
5183 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5184 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5185 if (FAILED(rc)) throw rc;
5186 }
5187 if (pMedium->i_isMediumFormatFile())
5188 {
5189 ComPtr<IProgress> pProgress2;
5190 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5191 if (FAILED(rc)) throw rc;
5192 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5193 if (FAILED(rc)) throw rc;
5194 }
5195
5196 /* Close the medium, deliberately without checking the return
5197 * code, and without leaving any trace in the error info, as
5198 * a failure here is a very minor issue, which shouldn't happen
5199 * as above we even managed to delete the medium. */
5200 {
5201 ErrorInfoKeeper eik;
5202 pMedium->Close();
5203 }
5204 }
5205 i_setMachineState(oldState);
5206 alock.acquire();
5207
5208 // delete the files pushed on the task list by Machine::Delete()
5209 // (this includes saved states of the machine and snapshots and
5210 // medium storage files from the IMedium list passed in, and the
5211 // machine XML file)
5212 for (StringsList::const_iterator
5213 it = task.m_llFilesToDelete.begin();
5214 it != task.m_llFilesToDelete.end();
5215 ++it)
5216 {
5217 const Utf8Str &strFile = *it;
5218 LogFunc(("Deleting file %s\n", strFile.c_str()));
5219 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5220 if (FAILED(rc)) throw rc;
5221
5222 int vrc = RTFileDelete(strFile.c_str());
5223 if (RT_FAILURE(vrc))
5224 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5225 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5226 }
5227
5228 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5229 if (FAILED(rc)) throw rc;
5230
5231 /* delete the settings only when the file actually exists */
5232 if (mData->pMachineConfigFile->fileExists())
5233 {
5234 /* Delete any backup or uncommitted XML files. Ignore failures.
5235 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5236 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5237 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5238 RTFileDelete(otherXml.c_str());
5239 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5240 RTFileDelete(otherXml.c_str());
5241
5242 /* delete the Logs folder, nothing important should be left
5243 * there (we don't check for errors because the user might have
5244 * some private files there that we don't want to delete) */
5245 Utf8Str logFolder;
5246 getLogFolder(logFolder);
5247 Assert(logFolder.length());
5248 if (RTDirExists(logFolder.c_str()))
5249 {
5250 /* Delete all VBox.log[.N] files from the Logs folder
5251 * (this must be in sync with the rotation logic in
5252 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5253 * files that may have been created by the GUI. */
5254 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5255 logFolder.c_str(), RTPATH_DELIMITER);
5256 RTFileDelete(log.c_str());
5257 log = Utf8StrFmt("%s%cVBox.png",
5258 logFolder.c_str(), RTPATH_DELIMITER);
5259 RTFileDelete(log.c_str());
5260 for (int i = uLogHistoryCount; i > 0; i--)
5261 {
5262 log = Utf8StrFmt("%s%cVBox.log.%d",
5263 logFolder.c_str(), RTPATH_DELIMITER, i);
5264 RTFileDelete(log.c_str());
5265 log = Utf8StrFmt("%s%cVBox.png.%d",
5266 logFolder.c_str(), RTPATH_DELIMITER, i);
5267 RTFileDelete(log.c_str());
5268 }
5269#if defined(RT_OS_WINDOWS)
5270 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5271 RTFileDelete(log.c_str());
5272 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5273 RTFileDelete(log.c_str());
5274#endif
5275
5276 RTDirRemove(logFolder.c_str());
5277 }
5278
5279 /* delete the Snapshots folder, nothing important should be left
5280 * there (we don't check for errors because the user might have
5281 * some private files there that we don't want to delete) */
5282 Utf8Str strFullSnapshotFolder;
5283 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5284 Assert(!strFullSnapshotFolder.isEmpty());
5285 if (RTDirExists(strFullSnapshotFolder.c_str()))
5286 RTDirRemove(strFullSnapshotFolder.c_str());
5287
5288 // delete the directory that contains the settings file, but only
5289 // if it matches the VM name
5290 Utf8Str settingsDir;
5291 if (i_isInOwnDir(&settingsDir))
5292 RTDirRemove(settingsDir.c_str());
5293 }
5294
5295 alock.release();
5296
5297 mParent->i_saveModifiedRegistries();
5298 }
5299 catch (HRESULT aRC) { rc = aRC; }
5300
5301 task.m_pProgress->i_notifyComplete(rc);
5302
5303 LogFlowThisFuncLeave();
5304}
5305
5306HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5307{
5308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5309
5310 HRESULT rc = i_checkStateDependency(MutableStateDep);
5311 if (FAILED(rc)) return rc;
5312
5313 if (mData->mRegistered)
5314 return setError(VBOX_E_INVALID_VM_STATE,
5315 tr("Cannot delete settings of a registered machine"));
5316
5317 // collect files to delete
5318 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5319 if (mData->pMachineConfigFile->fileExists())
5320 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5321
5322 RTCList<ComPtr<IMedium> > llMediums;
5323 for (size_t i = 0; i < aMedia.size(); ++i)
5324 {
5325 IMedium *pIMedium(aMedia[i]);
5326 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5327 if (pMedium.isNull())
5328 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5329 SafeArray<BSTR> ids;
5330 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5331 if (FAILED(rc)) return rc;
5332 /* At this point the medium should not have any back references
5333 * anymore. If it has it is attached to another VM and *must* not
5334 * deleted. */
5335 if (ids.size() < 1)
5336 llMediums.append(pMedium);
5337 }
5338
5339 ComObjPtr<Progress> pProgress;
5340 pProgress.createObject();
5341 rc = pProgress->init(i_getVirtualBox(),
5342 static_cast<IMachine*>(this) /* aInitiator */,
5343 tr("Deleting files"),
5344 true /* fCancellable */,
5345 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5346 tr("Collecting file inventory"));
5347 if (FAILED(rc))
5348 return rc;
5349
5350 /* create and start the task on a separate thread (note that it will not
5351 * start working until we release alock) */
5352 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5353 rc = pTask->createThread();
5354 pTask = NULL;
5355 if (FAILED(rc))
5356 return rc;
5357
5358 pProgress.queryInterfaceTo(aProgress.asOutParam());
5359
5360 LogFlowFuncLeave();
5361
5362 return S_OK;
5363}
5364
5365HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5366{
5367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5368
5369 ComObjPtr<Snapshot> pSnapshot;
5370 HRESULT rc;
5371
5372 if (aNameOrId.isEmpty())
5373 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5374 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5375 else
5376 {
5377 Guid uuid(aNameOrId);
5378 if (uuid.isValid())
5379 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5380 else
5381 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5382 }
5383 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5384
5385 return rc;
5386}
5387
5388HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5389 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5390{
5391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5392
5393 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5394 if (FAILED(rc)) return rc;
5395
5396 ComObjPtr<SharedFolder> sharedFolder;
5397 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5398 if (SUCCEEDED(rc))
5399 return setError(VBOX_E_OBJECT_IN_USE,
5400 tr("Shared folder named '%s' already exists"),
5401 aName.c_str());
5402
5403 sharedFolder.createObject();
5404 rc = sharedFolder->init(i_getMachine(),
5405 aName,
5406 aHostPath,
5407 !!aWritable,
5408 !!aAutomount,
5409 aAutoMountPoint,
5410 true /* fFailOnError */);
5411 if (FAILED(rc)) return rc;
5412
5413 i_setModified(IsModified_SharedFolders);
5414 mHWData.backup();
5415 mHWData->mSharedFolders.push_back(sharedFolder);
5416
5417 /* inform the direct session if any */
5418 alock.release();
5419 i_onSharedFolderChange();
5420
5421 return S_OK;
5422}
5423
5424HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5425{
5426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5427
5428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5429 if (FAILED(rc)) return rc;
5430
5431 ComObjPtr<SharedFolder> sharedFolder;
5432 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5433 if (FAILED(rc)) return rc;
5434
5435 i_setModified(IsModified_SharedFolders);
5436 mHWData.backup();
5437 mHWData->mSharedFolders.remove(sharedFolder);
5438
5439 /* inform the direct session if any */
5440 alock.release();
5441 i_onSharedFolderChange();
5442
5443 return S_OK;
5444}
5445
5446HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5447{
5448 /* start with No */
5449 *aCanShow = FALSE;
5450
5451 ComPtr<IInternalSessionControl> directControl;
5452 {
5453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5454
5455 if (mData->mSession.mState != SessionState_Locked)
5456 return setError(VBOX_E_INVALID_VM_STATE,
5457 tr("Machine is not locked for session (session state: %s)"),
5458 Global::stringifySessionState(mData->mSession.mState));
5459
5460 if (mData->mSession.mLockType == LockType_VM)
5461 directControl = mData->mSession.mDirectControl;
5462 }
5463
5464 /* ignore calls made after #OnSessionEnd() is called */
5465 if (!directControl)
5466 return S_OK;
5467
5468 LONG64 dummy;
5469 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5470}
5471
5472HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5473{
5474 ComPtr<IInternalSessionControl> directControl;
5475 {
5476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5477
5478 if (mData->mSession.mState != SessionState_Locked)
5479 return setError(E_FAIL,
5480 tr("Machine is not locked for session (session state: %s)"),
5481 Global::stringifySessionState(mData->mSession.mState));
5482
5483 if (mData->mSession.mLockType == LockType_VM)
5484 directControl = mData->mSession.mDirectControl;
5485 }
5486
5487 /* ignore calls made after #OnSessionEnd() is called */
5488 if (!directControl)
5489 return S_OK;
5490
5491 BOOL dummy;
5492 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5493}
5494
5495#ifdef VBOX_WITH_GUEST_PROPS
5496/**
5497 * Look up a guest property in VBoxSVC's internal structures.
5498 */
5499HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5500 com::Utf8Str &aValue,
5501 LONG64 *aTimestamp,
5502 com::Utf8Str &aFlags) const
5503{
5504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5505
5506 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5507 if (it != mHWData->mGuestProperties.end())
5508 {
5509 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5510 aValue = it->second.strValue;
5511 *aTimestamp = it->second.mTimestamp;
5512 GuestPropWriteFlags(it->second.mFlags, szFlags);
5513 aFlags = Utf8Str(szFlags);
5514 }
5515
5516 return S_OK;
5517}
5518
5519/**
5520 * Query the VM that a guest property belongs to for the property.
5521 * @returns E_ACCESSDENIED if the VM process is not available or not
5522 * currently handling queries and the lookup should then be done in
5523 * VBoxSVC.
5524 */
5525HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5526 com::Utf8Str &aValue,
5527 LONG64 *aTimestamp,
5528 com::Utf8Str &aFlags) const
5529{
5530 HRESULT rc = S_OK;
5531 Bstr bstrValue;
5532 Bstr bstrFlags;
5533
5534 ComPtr<IInternalSessionControl> directControl;
5535 {
5536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5537 if (mData->mSession.mLockType == LockType_VM)
5538 directControl = mData->mSession.mDirectControl;
5539 }
5540
5541 /* ignore calls made after #OnSessionEnd() is called */
5542 if (!directControl)
5543 rc = E_ACCESSDENIED;
5544 else
5545 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5546 0 /* accessMode */,
5547 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5548
5549 aValue = bstrValue;
5550 aFlags = bstrFlags;
5551
5552 return rc;
5553}
5554#endif // VBOX_WITH_GUEST_PROPS
5555
5556HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5557 com::Utf8Str &aValue,
5558 LONG64 *aTimestamp,
5559 com::Utf8Str &aFlags)
5560{
5561#ifndef VBOX_WITH_GUEST_PROPS
5562 ReturnComNotImplemented();
5563#else // VBOX_WITH_GUEST_PROPS
5564
5565 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5566
5567 if (rc == E_ACCESSDENIED)
5568 /* The VM is not running or the service is not (yet) accessible */
5569 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5570 return rc;
5571#endif // VBOX_WITH_GUEST_PROPS
5572}
5573
5574HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5575{
5576 LONG64 dummyTimestamp;
5577 com::Utf8Str dummyFlags;
5578 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5579 return rc;
5580
5581}
5582HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5583{
5584 com::Utf8Str dummyFlags;
5585 com::Utf8Str dummyValue;
5586 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5587 return rc;
5588}
5589
5590#ifdef VBOX_WITH_GUEST_PROPS
5591/**
5592 * Set a guest property in VBoxSVC's internal structures.
5593 */
5594HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5595 const com::Utf8Str &aFlags, bool fDelete)
5596{
5597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5598 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5599 if (FAILED(rc)) return rc;
5600
5601 try
5602 {
5603 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5604 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5605 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5606
5607 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5608 if (it == mHWData->mGuestProperties.end())
5609 {
5610 if (!fDelete)
5611 {
5612 i_setModified(IsModified_MachineData);
5613 mHWData.backupEx();
5614
5615 RTTIMESPEC time;
5616 HWData::GuestProperty prop;
5617 prop.strValue = Bstr(aValue).raw();
5618 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5619 prop.mFlags = fFlags;
5620 mHWData->mGuestProperties[aName] = prop;
5621 }
5622 }
5623 else
5624 {
5625 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5626 {
5627 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5628 }
5629 else
5630 {
5631 i_setModified(IsModified_MachineData);
5632 mHWData.backupEx();
5633
5634 /* The backupEx() operation invalidates our iterator,
5635 * so get a new one. */
5636 it = mHWData->mGuestProperties.find(aName);
5637 Assert(it != mHWData->mGuestProperties.end());
5638
5639 if (!fDelete)
5640 {
5641 RTTIMESPEC time;
5642 it->second.strValue = aValue;
5643 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5644 it->second.mFlags = fFlags;
5645 }
5646 else
5647 mHWData->mGuestProperties.erase(it);
5648 }
5649 }
5650
5651 if (SUCCEEDED(rc))
5652 {
5653 alock.release();
5654
5655 mParent->i_onGuestPropertyChange(mData->mUuid,
5656 Bstr(aName).raw(),
5657 Bstr(aValue).raw(),
5658 Bstr(aFlags).raw());
5659 }
5660 }
5661 catch (std::bad_alloc &)
5662 {
5663 rc = E_OUTOFMEMORY;
5664 }
5665
5666 return rc;
5667}
5668
5669/**
5670 * Set a property on the VM that that property belongs to.
5671 * @returns E_ACCESSDENIED if the VM process is not available or not
5672 * currently handling queries and the setting should then be done in
5673 * VBoxSVC.
5674 */
5675HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5676 const com::Utf8Str &aFlags, bool fDelete)
5677{
5678 HRESULT rc;
5679
5680 try
5681 {
5682 ComPtr<IInternalSessionControl> directControl;
5683 {
5684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5685 if (mData->mSession.mLockType == LockType_VM)
5686 directControl = mData->mSession.mDirectControl;
5687 }
5688
5689 Bstr dummy1; /* will not be changed (setter) */
5690 Bstr dummy2; /* will not be changed (setter) */
5691 LONG64 dummy64;
5692 if (!directControl)
5693 rc = E_ACCESSDENIED;
5694 else
5695 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5696 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5697 fDelete ? 2 : 1 /* accessMode */,
5698 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5699 }
5700 catch (std::bad_alloc &)
5701 {
5702 rc = E_OUTOFMEMORY;
5703 }
5704
5705 return rc;
5706}
5707#endif // VBOX_WITH_GUEST_PROPS
5708
5709HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5710 const com::Utf8Str &aFlags)
5711{
5712#ifndef VBOX_WITH_GUEST_PROPS
5713 ReturnComNotImplemented();
5714#else // VBOX_WITH_GUEST_PROPS
5715 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5716 if (rc == E_ACCESSDENIED)
5717 /* The VM is not running or the service is not (yet) accessible */
5718 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5719 return rc;
5720#endif // VBOX_WITH_GUEST_PROPS
5721}
5722
5723HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5724{
5725 return setGuestProperty(aProperty, aValue, "");
5726}
5727
5728HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5729{
5730#ifndef VBOX_WITH_GUEST_PROPS
5731 ReturnComNotImplemented();
5732#else // VBOX_WITH_GUEST_PROPS
5733 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5734 if (rc == E_ACCESSDENIED)
5735 /* The VM is not running or the service is not (yet) accessible */
5736 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5737 return rc;
5738#endif // VBOX_WITH_GUEST_PROPS
5739}
5740
5741#ifdef VBOX_WITH_GUEST_PROPS
5742/**
5743 * Enumerate the guest properties in VBoxSVC's internal structures.
5744 */
5745HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5746 std::vector<com::Utf8Str> &aNames,
5747 std::vector<com::Utf8Str> &aValues,
5748 std::vector<LONG64> &aTimestamps,
5749 std::vector<com::Utf8Str> &aFlags)
5750{
5751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5752 Utf8Str strPatterns(aPatterns);
5753
5754 /*
5755 * Look for matching patterns and build up a list.
5756 */
5757 HWData::GuestPropertyMap propMap;
5758 for (HWData::GuestPropertyMap::const_iterator
5759 it = mHWData->mGuestProperties.begin();
5760 it != mHWData->mGuestProperties.end();
5761 ++it)
5762 {
5763 if ( strPatterns.isEmpty()
5764 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5765 RTSTR_MAX,
5766 it->first.c_str(),
5767 RTSTR_MAX,
5768 NULL)
5769 )
5770 propMap.insert(*it);
5771 }
5772
5773 alock.release();
5774
5775 /*
5776 * And build up the arrays for returning the property information.
5777 */
5778 size_t cEntries = propMap.size();
5779
5780 aNames.resize(cEntries);
5781 aValues.resize(cEntries);
5782 aTimestamps.resize(cEntries);
5783 aFlags.resize(cEntries);
5784
5785 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5786 size_t i = 0;
5787 for (HWData::GuestPropertyMap::const_iterator
5788 it = propMap.begin();
5789 it != propMap.end();
5790 ++it, ++i)
5791 {
5792 aNames[i] = it->first;
5793 aValues[i] = it->second.strValue;
5794 aTimestamps[i] = it->second.mTimestamp;
5795 GuestPropWriteFlags(it->second.mFlags, szFlags);
5796 aFlags[i] = Utf8Str(szFlags);
5797 }
5798
5799 return S_OK;
5800}
5801
5802/**
5803 * Enumerate the properties managed by a VM.
5804 * @returns E_ACCESSDENIED if the VM process is not available or not
5805 * currently handling queries and the setting should then be done in
5806 * VBoxSVC.
5807 */
5808HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5809 std::vector<com::Utf8Str> &aNames,
5810 std::vector<com::Utf8Str> &aValues,
5811 std::vector<LONG64> &aTimestamps,
5812 std::vector<com::Utf8Str> &aFlags)
5813{
5814 HRESULT rc;
5815 ComPtr<IInternalSessionControl> directControl;
5816 {
5817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5818 if (mData->mSession.mLockType == LockType_VM)
5819 directControl = mData->mSession.mDirectControl;
5820 }
5821
5822 com::SafeArray<BSTR> bNames;
5823 com::SafeArray<BSTR> bValues;
5824 com::SafeArray<LONG64> bTimestamps;
5825 com::SafeArray<BSTR> bFlags;
5826
5827 if (!directControl)
5828 rc = E_ACCESSDENIED;
5829 else
5830 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5831 ComSafeArrayAsOutParam(bNames),
5832 ComSafeArrayAsOutParam(bValues),
5833 ComSafeArrayAsOutParam(bTimestamps),
5834 ComSafeArrayAsOutParam(bFlags));
5835 size_t i;
5836 aNames.resize(bNames.size());
5837 for (i = 0; i < bNames.size(); ++i)
5838 aNames[i] = Utf8Str(bNames[i]);
5839 aValues.resize(bValues.size());
5840 for (i = 0; i < bValues.size(); ++i)
5841 aValues[i] = Utf8Str(bValues[i]);
5842 aTimestamps.resize(bTimestamps.size());
5843 for (i = 0; i < bTimestamps.size(); ++i)
5844 aTimestamps[i] = bTimestamps[i];
5845 aFlags.resize(bFlags.size());
5846 for (i = 0; i < bFlags.size(); ++i)
5847 aFlags[i] = Utf8Str(bFlags[i]);
5848
5849 return rc;
5850}
5851#endif // VBOX_WITH_GUEST_PROPS
5852HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5853 std::vector<com::Utf8Str> &aNames,
5854 std::vector<com::Utf8Str> &aValues,
5855 std::vector<LONG64> &aTimestamps,
5856 std::vector<com::Utf8Str> &aFlags)
5857{
5858#ifndef VBOX_WITH_GUEST_PROPS
5859 ReturnComNotImplemented();
5860#else // VBOX_WITH_GUEST_PROPS
5861
5862 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5863
5864 if (rc == E_ACCESSDENIED)
5865 /* The VM is not running or the service is not (yet) accessible */
5866 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5867 return rc;
5868#endif // VBOX_WITH_GUEST_PROPS
5869}
5870
5871HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5872 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5873{
5874 MediumAttachmentList atts;
5875
5876 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5877 if (FAILED(rc)) return rc;
5878
5879 aMediumAttachments.resize(atts.size());
5880 size_t i = 0;
5881 for (MediumAttachmentList::const_iterator
5882 it = atts.begin();
5883 it != atts.end();
5884 ++it, ++i)
5885 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5886
5887 return S_OK;
5888}
5889
5890HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5891 LONG aControllerPort,
5892 LONG aDevice,
5893 ComPtr<IMediumAttachment> &aAttachment)
5894{
5895 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5896 aName.c_str(), aControllerPort, aDevice));
5897
5898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5899
5900 aAttachment = NULL;
5901
5902 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5903 aName,
5904 aControllerPort,
5905 aDevice);
5906 if (pAttach.isNull())
5907 return setError(VBOX_E_OBJECT_NOT_FOUND,
5908 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5909 aDevice, aControllerPort, aName.c_str());
5910
5911 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5912
5913 return S_OK;
5914}
5915
5916
5917HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5918 StorageBus_T aConnectionType,
5919 ComPtr<IStorageController> &aController)
5920{
5921 if ( (aConnectionType <= StorageBus_Null)
5922 || (aConnectionType > StorageBus_VirtioSCSI))
5923 return setError(E_INVALIDARG,
5924 tr("Invalid connection type: %d"),
5925 aConnectionType);
5926
5927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5928
5929 HRESULT rc = i_checkStateDependency(MutableStateDep);
5930 if (FAILED(rc)) return rc;
5931
5932 /* try to find one with the name first. */
5933 ComObjPtr<StorageController> ctrl;
5934
5935 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5936 if (SUCCEEDED(rc))
5937 return setError(VBOX_E_OBJECT_IN_USE,
5938 tr("Storage controller named '%s' already exists"),
5939 aName.c_str());
5940
5941 ctrl.createObject();
5942
5943 /* get a new instance number for the storage controller */
5944 ULONG ulInstance = 0;
5945 bool fBootable = true;
5946 for (StorageControllerList::const_iterator
5947 it = mStorageControllers->begin();
5948 it != mStorageControllers->end();
5949 ++it)
5950 {
5951 if ((*it)->i_getStorageBus() == aConnectionType)
5952 {
5953 ULONG ulCurInst = (*it)->i_getInstance();
5954
5955 if (ulCurInst >= ulInstance)
5956 ulInstance = ulCurInst + 1;
5957
5958 /* Only one controller of each type can be marked as bootable. */
5959 if ((*it)->i_getBootable())
5960 fBootable = false;
5961 }
5962 }
5963
5964 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5965 if (FAILED(rc)) return rc;
5966
5967 i_setModified(IsModified_Storage);
5968 mStorageControllers.backup();
5969 mStorageControllers->push_back(ctrl);
5970
5971 ctrl.queryInterfaceTo(aController.asOutParam());
5972
5973 /* inform the direct session if any */
5974 alock.release();
5975 i_onStorageControllerChange(i_getId(), aName);
5976
5977 return S_OK;
5978}
5979
5980HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5981 ComPtr<IStorageController> &aStorageController)
5982{
5983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5984
5985 ComObjPtr<StorageController> ctrl;
5986
5987 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5988 if (SUCCEEDED(rc))
5989 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5990
5991 return rc;
5992}
5993
5994HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5995 ULONG aInstance,
5996 ComPtr<IStorageController> &aStorageController)
5997{
5998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5999
6000 for (StorageControllerList::const_iterator
6001 it = mStorageControllers->begin();
6002 it != mStorageControllers->end();
6003 ++it)
6004 {
6005 if ( (*it)->i_getStorageBus() == aConnectionType
6006 && (*it)->i_getInstance() == aInstance)
6007 {
6008 (*it).queryInterfaceTo(aStorageController.asOutParam());
6009 return S_OK;
6010 }
6011 }
6012
6013 return setError(VBOX_E_OBJECT_NOT_FOUND,
6014 tr("Could not find a storage controller with instance number '%lu'"),
6015 aInstance);
6016}
6017
6018HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6019{
6020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6021
6022 HRESULT rc = i_checkStateDependency(MutableStateDep);
6023 if (FAILED(rc)) return rc;
6024
6025 ComObjPtr<StorageController> ctrl;
6026
6027 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6028 if (SUCCEEDED(rc))
6029 {
6030 /* Ensure that only one controller of each type is marked as bootable. */
6031 if (aBootable == TRUE)
6032 {
6033 for (StorageControllerList::const_iterator
6034 it = mStorageControllers->begin();
6035 it != mStorageControllers->end();
6036 ++it)
6037 {
6038 ComObjPtr<StorageController> aCtrl = (*it);
6039
6040 if ( (aCtrl->i_getName() != aName)
6041 && aCtrl->i_getBootable() == TRUE
6042 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6043 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6044 {
6045 aCtrl->i_setBootable(FALSE);
6046 break;
6047 }
6048 }
6049 }
6050
6051 if (SUCCEEDED(rc))
6052 {
6053 ctrl->i_setBootable(aBootable);
6054 i_setModified(IsModified_Storage);
6055 }
6056 }
6057
6058 if (SUCCEEDED(rc))
6059 {
6060 /* inform the direct session if any */
6061 alock.release();
6062 i_onStorageControllerChange(i_getId(), aName);
6063 }
6064
6065 return rc;
6066}
6067
6068HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6069{
6070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 HRESULT rc = i_checkStateDependency(MutableStateDep);
6073 if (FAILED(rc)) return rc;
6074
6075 ComObjPtr<StorageController> ctrl;
6076 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6077 if (FAILED(rc)) return rc;
6078
6079 MediumAttachmentList llDetachedAttachments;
6080 {
6081 /* find all attached devices to the appropriate storage controller and detach them all */
6082 // make a temporary list because detachDevice invalidates iterators into
6083 // mMediumAttachments
6084 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6085
6086 for (MediumAttachmentList::const_iterator
6087 it = llAttachments2.begin();
6088 it != llAttachments2.end();
6089 ++it)
6090 {
6091 MediumAttachment *pAttachTemp = *it;
6092
6093 AutoCaller localAutoCaller(pAttachTemp);
6094 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6095
6096 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6097
6098 if (pAttachTemp->i_getControllerName() == aName)
6099 {
6100 llDetachedAttachments.push_back(pAttachTemp);
6101 rc = i_detachDevice(pAttachTemp, alock, NULL);
6102 if (FAILED(rc)) return rc;
6103 }
6104 }
6105 }
6106
6107 /* send event about detached devices before removing parent controller */
6108 for (MediumAttachmentList::const_iterator
6109 it = llDetachedAttachments.begin();
6110 it != llDetachedAttachments.end();
6111 ++it)
6112 {
6113 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6114 }
6115
6116 /* We can remove it now. */
6117 i_setModified(IsModified_Storage);
6118 mStorageControllers.backup();
6119
6120 ctrl->i_unshare();
6121
6122 mStorageControllers->remove(ctrl);
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onStorageControllerChange(i_getId(), aName);
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6132 ComPtr<IUSBController> &aController)
6133{
6134 if ( (aType <= USBControllerType_Null)
6135 || (aType >= USBControllerType_Last))
6136 return setError(E_INVALIDARG,
6137 tr("Invalid USB controller type: %d"),
6138 aType);
6139
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 /* try to find one with the same type first. */
6146 ComObjPtr<USBController> ctrl;
6147
6148 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6149 if (SUCCEEDED(rc))
6150 return setError(VBOX_E_OBJECT_IN_USE,
6151 tr("USB controller named '%s' already exists"),
6152 aName.c_str());
6153
6154 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6155 ULONG maxInstances;
6156 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6157 if (FAILED(rc))
6158 return rc;
6159
6160 ULONG cInstances = i_getUSBControllerCountByType(aType);
6161 if (cInstances >= maxInstances)
6162 return setError(E_INVALIDARG,
6163 tr("Too many USB controllers of this type"));
6164
6165 ctrl.createObject();
6166
6167 rc = ctrl->init(this, aName, aType);
6168 if (FAILED(rc)) return rc;
6169
6170 i_setModified(IsModified_USB);
6171 mUSBControllers.backup();
6172 mUSBControllers->push_back(ctrl);
6173
6174 ctrl.queryInterfaceTo(aController.asOutParam());
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onUSBControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6184{
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 ComObjPtr<USBController> ctrl;
6188
6189 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6190 if (SUCCEEDED(rc))
6191 ctrl.queryInterfaceTo(aController.asOutParam());
6192
6193 return rc;
6194}
6195
6196HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6197 ULONG *aControllers)
6198{
6199 if ( (aType <= USBControllerType_Null)
6200 || (aType >= USBControllerType_Last))
6201 return setError(E_INVALIDARG,
6202 tr("Invalid USB controller type: %d"),
6203 aType);
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 ComObjPtr<USBController> ctrl;
6208
6209 *aControllers = i_getUSBControllerCountByType(aType);
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6215{
6216
6217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 HRESULT rc = i_checkStateDependency(MutableStateDep);
6220 if (FAILED(rc)) return rc;
6221
6222 ComObjPtr<USBController> ctrl;
6223 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6224 if (FAILED(rc)) return rc;
6225
6226 i_setModified(IsModified_USB);
6227 mUSBControllers.backup();
6228
6229 ctrl->i_unshare();
6230
6231 mUSBControllers->remove(ctrl);
6232
6233 /* inform the direct session if any */
6234 alock.release();
6235 i_onUSBControllerChange();
6236
6237 return S_OK;
6238}
6239
6240HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6241 ULONG *aOriginX,
6242 ULONG *aOriginY,
6243 ULONG *aWidth,
6244 ULONG *aHeight,
6245 BOOL *aEnabled)
6246{
6247 uint32_t u32OriginX= 0;
6248 uint32_t u32OriginY= 0;
6249 uint32_t u32Width = 0;
6250 uint32_t u32Height = 0;
6251 uint16_t u16Flags = 0;
6252
6253 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6254 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6255 if (RT_FAILURE(vrc))
6256 {
6257#ifdef RT_OS_WINDOWS
6258 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6259 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6260 * So just assign fEnable to TRUE again.
6261 * The right fix would be to change GUI API wrappers to make sure that parameters
6262 * are changed only if API succeeds.
6263 */
6264 *aEnabled = TRUE;
6265#endif
6266 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6267 tr("Saved guest size is not available (%Rrc)"),
6268 vrc);
6269 }
6270
6271 *aOriginX = u32OriginX;
6272 *aOriginY = u32OriginY;
6273 *aWidth = u32Width;
6274 *aHeight = u32Height;
6275 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6276
6277 return S_OK;
6278}
6279
6280HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6281 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6282{
6283 if (aScreenId != 0)
6284 return E_NOTIMPL;
6285
6286 if ( aBitmapFormat != BitmapFormat_BGR0
6287 && aBitmapFormat != BitmapFormat_BGRA
6288 && aBitmapFormat != BitmapFormat_RGBA
6289 && aBitmapFormat != BitmapFormat_PNG)
6290 return setError(E_NOTIMPL,
6291 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6292
6293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6294
6295 uint8_t *pu8Data = NULL;
6296 uint32_t cbData = 0;
6297 uint32_t u32Width = 0;
6298 uint32_t u32Height = 0;
6299
6300 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6301
6302 if (RT_FAILURE(vrc))
6303 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6304 tr("Saved thumbnail data is not available (%Rrc)"),
6305 vrc);
6306
6307 HRESULT hr = S_OK;
6308
6309 *aWidth = u32Width;
6310 *aHeight = u32Height;
6311
6312 if (cbData > 0)
6313 {
6314 /* Convert pixels to the format expected by the API caller. */
6315 if (aBitmapFormat == BitmapFormat_BGR0)
6316 {
6317 /* [0] B, [1] G, [2] R, [3] 0. */
6318 aData.resize(cbData);
6319 memcpy(&aData.front(), pu8Data, cbData);
6320 }
6321 else if (aBitmapFormat == BitmapFormat_BGRA)
6322 {
6323 /* [0] B, [1] G, [2] R, [3] A. */
6324 aData.resize(cbData);
6325 for (uint32_t i = 0; i < cbData; i += 4)
6326 {
6327 aData[i] = pu8Data[i];
6328 aData[i + 1] = pu8Data[i + 1];
6329 aData[i + 2] = pu8Data[i + 2];
6330 aData[i + 3] = 0xff;
6331 }
6332 }
6333 else if (aBitmapFormat == BitmapFormat_RGBA)
6334 {
6335 /* [0] R, [1] G, [2] B, [3] A. */
6336 aData.resize(cbData);
6337 for (uint32_t i = 0; i < cbData; i += 4)
6338 {
6339 aData[i] = pu8Data[i + 2];
6340 aData[i + 1] = pu8Data[i + 1];
6341 aData[i + 2] = pu8Data[i];
6342 aData[i + 3] = 0xff;
6343 }
6344 }
6345 else if (aBitmapFormat == BitmapFormat_PNG)
6346 {
6347 uint8_t *pu8PNG = NULL;
6348 uint32_t cbPNG = 0;
6349 uint32_t cxPNG = 0;
6350 uint32_t cyPNG = 0;
6351
6352 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6353
6354 if (RT_SUCCESS(vrc))
6355 {
6356 aData.resize(cbPNG);
6357 if (cbPNG)
6358 memcpy(&aData.front(), pu8PNG, cbPNG);
6359 }
6360 else
6361 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6362 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6363 vrc);
6364
6365 RTMemFree(pu8PNG);
6366 }
6367 }
6368
6369 freeSavedDisplayScreenshot(pu8Data);
6370
6371 return hr;
6372}
6373
6374HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6375 ULONG *aWidth,
6376 ULONG *aHeight,
6377 std::vector<BitmapFormat_T> &aBitmapFormats)
6378{
6379 if (aScreenId != 0)
6380 return E_NOTIMPL;
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 uint8_t *pu8Data = NULL;
6385 uint32_t cbData = 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388
6389 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6390
6391 if (RT_FAILURE(vrc))
6392 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6393 tr("Saved screenshot data is not available (%Rrc)"),
6394 vrc);
6395
6396 *aWidth = u32Width;
6397 *aHeight = u32Height;
6398 aBitmapFormats.resize(1);
6399 aBitmapFormats[0] = BitmapFormat_PNG;
6400
6401 freeSavedDisplayScreenshot(pu8Data);
6402
6403 return S_OK;
6404}
6405
6406HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6407 BitmapFormat_T aBitmapFormat,
6408 ULONG *aWidth,
6409 ULONG *aHeight,
6410 std::vector<BYTE> &aData)
6411{
6412 if (aScreenId != 0)
6413 return E_NOTIMPL;
6414
6415 if (aBitmapFormat != BitmapFormat_PNG)
6416 return E_NOTIMPL;
6417
6418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6419
6420 uint8_t *pu8Data = NULL;
6421 uint32_t cbData = 0;
6422 uint32_t u32Width = 0;
6423 uint32_t u32Height = 0;
6424
6425 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6426
6427 if (RT_FAILURE(vrc))
6428 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6429 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6430 vrc);
6431
6432 *aWidth = u32Width;
6433 *aHeight = u32Height;
6434
6435 aData.resize(cbData);
6436 if (cbData)
6437 memcpy(&aData.front(), pu8Data, cbData);
6438
6439 freeSavedDisplayScreenshot(pu8Data);
6440
6441 return S_OK;
6442}
6443
6444HRESULT Machine::hotPlugCPU(ULONG aCpu)
6445{
6446 HRESULT rc = S_OK;
6447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6448
6449 if (!mHWData->mCPUHotPlugEnabled)
6450 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6451
6452 if (aCpu >= mHWData->mCPUCount)
6453 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6454
6455 if (mHWData->mCPUAttached[aCpu])
6456 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6457
6458 alock.release();
6459 rc = i_onCPUChange(aCpu, false);
6460 alock.acquire();
6461 if (FAILED(rc)) return rc;
6462
6463 i_setModified(IsModified_MachineData);
6464 mHWData.backup();
6465 mHWData->mCPUAttached[aCpu] = true;
6466
6467 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6468 if (Global::IsOnline(mData->mMachineState))
6469 i_saveSettings(NULL);
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6475{
6476 HRESULT rc = S_OK;
6477
6478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 if (!mHWData->mCPUHotPlugEnabled)
6481 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6482
6483 if (aCpu >= SchemaDefs::MaxCPUCount)
6484 return setError(E_INVALIDARG,
6485 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6486 SchemaDefs::MaxCPUCount);
6487
6488 if (!mHWData->mCPUAttached[aCpu])
6489 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6490
6491 /* CPU 0 can't be detached */
6492 if (aCpu == 0)
6493 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6494
6495 alock.release();
6496 rc = i_onCPUChange(aCpu, true);
6497 alock.acquire();
6498 if (FAILED(rc)) return rc;
6499
6500 i_setModified(IsModified_MachineData);
6501 mHWData.backup();
6502 mHWData->mCPUAttached[aCpu] = false;
6503
6504 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6505 if (Global::IsOnline(mData->mMachineState))
6506 i_saveSettings(NULL);
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6512{
6513 *aAttached = false;
6514
6515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 /* If hotplug is enabled the CPU is always enabled. */
6518 if (!mHWData->mCPUHotPlugEnabled)
6519 {
6520 if (aCpu < mHWData->mCPUCount)
6521 *aAttached = true;
6522 }
6523 else
6524 {
6525 if (aCpu < SchemaDefs::MaxCPUCount)
6526 *aAttached = mHWData->mCPUAttached[aCpu];
6527 }
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6533{
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 Utf8Str log = i_getLogFilename(aIdx);
6537 if (!RTFileExists(log.c_str()))
6538 log.setNull();
6539 aFilename = log;
6540
6541 return S_OK;
6542}
6543
6544HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6545{
6546 if (aSize < 0)
6547 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6548
6549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6550
6551 HRESULT rc = S_OK;
6552 Utf8Str log = i_getLogFilename(aIdx);
6553
6554 /* do not unnecessarily hold the lock while doing something which does
6555 * not need the lock and potentially takes a long time. */
6556 alock.release();
6557
6558 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6559 * keeps the SOAP reply size under 1M for the webservice (we're using
6560 * base64 encoded strings for binary data for years now, avoiding the
6561 * expansion of each byte array element to approx. 25 bytes of XML. */
6562 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6563 aData.resize(cbData);
6564
6565 RTFILE LogFile;
6566 int vrc = RTFileOpen(&LogFile, log.c_str(),
6567 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6568 if (RT_SUCCESS(vrc))
6569 {
6570 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6571 if (RT_SUCCESS(vrc))
6572 aData.resize(cbData);
6573 else
6574 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6575 tr("Could not read log file '%s' (%Rrc)"),
6576 log.c_str(), vrc);
6577 RTFileClose(LogFile);
6578 }
6579 else
6580 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6581 tr("Could not open log file '%s' (%Rrc)"),
6582 log.c_str(), vrc);
6583
6584 if (FAILED(rc))
6585 aData.resize(0);
6586
6587 return rc;
6588}
6589
6590
6591/**
6592 * Currently this method doesn't attach device to the running VM,
6593 * just makes sure it's plugged on next VM start.
6594 */
6595HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6596{
6597 // lock scope
6598 {
6599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 HRESULT rc = i_checkStateDependency(MutableStateDep);
6602 if (FAILED(rc)) return rc;
6603
6604 ChipsetType_T aChipset = ChipsetType_PIIX3;
6605 COMGETTER(ChipsetType)(&aChipset);
6606
6607 if (aChipset != ChipsetType_ICH9)
6608 {
6609 return setError(E_INVALIDARG,
6610 tr("Host PCI attachment only supported with ICH9 chipset"));
6611 }
6612
6613 // check if device with this host PCI address already attached
6614 for (HWData::PCIDeviceAssignmentList::const_iterator
6615 it = mHWData->mPCIDeviceAssignments.begin();
6616 it != mHWData->mPCIDeviceAssignments.end();
6617 ++it)
6618 {
6619 LONG iHostAddress = -1;
6620 ComPtr<PCIDeviceAttachment> pAttach;
6621 pAttach = *it;
6622 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6623 if (iHostAddress == aHostAddress)
6624 return setError(E_INVALIDARG,
6625 tr("Device with host PCI address already attached to this VM"));
6626 }
6627
6628 ComObjPtr<PCIDeviceAttachment> pda;
6629 char name[32];
6630
6631 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6632 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6633 pda.createObject();
6634 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6635 i_setModified(IsModified_MachineData);
6636 mHWData.backup();
6637 mHWData->mPCIDeviceAssignments.push_back(pda);
6638 }
6639
6640 return S_OK;
6641}
6642
6643/**
6644 * Currently this method doesn't detach device from the running VM,
6645 * just makes sure it's not plugged on next VM start.
6646 */
6647HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6648{
6649 ComObjPtr<PCIDeviceAttachment> pAttach;
6650 bool fRemoved = false;
6651 HRESULT rc;
6652
6653 // lock scope
6654 {
6655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 rc = i_checkStateDependency(MutableStateDep);
6658 if (FAILED(rc)) return rc;
6659
6660 for (HWData::PCIDeviceAssignmentList::const_iterator
6661 it = mHWData->mPCIDeviceAssignments.begin();
6662 it != mHWData->mPCIDeviceAssignments.end();
6663 ++it)
6664 {
6665 LONG iHostAddress = -1;
6666 pAttach = *it;
6667 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6668 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6669 {
6670 i_setModified(IsModified_MachineData);
6671 mHWData.backup();
6672 mHWData->mPCIDeviceAssignments.remove(pAttach);
6673 fRemoved = true;
6674 break;
6675 }
6676 }
6677 }
6678
6679
6680 /* Fire event outside of the lock */
6681 if (fRemoved)
6682 {
6683 Assert(!pAttach.isNull());
6684 ComPtr<IEventSource> es;
6685 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6686 Assert(SUCCEEDED(rc));
6687 Bstr mid;
6688 rc = this->COMGETTER(Id)(mid.asOutParam());
6689 Assert(SUCCEEDED(rc));
6690 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6691 }
6692
6693 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6694 tr("No host PCI device %08x attached"),
6695 aHostAddress
6696 );
6697}
6698
6699HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6700{
6701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6702
6703 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6704 size_t i = 0;
6705 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6706 it = mHWData->mPCIDeviceAssignments.begin();
6707 it != mHWData->mPCIDeviceAssignments.end();
6708 ++it, ++i)
6709 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6710
6711 return S_OK;
6712}
6713
6714HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6715{
6716 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6717
6718 return S_OK;
6719}
6720
6721HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6731{
6732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6733 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6734 if (SUCCEEDED(hrc))
6735 {
6736 hrc = mHWData.backupEx();
6737 if (SUCCEEDED(hrc))
6738 {
6739 i_setModified(IsModified_MachineData);
6740 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6741 }
6742 }
6743 return hrc;
6744}
6745
6746HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6747{
6748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6749 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6750 return S_OK;
6751}
6752
6753HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6754{
6755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6756 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6757 if (SUCCEEDED(hrc))
6758 {
6759 hrc = mHWData.backupEx();
6760 if (SUCCEEDED(hrc))
6761 {
6762 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6763 if (SUCCEEDED(hrc))
6764 i_setModified(IsModified_MachineData);
6765 }
6766 }
6767 return hrc;
6768}
6769
6770HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6771{
6772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6773
6774 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6775
6776 return S_OK;
6777}
6778
6779HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6780{
6781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6782 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6783 if (SUCCEEDED(hrc))
6784 {
6785 hrc = mHWData.backupEx();
6786 if (SUCCEEDED(hrc))
6787 {
6788 i_setModified(IsModified_MachineData);
6789 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6790 }
6791 }
6792 return hrc;
6793}
6794
6795HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6809 if ( SUCCEEDED(hrc)
6810 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6811 {
6812 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6813 int vrc;
6814
6815 if (aAutostartEnabled)
6816 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6817 else
6818 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6819
6820 if (RT_SUCCESS(vrc))
6821 {
6822 hrc = mHWData.backupEx();
6823 if (SUCCEEDED(hrc))
6824 {
6825 i_setModified(IsModified_MachineData);
6826 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6827 }
6828 }
6829 else if (vrc == VERR_NOT_SUPPORTED)
6830 hrc = setError(VBOX_E_NOT_SUPPORTED,
6831 tr("The VM autostart feature is not supported on this platform"));
6832 else if (vrc == VERR_PATH_NOT_FOUND)
6833 hrc = setError(E_FAIL,
6834 tr("The path to the autostart database is not set"));
6835 else
6836 hrc = setError(E_UNEXPECTED,
6837 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6838 aAutostartEnabled ? "Adding" : "Removing",
6839 mUserData->s.strName.c_str(), vrc);
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6854{
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6864 }
6865 }
6866 return hrc;
6867}
6868
6869HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6870{
6871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6872
6873 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6874
6875 return S_OK;
6876}
6877
6878HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6879{
6880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6881 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6882 if ( SUCCEEDED(hrc)
6883 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6884 {
6885 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6886 int vrc;
6887
6888 if (aAutostopType != AutostopType_Disabled)
6889 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6890 else
6891 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6892
6893 if (RT_SUCCESS(vrc))
6894 {
6895 hrc = mHWData.backupEx();
6896 if (SUCCEEDED(hrc))
6897 {
6898 i_setModified(IsModified_MachineData);
6899 mHWData->mAutostart.enmAutostopType = aAutostopType;
6900 }
6901 }
6902 else if (vrc == VERR_NOT_SUPPORTED)
6903 hrc = setError(VBOX_E_NOT_SUPPORTED,
6904 tr("The VM autostop feature is not supported on this platform"));
6905 else if (vrc == VERR_PATH_NOT_FOUND)
6906 hrc = setError(E_FAIL,
6907 tr("The path to the autostart database is not set"));
6908 else
6909 hrc = setError(E_UNEXPECTED,
6910 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6911 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6912 mUserData->s.strName.c_str(), vrc);
6913 }
6914 return hrc;
6915}
6916
6917HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6918{
6919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6920
6921 aDefaultFrontend = mHWData->mDefaultFrontend;
6922
6923 return S_OK;
6924}
6925
6926HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6927{
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mHWData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mHWData->mDefaultFrontend = aDefaultFrontend;
6937 }
6938 }
6939 return hrc;
6940}
6941
6942HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6943{
6944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6945 size_t cbIcon = mUserData->s.ovIcon.size();
6946 aIcon.resize(cbIcon);
6947 if (cbIcon)
6948 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6956 if (SUCCEEDED(hrc))
6957 {
6958 i_setModified(IsModified_MachineData);
6959 mUserData.backup();
6960 size_t cbIcon = aIcon.size();
6961 mUserData->s.ovIcon.resize(cbIcon);
6962 if (cbIcon)
6963 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6964 }
6965 return hrc;
6966}
6967
6968HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6969{
6970#ifdef VBOX_WITH_USB
6971 *aUSBProxyAvailable = true;
6972#else
6973 *aUSBProxyAvailable = false;
6974#endif
6975 return S_OK;
6976}
6977
6978HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6979{
6980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 *aVMProcessPriority = mUserData->s.enmVMPriority;
6983
6984 return S_OK;
6985}
6986
6987HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6988{
6989 RT_NOREF(aVMProcessPriority);
6990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6992 if (SUCCEEDED(hrc))
6993 {
6994 hrc = mUserData.backupEx();
6995 if (SUCCEEDED(hrc))
6996 {
6997 i_setModified(IsModified_MachineData);
6998 mUserData->s.enmVMPriority = aVMProcessPriority;
6999 }
7000 }
7001 alock.release();
7002 if (SUCCEEDED(hrc))
7003 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7004 return hrc;
7005}
7006
7007HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7008 ComPtr<IProgress> &aProgress)
7009{
7010 ComObjPtr<Progress> pP;
7011 Progress *ppP = pP;
7012 IProgress *iP = static_cast<IProgress *>(ppP);
7013 IProgress **pProgress = &iP;
7014
7015 IMachine *pTarget = aTarget;
7016
7017 /* Convert the options. */
7018 RTCList<CloneOptions_T> optList;
7019 if (aOptions.size())
7020 for (size_t i = 0; i < aOptions.size(); ++i)
7021 optList.append(aOptions[i]);
7022
7023 if (optList.contains(CloneOptions_Link))
7024 {
7025 if (!i_isSnapshotMachine())
7026 return setError(E_INVALIDARG,
7027 tr("Linked clone can only be created from a snapshot"));
7028 if (aMode != CloneMode_MachineState)
7029 return setError(E_INVALIDARG,
7030 tr("Linked clone can only be created for a single machine state"));
7031 }
7032 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7033
7034 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7035
7036 HRESULT rc = pWorker->start(pProgress);
7037
7038 pP = static_cast<Progress *>(*pProgress);
7039 pP.queryInterfaceTo(aProgress.asOutParam());
7040
7041 return rc;
7042
7043}
7044
7045HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7046 const com::Utf8Str &aType,
7047 ComPtr<IProgress> &aProgress)
7048{
7049 LogFlowThisFuncEnter();
7050
7051 ComObjPtr<Progress> ptrProgress;
7052 HRESULT hrc = ptrProgress.createObject();
7053 if (SUCCEEDED(hrc))
7054 {
7055 /* Initialize our worker task */
7056 MachineMoveVM *pTask = NULL;
7057 try
7058 {
7059 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7060 }
7061 catch (std::bad_alloc &)
7062 {
7063 return E_OUTOFMEMORY;
7064 }
7065
7066 hrc = pTask->init();//no exceptions are thrown
7067
7068 if (SUCCEEDED(hrc))
7069 {
7070 hrc = pTask->createThread();
7071 pTask = NULL; /* Consumed by createThread(). */
7072 if (SUCCEEDED(hrc))
7073 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7074 else
7075 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7076 }
7077 else
7078 delete pTask;
7079 }
7080
7081 LogFlowThisFuncLeave();
7082 return hrc;
7083
7084}
7085
7086HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7087{
7088 NOREF(aProgress);
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 // This check should always fail.
7092 HRESULT rc = i_checkStateDependency(MutableStateDep);
7093 if (FAILED(rc)) return rc;
7094
7095 AssertFailedReturn(E_NOTIMPL);
7096}
7097
7098HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7099{
7100 NOREF(aSavedStateFile);
7101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7102
7103 // This check should always fail.
7104 HRESULT rc = i_checkStateDependency(MutableStateDep);
7105 if (FAILED(rc)) return rc;
7106
7107 AssertFailedReturn(E_NOTIMPL);
7108}
7109
7110HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7111{
7112 NOREF(aFRemoveFile);
7113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7114
7115 // This check should always fail.
7116 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7117 if (FAILED(rc)) return rc;
7118
7119 AssertFailedReturn(E_NOTIMPL);
7120}
7121
7122// public methods for internal purposes
7123/////////////////////////////////////////////////////////////////////////////
7124
7125/**
7126 * Adds the given IsModified_* flag to the dirty flags of the machine.
7127 * This must be called either during i_loadSettings or under the machine write lock.
7128 * @param fl Flag
7129 * @param fAllowStateModification If state modifications are allowed.
7130 */
7131void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7132{
7133 mData->flModifications |= fl;
7134 if (fAllowStateModification && i_isStateModificationAllowed())
7135 mData->mCurrentStateModified = true;
7136}
7137
7138/**
7139 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7140 * care of the write locking.
7141 *
7142 * @param fModification The flag to add.
7143 * @param fAllowStateModification If state modifications are allowed.
7144 */
7145void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7146{
7147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7148 i_setModified(fModification, fAllowStateModification);
7149}
7150
7151/**
7152 * Saves the registry entry of this machine to the given configuration node.
7153 *
7154 * @param data Machine registry data.
7155 *
7156 * @note locks this object for reading.
7157 */
7158HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7159{
7160 AutoLimitedCaller autoCaller(this);
7161 AssertComRCReturnRC(autoCaller.rc());
7162
7163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 data.uuid = mData->mUuid;
7166 data.strSettingsFile = mData->m_strConfigFile;
7167
7168 return S_OK;
7169}
7170
7171/**
7172 * Calculates the absolute path of the given path taking the directory of the
7173 * machine settings file as the current directory.
7174 *
7175 * @param strPath Path to calculate the absolute path for.
7176 * @param aResult Where to put the result (used only on success, can be the
7177 * same Utf8Str instance as passed in @a aPath).
7178 * @return IPRT result.
7179 *
7180 * @note Locks this object for reading.
7181 */
7182int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7183{
7184 AutoCaller autoCaller(this);
7185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7186
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188
7189 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7190
7191 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7192
7193 strSettingsDir.stripFilename();
7194 char szFolder[RTPATH_MAX];
7195 size_t cbFolder = sizeof(szFolder);
7196 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7197 if (RT_SUCCESS(vrc))
7198 aResult = szFolder;
7199
7200 return vrc;
7201}
7202
7203/**
7204 * Copies strSource to strTarget, making it relative to the machine folder
7205 * if it is a subdirectory thereof, or simply copying it otherwise.
7206 *
7207 * @param strSource Path to evaluate and copy.
7208 * @param strTarget Buffer to receive target path.
7209 *
7210 * @note Locks this object for reading.
7211 */
7212void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7213 Utf8Str &strTarget)
7214{
7215 AutoCaller autoCaller(this);
7216 AssertComRCReturn(autoCaller.rc(), (void)0);
7217
7218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7219
7220 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7221 // use strTarget as a temporary buffer to hold the machine settings dir
7222 strTarget = mData->m_strConfigFileFull;
7223 strTarget.stripFilename();
7224 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7225 {
7226 // is relative: then append what's left
7227 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7228 // for empty paths (only possible for subdirs) use "." to avoid
7229 // triggering default settings for not present config attributes.
7230 if (strTarget.isEmpty())
7231 strTarget = ".";
7232 }
7233 else
7234 // is not relative: then overwrite
7235 strTarget = strSource;
7236}
7237
7238/**
7239 * Returns the full path to the machine's log folder in the
7240 * \a aLogFolder argument.
7241 */
7242void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7243{
7244 AutoCaller autoCaller(this);
7245 AssertComRCReturnVoid(autoCaller.rc());
7246
7247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7248
7249 char szTmp[RTPATH_MAX];
7250 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7251 if (RT_SUCCESS(vrc))
7252 {
7253 if (szTmp[0] && !mUserData.isNull())
7254 {
7255 char szTmp2[RTPATH_MAX];
7256 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7257 if (RT_SUCCESS(vrc))
7258 aLogFolder = Utf8StrFmt("%s%c%s",
7259 szTmp2,
7260 RTPATH_DELIMITER,
7261 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7262 }
7263 else
7264 vrc = VERR_PATH_IS_RELATIVE;
7265 }
7266
7267 if (RT_FAILURE(vrc))
7268 {
7269 // fallback if VBOX_USER_LOGHOME is not set or invalid
7270 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7271 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7272 aLogFolder.append(RTPATH_DELIMITER);
7273 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7274 }
7275}
7276
7277/**
7278 * Returns the full path to the machine's log file for an given index.
7279 */
7280Utf8Str Machine::i_getLogFilename(ULONG idx)
7281{
7282 Utf8Str logFolder;
7283 getLogFolder(logFolder);
7284 Assert(logFolder.length());
7285
7286 Utf8Str log;
7287 if (idx == 0)
7288 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7289#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7290 else if (idx == 1)
7291 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7292 else
7293 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7294#else
7295 else
7296 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7297#endif
7298 return log;
7299}
7300
7301/**
7302 * Returns the full path to the machine's hardened log file.
7303 */
7304Utf8Str Machine::i_getHardeningLogFilename(void)
7305{
7306 Utf8Str strFilename;
7307 getLogFolder(strFilename);
7308 Assert(strFilename.length());
7309 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7310 return strFilename;
7311}
7312
7313/**
7314 * Returns the default NVRAM filename based on the location of the VM config.
7315 * This intentionally works differently than the saved state file naming since
7316 * it is part of the current state. Taking a snapshot will use a similar naming
7317 * as for saved state, because these are actually read-only, retaining a
7318 * a specific state just like saved state.
7319 */
7320
7321Utf8Str Machine::i_getDefaultNVRAMFilename()
7322{
7323 AutoCaller autoCaller(this);
7324 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7325
7326 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7327 Utf8Str strNVRAMFilePathAbs = mData->m_strConfigFileFull;
7328 strNVRAMFilePathAbs.stripSuffix();
7329 strNVRAMFilePathAbs += ".nvram";
7330 Utf8Str strNVRAMFilePath;
7331 i_copyPathRelativeToMachine(strNVRAMFilePathAbs, strNVRAMFilePath);
7332
7333 return strNVRAMFilePath;
7334}
7335
7336
7337/**
7338 * Composes a unique saved state filename based on the current system time. The filename is
7339 * granular to the second so this will work so long as no more than one snapshot is taken on
7340 * a machine per second.
7341 *
7342 * Before version 4.1, we used this formula for saved state files:
7343 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7344 * which no longer works because saved state files can now be shared between the saved state of the
7345 * "saved" machine and an online snapshot, and the following would cause problems:
7346 * 1) save machine
7347 * 2) create online snapshot from that machine state --> reusing saved state file
7348 * 3) save machine again --> filename would be reused, breaking the online snapshot
7349 *
7350 * So instead we now use a timestamp.
7351 *
7352 * @param strStateFilePath
7353 */
7354
7355void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7356{
7357 AutoCaller autoCaller(this);
7358 AssertComRCReturnVoid(autoCaller.rc());
7359
7360 {
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7363 }
7364
7365 RTTIMESPEC ts;
7366 RTTimeNow(&ts);
7367 RTTIME time;
7368 RTTimeExplode(&time, &ts);
7369
7370 strStateFilePath += RTPATH_DELIMITER;
7371 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7372 time.i32Year, time.u8Month, time.u8MonthDay,
7373 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7374}
7375
7376/**
7377 * Returns whether at least one USB controller is present for the VM.
7378 */
7379bool Machine::i_isUSBControllerPresent()
7380{
7381 AutoCaller autoCaller(this);
7382 AssertComRCReturn(autoCaller.rc(), false);
7383
7384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7385
7386 return (mUSBControllers->size() > 0);
7387}
7388
7389/**
7390 * @note Locks this object for writing, calls the client process
7391 * (inside the lock).
7392 */
7393HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7394 const Utf8Str &strFrontend,
7395 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7396 ProgressProxy *aProgress)
7397{
7398 LogFlowThisFuncEnter();
7399
7400 AssertReturn(aControl, E_FAIL);
7401 AssertReturn(aProgress, E_FAIL);
7402 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7403
7404 AutoCaller autoCaller(this);
7405 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7406
7407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7408
7409 if (!mData->mRegistered)
7410 return setError(E_UNEXPECTED,
7411 tr("The machine '%s' is not registered"),
7412 mUserData->s.strName.c_str());
7413
7414 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7415
7416 /* The process started when launching a VM with separate UI/VM processes is always
7417 * the UI process, i.e. needs special handling as it won't claim the session. */
7418 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7419
7420 if (fSeparate)
7421 {
7422 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7423 return setError(VBOX_E_INVALID_OBJECT_STATE,
7424 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7425 mUserData->s.strName.c_str());
7426 }
7427 else
7428 {
7429 if ( mData->mSession.mState == SessionState_Locked
7430 || mData->mSession.mState == SessionState_Spawning
7431 || mData->mSession.mState == SessionState_Unlocking)
7432 return setError(VBOX_E_INVALID_OBJECT_STATE,
7433 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7434 mUserData->s.strName.c_str());
7435
7436 /* may not be busy */
7437 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7438 }
7439
7440 /* Hardening logging */
7441#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7442 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7443 {
7444 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7445 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7446 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7447 {
7448 Utf8Str strStartupLogDir = strHardeningLogFile;
7449 strStartupLogDir.stripFilename();
7450 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7451 file without stripping the file. */
7452 }
7453 strSupHardeningLogArg.append(strHardeningLogFile);
7454
7455 /* Remove legacy log filename to avoid confusion. */
7456 Utf8Str strOldStartupLogFile;
7457 getLogFolder(strOldStartupLogFile);
7458 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7459 RTFileDelete(strOldStartupLogFile.c_str());
7460 }
7461#else
7462 Utf8Str strSupHardeningLogArg;
7463#endif
7464
7465 Utf8Str strAppOverride;
7466#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7467 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7468#endif
7469
7470 bool fUseVBoxSDS = false;
7471 Utf8Str strCanonicalName;
7472 if (false)
7473 { }
7474#ifdef VBOX_WITH_QTGUI
7475 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7476 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7477 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7478 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7479 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7480 {
7481 strCanonicalName = "GUI/Qt";
7482 fUseVBoxSDS = true;
7483 }
7484#endif
7485#ifdef VBOX_WITH_VBOXSDL
7486 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7487 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7488 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7489 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7490 {
7491 strCanonicalName = "GUI/SDL";
7492 fUseVBoxSDS = true;
7493 }
7494#endif
7495#ifdef VBOX_WITH_HEADLESS
7496 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7497 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7498 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7499 {
7500 strCanonicalName = "headless";
7501 }
7502#endif
7503 else
7504 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7505
7506 Utf8Str idStr = mData->mUuid.toString();
7507 Utf8Str const &strMachineName = mUserData->s.strName;
7508 RTPROCESS pid = NIL_RTPROCESS;
7509
7510#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7511 RT_NOREF(fUseVBoxSDS);
7512#else
7513 DWORD idCallerSession = ~(DWORD)0;
7514 if (fUseVBoxSDS)
7515 {
7516 /*
7517 * The VBoxSDS should be used for process launching the VM with
7518 * GUI only if the caller and the VBoxSDS are in different Windows
7519 * sessions and the caller in the interactive one.
7520 */
7521 fUseVBoxSDS = false;
7522
7523 /* Get windows session of the current process. The process token used
7524 due to several reasons:
7525 1. The token is absent for the current thread except someone set it
7526 for us.
7527 2. Needs to get the id of the session where the process is started.
7528 We only need to do this once, though. */
7529 static DWORD s_idCurrentSession = ~(DWORD)0;
7530 DWORD idCurrentSession = s_idCurrentSession;
7531 if (idCurrentSession == ~(DWORD)0)
7532 {
7533 HANDLE hCurrentProcessToken = NULL;
7534 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7535 {
7536 DWORD cbIgn = 0;
7537 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7538 s_idCurrentSession = idCurrentSession;
7539 else
7540 {
7541 idCurrentSession = ~(DWORD)0;
7542 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7543 }
7544 CloseHandle(hCurrentProcessToken);
7545 }
7546 else
7547 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7548 }
7549
7550 /* get the caller's session */
7551 HRESULT hrc = CoImpersonateClient();
7552 if (SUCCEEDED(hrc))
7553 {
7554 HANDLE hCallerThreadToken;
7555 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7556 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7557 &hCallerThreadToken))
7558 {
7559 SetLastError(NO_ERROR);
7560 DWORD cbIgn = 0;
7561 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7562 {
7563 /* Only need to use SDS if the session ID differs: */
7564 if (idCurrentSession != idCallerSession)
7565 {
7566 fUseVBoxSDS = false;
7567
7568 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7569 DWORD cbTokenGroups = 0;
7570 PTOKEN_GROUPS pTokenGroups = NULL;
7571 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7572 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7573 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7574 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7575 {
7576 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7577 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7578 PSID pInteractiveSid = NULL;
7579 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7580 {
7581 /* Iterate over the groups looking for the interactive SID: */
7582 fUseVBoxSDS = false;
7583 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7584 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7585 {
7586 fUseVBoxSDS = true;
7587 break;
7588 }
7589 FreeSid(pInteractiveSid);
7590 }
7591 }
7592 else
7593 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7594 RTMemTmpFree(pTokenGroups);
7595 }
7596 }
7597 else
7598 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7599 CloseHandle(hCallerThreadToken);
7600 }
7601 else
7602 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7603 CoRevertToSelf();
7604 }
7605 else
7606 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7607 }
7608 if (fUseVBoxSDS)
7609 {
7610 /* connect to VBoxSDS */
7611 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7612 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7613 if (FAILED(rc))
7614 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7615 strMachineName.c_str());
7616
7617 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7618 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7619 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7620 service to access the files. */
7621 rc = CoSetProxyBlanket(pVBoxSDS,
7622 RPC_C_AUTHN_DEFAULT,
7623 RPC_C_AUTHZ_DEFAULT,
7624 COLE_DEFAULT_PRINCIPAL,
7625 RPC_C_AUTHN_LEVEL_DEFAULT,
7626 RPC_C_IMP_LEVEL_IMPERSONATE,
7627 NULL,
7628 EOAC_DEFAULT);
7629 if (FAILED(rc))
7630 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7631
7632 size_t const cEnvVars = aEnvironmentChanges.size();
7633 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7634 for (size_t i = 0; i < cEnvVars; i++)
7635 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7636
7637 ULONG uPid = 0;
7638 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7639 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7640 idCallerSession, &uPid);
7641 if (FAILED(rc))
7642 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7643 pid = (RTPROCESS)uPid;
7644 }
7645 else
7646#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7647 {
7648 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7649 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7650 if (RT_FAILURE(vrc))
7651 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7652 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7653 }
7654
7655 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7656 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7657
7658 if (!fSeparate)
7659 {
7660 /*
7661 * Note that we don't release the lock here before calling the client,
7662 * because it doesn't need to call us back if called with a NULL argument.
7663 * Releasing the lock here is dangerous because we didn't prepare the
7664 * launch data yet, but the client we've just started may happen to be
7665 * too fast and call LockMachine() that will fail (because of PID, etc.),
7666 * so that the Machine will never get out of the Spawning session state.
7667 */
7668
7669 /* inform the session that it will be a remote one */
7670 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7671#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7672 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7673#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7674 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7675#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7676 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7677
7678 if (FAILED(rc))
7679 {
7680 /* restore the session state */
7681 mData->mSession.mState = SessionState_Unlocked;
7682 alock.release();
7683 mParent->i_addProcessToReap(pid);
7684 /* The failure may occur w/o any error info (from RPC), so provide one */
7685 return setError(VBOX_E_VM_ERROR,
7686 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7687 }
7688
7689 /* attach launch data to the machine */
7690 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7691 mData->mSession.mRemoteControls.push_back(aControl);
7692 mData->mSession.mProgress = aProgress;
7693 mData->mSession.mPID = pid;
7694 mData->mSession.mState = SessionState_Spawning;
7695 Assert(strCanonicalName.isNotEmpty());
7696 mData->mSession.mName = strCanonicalName;
7697 }
7698 else
7699 {
7700 /* For separate UI process we declare the launch as completed instantly, as the
7701 * actual headless VM start may or may not come. No point in remembering anything
7702 * yet, as what matters for us is when the headless VM gets started. */
7703 aProgress->i_notifyComplete(S_OK);
7704 }
7705
7706 alock.release();
7707 mParent->i_addProcessToReap(pid);
7708
7709 LogFlowThisFuncLeave();
7710 return S_OK;
7711}
7712
7713/**
7714 * Returns @c true if the given session machine instance has an open direct
7715 * session (and optionally also for direct sessions which are closing) and
7716 * returns the session control machine instance if so.
7717 *
7718 * Note that when the method returns @c false, the arguments remain unchanged.
7719 *
7720 * @param aMachine Session machine object.
7721 * @param aControl Direct session control object (optional).
7722 * @param aRequireVM If true then only allow VM sessions.
7723 * @param aAllowClosing If true then additionally a session which is currently
7724 * being closed will also be allowed.
7725 *
7726 * @note locks this object for reading.
7727 */
7728bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7729 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7730 bool aRequireVM /*= false*/,
7731 bool aAllowClosing /*= false*/)
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_Locked
7743 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7744 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7745 )
7746 {
7747 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7748
7749 aMachine = mData->mSession.mMachine;
7750
7751 if (aControl != NULL)
7752 *aControl = mData->mSession.mDirectControl;
7753
7754 return true;
7755 }
7756
7757 return false;
7758}
7759
7760/**
7761 * Returns @c true if the given machine has an spawning direct session.
7762 *
7763 * @note locks this object for reading.
7764 */
7765bool Machine::i_isSessionSpawning()
7766{
7767 AutoLimitedCaller autoCaller(this);
7768 AssertComRCReturn(autoCaller.rc(), false);
7769
7770 /* just return false for inaccessible machines */
7771 if (getObjectState().getState() != ObjectState::Ready)
7772 return false;
7773
7774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7775
7776 if (mData->mSession.mState == SessionState_Spawning)
7777 return true;
7778
7779 return false;
7780}
7781
7782/**
7783 * Called from the client watcher thread to check for unexpected client process
7784 * death during Session_Spawning state (e.g. before it successfully opened a
7785 * direct session).
7786 *
7787 * On Win32 and on OS/2, this method is called only when we've got the
7788 * direct client's process termination notification, so it always returns @c
7789 * true.
7790 *
7791 * On other platforms, this method returns @c true if the client process is
7792 * terminated and @c false if it's still alive.
7793 *
7794 * @note Locks this object for writing.
7795 */
7796bool Machine::i_checkForSpawnFailure()
7797{
7798 AutoCaller autoCaller(this);
7799 if (!autoCaller.isOk())
7800 {
7801 /* nothing to do */
7802 LogFlowThisFunc(("Already uninitialized!\n"));
7803 return true;
7804 }
7805
7806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7807
7808 if (mData->mSession.mState != SessionState_Spawning)
7809 {
7810 /* nothing to do */
7811 LogFlowThisFunc(("Not spawning any more!\n"));
7812 return true;
7813 }
7814
7815 HRESULT rc = S_OK;
7816
7817 /* PID not yet initialized, skip check. */
7818 if (mData->mSession.mPID == NIL_RTPROCESS)
7819 return false;
7820
7821 RTPROCSTATUS status;
7822 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7823
7824 if (vrc != VERR_PROCESS_RUNNING)
7825 {
7826 Utf8Str strExtraInfo;
7827
7828#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7829 /* If the startup logfile exists and is of non-zero length, tell the
7830 user to look there for more details to encourage them to attach it
7831 when reporting startup issues. */
7832 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7833 uint64_t cbStartupLogFile = 0;
7834 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7835 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7836 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7837#endif
7838
7839 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7840 rc = setError(E_FAIL,
7841 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7842 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7843 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7844 rc = setError(E_FAIL,
7845 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7846 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7847 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7848 rc = setError(E_FAIL,
7849 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7850 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7851 else
7852 rc = setErrorBoth(E_FAIL, vrc,
7853 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7854 i_getName().c_str(), vrc, strExtraInfo.c_str());
7855 }
7856
7857 if (FAILED(rc))
7858 {
7859 /* Close the remote session, remove the remote control from the list
7860 * and reset session state to Closed (@note keep the code in sync with
7861 * the relevant part in LockMachine()). */
7862
7863 Assert(mData->mSession.mRemoteControls.size() == 1);
7864 if (mData->mSession.mRemoteControls.size() == 1)
7865 {
7866 ErrorInfoKeeper eik;
7867 mData->mSession.mRemoteControls.front()->Uninitialize();
7868 }
7869
7870 mData->mSession.mRemoteControls.clear();
7871 mData->mSession.mState = SessionState_Unlocked;
7872
7873 /* finalize the progress after setting the state */
7874 if (!mData->mSession.mProgress.isNull())
7875 {
7876 mData->mSession.mProgress->notifyComplete(rc);
7877 mData->mSession.mProgress.setNull();
7878 }
7879
7880 mData->mSession.mPID = NIL_RTPROCESS;
7881
7882 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7883 return true;
7884 }
7885
7886 return false;
7887}
7888
7889/**
7890 * Checks whether the machine can be registered. If so, commits and saves
7891 * all settings.
7892 *
7893 * @note Must be called from mParent's write lock. Locks this object and
7894 * children for writing.
7895 */
7896HRESULT Machine::i_prepareRegister()
7897{
7898 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7899
7900 AutoLimitedCaller autoCaller(this);
7901 AssertComRCReturnRC(autoCaller.rc());
7902
7903 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7904
7905 /* wait for state dependents to drop to zero */
7906 i_ensureNoStateDependencies();
7907
7908 if (!mData->mAccessible)
7909 return setError(VBOX_E_INVALID_OBJECT_STATE,
7910 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7911 mUserData->s.strName.c_str(),
7912 mData->mUuid.toString().c_str());
7913
7914 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7915
7916 if (mData->mRegistered)
7917 return setError(VBOX_E_INVALID_OBJECT_STATE,
7918 tr("The machine '%s' with UUID {%s} is already registered"),
7919 mUserData->s.strName.c_str(),
7920 mData->mUuid.toString().c_str());
7921
7922 HRESULT rc = S_OK;
7923
7924 // Ensure the settings are saved. If we are going to be registered and
7925 // no config file exists yet, create it by calling i_saveSettings() too.
7926 if ( (mData->flModifications)
7927 || (!mData->pMachineConfigFile->fileExists())
7928 )
7929 {
7930 rc = i_saveSettings(NULL);
7931 // no need to check whether VirtualBox.xml needs saving too since
7932 // we can't have a machine XML file rename pending
7933 if (FAILED(rc)) return rc;
7934 }
7935
7936 /* more config checking goes here */
7937
7938 if (SUCCEEDED(rc))
7939 {
7940 /* we may have had implicit modifications we want to fix on success */
7941 i_commit();
7942
7943 mData->mRegistered = true;
7944 }
7945 else
7946 {
7947 /* we may have had implicit modifications we want to cancel on failure*/
7948 i_rollback(false /* aNotify */);
7949 }
7950
7951 return rc;
7952}
7953
7954/**
7955 * Increases the number of objects dependent on the machine state or on the
7956 * registered state. Guarantees that these two states will not change at least
7957 * until #i_releaseStateDependency() is called.
7958 *
7959 * Depending on the @a aDepType value, additional state checks may be made.
7960 * These checks will set extended error info on failure. See
7961 * #i_checkStateDependency() for more info.
7962 *
7963 * If this method returns a failure, the dependency is not added and the caller
7964 * is not allowed to rely on any particular machine state or registration state
7965 * value and may return the failed result code to the upper level.
7966 *
7967 * @param aDepType Dependency type to add.
7968 * @param aState Current machine state (NULL if not interested).
7969 * @param aRegistered Current registered state (NULL if not interested).
7970 *
7971 * @note Locks this object for writing.
7972 */
7973HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7974 MachineState_T *aState /* = NULL */,
7975 BOOL *aRegistered /* = NULL */)
7976{
7977 AutoCaller autoCaller(this);
7978 AssertComRCReturnRC(autoCaller.rc());
7979
7980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7981
7982 HRESULT rc = i_checkStateDependency(aDepType);
7983 if (FAILED(rc)) return rc;
7984
7985 {
7986 if (mData->mMachineStateChangePending != 0)
7987 {
7988 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7989 * drop to zero so don't add more. It may make sense to wait a bit
7990 * and retry before reporting an error (since the pending state
7991 * transition should be really quick) but let's just assert for
7992 * now to see if it ever happens on practice. */
7993
7994 AssertFailed();
7995
7996 return setError(E_ACCESSDENIED,
7997 tr("Machine state change is in progress. Please retry the operation later."));
7998 }
7999
8000 ++mData->mMachineStateDeps;
8001 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8002 }
8003
8004 if (aState)
8005 *aState = mData->mMachineState;
8006 if (aRegistered)
8007 *aRegistered = mData->mRegistered;
8008
8009 return S_OK;
8010}
8011
8012/**
8013 * Decreases the number of objects dependent on the machine state.
8014 * Must always complete the #i_addStateDependency() call after the state
8015 * dependency is no more necessary.
8016 */
8017void Machine::i_releaseStateDependency()
8018{
8019 AutoCaller autoCaller(this);
8020 AssertComRCReturnVoid(autoCaller.rc());
8021
8022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8023
8024 /* releaseStateDependency() w/o addStateDependency()? */
8025 AssertReturnVoid(mData->mMachineStateDeps != 0);
8026 -- mData->mMachineStateDeps;
8027
8028 if (mData->mMachineStateDeps == 0)
8029 {
8030 /* inform i_ensureNoStateDependencies() that there are no more deps */
8031 if (mData->mMachineStateChangePending != 0)
8032 {
8033 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8034 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8035 }
8036 }
8037}
8038
8039Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8040{
8041 /* start with nothing found */
8042 Utf8Str strResult("");
8043
8044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8045
8046 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8047 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8048 // found:
8049 strResult = it->second; // source is a Utf8Str
8050
8051 return strResult;
8052}
8053
8054// protected methods
8055/////////////////////////////////////////////////////////////////////////////
8056
8057/**
8058 * Performs machine state checks based on the @a aDepType value. If a check
8059 * fails, this method will set extended error info, otherwise it will return
8060 * S_OK. It is supposed, that on failure, the caller will immediately return
8061 * the return value of this method to the upper level.
8062 *
8063 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8064 *
8065 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8066 * current state of this machine object allows to change settings of the
8067 * machine (i.e. the machine is not registered, or registered but not running
8068 * and not saved). It is useful to call this method from Machine setters
8069 * before performing any change.
8070 *
8071 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8072 * as for MutableStateDep except that if the machine is saved, S_OK is also
8073 * returned. This is useful in setters which allow changing machine
8074 * properties when it is in the saved state.
8075 *
8076 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8077 * if the current state of this machine object allows to change runtime
8078 * changeable settings of the machine (i.e. the machine is not registered, or
8079 * registered but either running or not running and not saved). It is useful
8080 * to call this method from Machine setters before performing any changes to
8081 * runtime changeable settings.
8082 *
8083 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8084 * the same as for MutableOrRunningStateDep except that if the machine is
8085 * saved, S_OK is also returned. This is useful in setters which allow
8086 * changing runtime and saved state changeable machine properties.
8087 *
8088 * @param aDepType Dependency type to check.
8089 *
8090 * @note Non Machine based classes should use #i_addStateDependency() and
8091 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8092 * template.
8093 *
8094 * @note This method must be called from under this object's read or write
8095 * lock.
8096 */
8097HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8098{
8099 switch (aDepType)
8100 {
8101 case AnyStateDep:
8102 {
8103 break;
8104 }
8105 case MutableStateDep:
8106 {
8107 if ( mData->mRegistered
8108 && ( !i_isSessionMachine()
8109 || ( mData->mMachineState != MachineState_Aborted
8110 && mData->mMachineState != MachineState_Teleported
8111 && mData->mMachineState != MachineState_PoweredOff
8112 )
8113 )
8114 )
8115 return setError(VBOX_E_INVALID_VM_STATE,
8116 tr("The machine is not mutable (state is %s)"),
8117 Global::stringifyMachineState(mData->mMachineState));
8118 break;
8119 }
8120 case MutableOrSavedStateDep:
8121 {
8122 if ( mData->mRegistered
8123 && ( !i_isSessionMachine()
8124 || ( mData->mMachineState != MachineState_Aborted
8125 && mData->mMachineState != MachineState_Teleported
8126 && mData->mMachineState != MachineState_Saved
8127 && mData->mMachineState != MachineState_PoweredOff
8128 )
8129 )
8130 )
8131 return setError(VBOX_E_INVALID_VM_STATE,
8132 tr("The machine is not mutable or saved (state is %s)"),
8133 Global::stringifyMachineState(mData->mMachineState));
8134 break;
8135 }
8136 case MutableOrRunningStateDep:
8137 {
8138 if ( mData->mRegistered
8139 && ( !i_isSessionMachine()
8140 || ( mData->mMachineState != MachineState_Aborted
8141 && mData->mMachineState != MachineState_Teleported
8142 && mData->mMachineState != MachineState_PoweredOff
8143 && !Global::IsOnline(mData->mMachineState)
8144 )
8145 )
8146 )
8147 return setError(VBOX_E_INVALID_VM_STATE,
8148 tr("The machine is not mutable or running (state is %s)"),
8149 Global::stringifyMachineState(mData->mMachineState));
8150 break;
8151 }
8152 case MutableOrSavedOrRunningStateDep:
8153 {
8154 if ( mData->mRegistered
8155 && ( !i_isSessionMachine()
8156 || ( mData->mMachineState != MachineState_Aborted
8157 && mData->mMachineState != MachineState_Teleported
8158 && mData->mMachineState != MachineState_Saved
8159 && mData->mMachineState != MachineState_PoweredOff
8160 && !Global::IsOnline(mData->mMachineState)
8161 )
8162 )
8163 )
8164 return setError(VBOX_E_INVALID_VM_STATE,
8165 tr("The machine is not mutable, saved or running (state is %s)"),
8166 Global::stringifyMachineState(mData->mMachineState));
8167 break;
8168 }
8169 }
8170
8171 return S_OK;
8172}
8173
8174/**
8175 * Helper to initialize all associated child objects and allocate data
8176 * structures.
8177 *
8178 * This method must be called as a part of the object's initialization procedure
8179 * (usually done in the #init() method).
8180 *
8181 * @note Must be called only from #init() or from #i_registeredInit().
8182 */
8183HRESULT Machine::initDataAndChildObjects()
8184{
8185 AutoCaller autoCaller(this);
8186 AssertComRCReturnRC(autoCaller.rc());
8187 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8188 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8189
8190 AssertReturn(!mData->mAccessible, E_FAIL);
8191
8192 /* allocate data structures */
8193 mSSData.allocate();
8194 mUserData.allocate();
8195 mHWData.allocate();
8196 mMediumAttachments.allocate();
8197 mStorageControllers.allocate();
8198 mUSBControllers.allocate();
8199
8200 /* initialize mOSTypeId */
8201 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8202
8203/** @todo r=bird: init() methods never fails, right? Why don't we make them
8204 * return void then! */
8205
8206 /* create associated BIOS settings object */
8207 unconst(mBIOSSettings).createObject();
8208 mBIOSSettings->init(this);
8209
8210 /* create associated record settings object */
8211 unconst(mRecordingSettings).createObject();
8212 mRecordingSettings->init(this);
8213
8214 /* create an associated VRDE object (default is disabled) */
8215 unconst(mVRDEServer).createObject();
8216 mVRDEServer->init(this);
8217
8218 /* create associated serial port objects */
8219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8220 {
8221 unconst(mSerialPorts[slot]).createObject();
8222 mSerialPorts[slot]->init(this, slot);
8223 }
8224
8225 /* create associated parallel port objects */
8226 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8227 {
8228 unconst(mParallelPorts[slot]).createObject();
8229 mParallelPorts[slot]->init(this, slot);
8230 }
8231
8232 /* create the audio adapter object (always present, default is disabled) */
8233 unconst(mAudioAdapter).createObject();
8234 mAudioAdapter->init(this);
8235
8236 /* create the USB device filters object (always present) */
8237 unconst(mUSBDeviceFilters).createObject();
8238 mUSBDeviceFilters->init(this);
8239
8240 /* create associated network adapter objects */
8241 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8242 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8243 {
8244 unconst(mNetworkAdapters[slot]).createObject();
8245 mNetworkAdapters[slot]->init(this, slot);
8246 }
8247
8248 /* create the bandwidth control */
8249 unconst(mBandwidthControl).createObject();
8250 mBandwidthControl->init(this);
8251
8252 return S_OK;
8253}
8254
8255/**
8256 * Helper to uninitialize all associated child objects and to free all data
8257 * structures.
8258 *
8259 * This method must be called as a part of the object's uninitialization
8260 * procedure (usually done in the #uninit() method).
8261 *
8262 * @note Must be called only from #uninit() or from #i_registeredInit().
8263 */
8264void Machine::uninitDataAndChildObjects()
8265{
8266 AutoCaller autoCaller(this);
8267 AssertComRCReturnVoid(autoCaller.rc());
8268 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8269 || getObjectState().getState() == ObjectState::Limited);
8270
8271 /* tell all our other child objects we've been uninitialized */
8272 if (mBandwidthControl)
8273 {
8274 mBandwidthControl->uninit();
8275 unconst(mBandwidthControl).setNull();
8276 }
8277
8278 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8279 {
8280 if (mNetworkAdapters[slot])
8281 {
8282 mNetworkAdapters[slot]->uninit();
8283 unconst(mNetworkAdapters[slot]).setNull();
8284 }
8285 }
8286
8287 if (mUSBDeviceFilters)
8288 {
8289 mUSBDeviceFilters->uninit();
8290 unconst(mUSBDeviceFilters).setNull();
8291 }
8292
8293 if (mAudioAdapter)
8294 {
8295 mAudioAdapter->uninit();
8296 unconst(mAudioAdapter).setNull();
8297 }
8298
8299 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8300 {
8301 if (mParallelPorts[slot])
8302 {
8303 mParallelPorts[slot]->uninit();
8304 unconst(mParallelPorts[slot]).setNull();
8305 }
8306 }
8307
8308 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8309 {
8310 if (mSerialPorts[slot])
8311 {
8312 mSerialPorts[slot]->uninit();
8313 unconst(mSerialPorts[slot]).setNull();
8314 }
8315 }
8316
8317 if (mVRDEServer)
8318 {
8319 mVRDEServer->uninit();
8320 unconst(mVRDEServer).setNull();
8321 }
8322
8323 if (mBIOSSettings)
8324 {
8325 mBIOSSettings->uninit();
8326 unconst(mBIOSSettings).setNull();
8327 }
8328
8329 if (mRecordingSettings)
8330 {
8331 mRecordingSettings->uninit();
8332 unconst(mRecordingSettings).setNull();
8333 }
8334
8335 /* Deassociate media (only when a real Machine or a SnapshotMachine
8336 * instance is uninitialized; SessionMachine instances refer to real
8337 * Machine media). This is necessary for a clean re-initialization of
8338 * the VM after successfully re-checking the accessibility state. Note
8339 * that in case of normal Machine or SnapshotMachine uninitialization (as
8340 * a result of unregistering or deleting the snapshot), outdated media
8341 * attachments will already be uninitialized and deleted, so this
8342 * code will not affect them. */
8343 if ( !mMediumAttachments.isNull()
8344 && !i_isSessionMachine()
8345 )
8346 {
8347 for (MediumAttachmentList::const_iterator
8348 it = mMediumAttachments->begin();
8349 it != mMediumAttachments->end();
8350 ++it)
8351 {
8352 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8353 if (pMedium.isNull())
8354 continue;
8355 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8356 AssertComRC(rc);
8357 }
8358 }
8359
8360 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8361 {
8362 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8363 if (mData->mFirstSnapshot)
8364 {
8365 // snapshots tree is protected by machine write lock; strictly
8366 // this isn't necessary here since we're deleting the entire
8367 // machine, but otherwise we assert in Snapshot::uninit()
8368 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8369 mData->mFirstSnapshot->uninit();
8370 mData->mFirstSnapshot.setNull();
8371 }
8372
8373 mData->mCurrentSnapshot.setNull();
8374 }
8375
8376 /* free data structures (the essential mData structure is not freed here
8377 * since it may be still in use) */
8378 mMediumAttachments.free();
8379 mStorageControllers.free();
8380 mUSBControllers.free();
8381 mHWData.free();
8382 mUserData.free();
8383 mSSData.free();
8384}
8385
8386/**
8387 * Returns a pointer to the Machine object for this machine that acts like a
8388 * parent for complex machine data objects such as shared folders, etc.
8389 *
8390 * For primary Machine objects and for SnapshotMachine objects, returns this
8391 * object's pointer itself. For SessionMachine objects, returns the peer
8392 * (primary) machine pointer.
8393 */
8394Machine *Machine::i_getMachine()
8395{
8396 if (i_isSessionMachine())
8397 return (Machine*)mPeer;
8398 return this;
8399}
8400
8401/**
8402 * Makes sure that there are no machine state dependents. If necessary, waits
8403 * for the number of dependents to drop to zero.
8404 *
8405 * Make sure this method is called from under this object's write lock to
8406 * guarantee that no new dependents may be added when this method returns
8407 * control to the caller.
8408 *
8409 * @note Locks this object for writing. The lock will be released while waiting
8410 * (if necessary).
8411 *
8412 * @warning To be used only in methods that change the machine state!
8413 */
8414void Machine::i_ensureNoStateDependencies()
8415{
8416 AssertReturnVoid(isWriteLockOnCurrentThread());
8417
8418 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8419
8420 /* Wait for all state dependents if necessary */
8421 if (mData->mMachineStateDeps != 0)
8422 {
8423 /* lazy semaphore creation */
8424 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8425 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8426
8427 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8428 mData->mMachineStateDeps));
8429
8430 ++mData->mMachineStateChangePending;
8431
8432 /* reset the semaphore before waiting, the last dependent will signal
8433 * it */
8434 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8435
8436 alock.release();
8437
8438 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8439
8440 alock.acquire();
8441
8442 -- mData->mMachineStateChangePending;
8443 }
8444}
8445
8446/**
8447 * Changes the machine state and informs callbacks.
8448 *
8449 * This method is not intended to fail so it either returns S_OK or asserts (and
8450 * returns a failure).
8451 *
8452 * @note Locks this object for writing.
8453 */
8454HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8455{
8456 LogFlowThisFuncEnter();
8457 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8458 Assert(aMachineState != MachineState_Null);
8459
8460 AutoCaller autoCaller(this);
8461 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8462
8463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8464
8465 /* wait for state dependents to drop to zero */
8466 i_ensureNoStateDependencies();
8467
8468 MachineState_T const enmOldState = mData->mMachineState;
8469 if (enmOldState != aMachineState)
8470 {
8471 mData->mMachineState = aMachineState;
8472 RTTimeNow(&mData->mLastStateChange);
8473
8474#ifdef VBOX_WITH_DTRACE_R3_MAIN
8475 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8476#endif
8477 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8478 }
8479
8480 LogFlowThisFuncLeave();
8481 return S_OK;
8482}
8483
8484/**
8485 * Searches for a shared folder with the given logical name
8486 * in the collection of shared folders.
8487 *
8488 * @param aName logical name of the shared folder
8489 * @param aSharedFolder where to return the found object
8490 * @param aSetError whether to set the error info if the folder is
8491 * not found
8492 * @return
8493 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8494 *
8495 * @note
8496 * must be called from under the object's lock!
8497 */
8498HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8499 ComObjPtr<SharedFolder> &aSharedFolder,
8500 bool aSetError /* = false */)
8501{
8502 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8503 for (HWData::SharedFolderList::const_iterator
8504 it = mHWData->mSharedFolders.begin();
8505 it != mHWData->mSharedFolders.end();
8506 ++it)
8507 {
8508 SharedFolder *pSF = *it;
8509 AutoCaller autoCaller(pSF);
8510 if (pSF->i_getName() == aName)
8511 {
8512 aSharedFolder = pSF;
8513 rc = S_OK;
8514 break;
8515 }
8516 }
8517
8518 if (aSetError && FAILED(rc))
8519 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8520
8521 return rc;
8522}
8523
8524/**
8525 * Initializes all machine instance data from the given settings structures
8526 * from XML. The exception is the machine UUID which needs special handling
8527 * depending on the caller's use case, so the caller needs to set that herself.
8528 *
8529 * This gets called in several contexts during machine initialization:
8530 *
8531 * -- When machine XML exists on disk already and needs to be loaded into memory,
8532 * for example, from #i_registeredInit() to load all registered machines on
8533 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8534 * attached to the machine should be part of some media registry already.
8535 *
8536 * -- During OVF import, when a machine config has been constructed from an
8537 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8538 * ensure that the media listed as attachments in the config (which have
8539 * been imported from the OVF) receive the correct registry ID.
8540 *
8541 * -- During VM cloning.
8542 *
8543 * @param config Machine settings from XML.
8544 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8545 * for each attached medium in the config.
8546 * @return
8547 */
8548HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8549 const Guid *puuidRegistry)
8550{
8551 // copy name, description, OS type, teleporter, UTC etc.
8552 mUserData->s = config.machineUserData;
8553
8554 // look up the object by Id to check it is valid
8555 ComObjPtr<GuestOSType> pGuestOSType;
8556 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8557 if (!pGuestOSType.isNull())
8558 mUserData->s.strOsType = pGuestOSType->i_id();
8559
8560 // stateFile (optional)
8561 if (config.strStateFile.isEmpty())
8562 mSSData->strStateFilePath.setNull();
8563 else
8564 {
8565 Utf8Str stateFilePathFull(config.strStateFile);
8566 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8567 if (RT_FAILURE(vrc))
8568 return setErrorBoth(E_FAIL, vrc,
8569 tr("Invalid saved state file path '%s' (%Rrc)"),
8570 config.strStateFile.c_str(),
8571 vrc);
8572 mSSData->strStateFilePath = stateFilePathFull;
8573 }
8574
8575 // snapshot folder needs special processing so set it again
8576 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8577 if (FAILED(rc)) return rc;
8578
8579 /* Copy the extra data items (config may or may not be the same as
8580 * mData->pMachineConfigFile) if necessary. When loading the XML files
8581 * from disk they are the same, but not for OVF import. */
8582 if (mData->pMachineConfigFile != &config)
8583 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8584
8585 /* currentStateModified (optional, default is true) */
8586 mData->mCurrentStateModified = config.fCurrentStateModified;
8587
8588 mData->mLastStateChange = config.timeLastStateChange;
8589
8590 /*
8591 * note: all mUserData members must be assigned prior this point because
8592 * we need to commit changes in order to let mUserData be shared by all
8593 * snapshot machine instances.
8594 */
8595 mUserData.commitCopy();
8596
8597 // machine registry, if present (must be loaded before snapshots)
8598 if (config.canHaveOwnMediaRegistry())
8599 {
8600 // determine machine folder
8601 Utf8Str strMachineFolder = i_getSettingsFileFull();
8602 strMachineFolder.stripFilename();
8603 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8604 config.mediaRegistry,
8605 strMachineFolder);
8606 if (FAILED(rc)) return rc;
8607 }
8608
8609 /* Snapshot node (optional) */
8610 size_t cRootSnapshots;
8611 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8612 {
8613 // there must be only one root snapshot
8614 Assert(cRootSnapshots == 1);
8615
8616 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8617
8618 rc = i_loadSnapshot(snap,
8619 config.uuidCurrentSnapshot,
8620 NULL); // no parent == first snapshot
8621 if (FAILED(rc)) return rc;
8622 }
8623
8624 // hardware data
8625 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8626 if (FAILED(rc)) return rc;
8627
8628 /*
8629 * NOTE: the assignment below must be the last thing to do,
8630 * otherwise it will be not possible to change the settings
8631 * somewhere in the code above because all setters will be
8632 * blocked by i_checkStateDependency(MutableStateDep).
8633 */
8634
8635 /* set the machine state to Aborted or Saved when appropriate */
8636 if (config.fAborted)
8637 {
8638 mSSData->strStateFilePath.setNull();
8639
8640 /* no need to use i_setMachineState() during init() */
8641 mData->mMachineState = MachineState_Aborted;
8642 }
8643 else if (!mSSData->strStateFilePath.isEmpty())
8644 {
8645 /* no need to use i_setMachineState() during init() */
8646 mData->mMachineState = MachineState_Saved;
8647 }
8648
8649 // after loading settings, we are no longer different from the XML on disk
8650 mData->flModifications = 0;
8651
8652 return S_OK;
8653}
8654
8655/**
8656 * Recursively loads all snapshots starting from the given.
8657 *
8658 * @param data snapshot settings.
8659 * @param aCurSnapshotId Current snapshot ID from the settings file.
8660 * @param aParentSnapshot Parent snapshot.
8661 */
8662HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8663 const Guid &aCurSnapshotId,
8664 Snapshot *aParentSnapshot)
8665{
8666 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8667 AssertReturn(!i_isSessionMachine(), E_FAIL);
8668
8669 HRESULT rc = S_OK;
8670
8671 Utf8Str strStateFile;
8672 if (!data.strStateFile.isEmpty())
8673 {
8674 /* optional */
8675 strStateFile = data.strStateFile;
8676 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8677 if (RT_FAILURE(vrc))
8678 return setErrorBoth(E_FAIL, vrc,
8679 tr("Invalid saved state file path '%s' (%Rrc)"),
8680 strStateFile.c_str(),
8681 vrc);
8682 }
8683
8684 /* create a snapshot machine object */
8685 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8686 pSnapshotMachine.createObject();
8687 rc = pSnapshotMachine->initFromSettings(this,
8688 data.hardware,
8689 &data.debugging,
8690 &data.autostart,
8691 data.uuid.ref(),
8692 strStateFile);
8693 if (FAILED(rc)) return rc;
8694
8695 /* create a snapshot object */
8696 ComObjPtr<Snapshot> pSnapshot;
8697 pSnapshot.createObject();
8698 /* initialize the snapshot */
8699 rc = pSnapshot->init(mParent, // VirtualBox object
8700 data.uuid,
8701 data.strName,
8702 data.strDescription,
8703 data.timestamp,
8704 pSnapshotMachine,
8705 aParentSnapshot);
8706 if (FAILED(rc)) return rc;
8707
8708 /* memorize the first snapshot if necessary */
8709 if (!mData->mFirstSnapshot)
8710 mData->mFirstSnapshot = pSnapshot;
8711
8712 /* memorize the current snapshot when appropriate */
8713 if ( !mData->mCurrentSnapshot
8714 && pSnapshot->i_getId() == aCurSnapshotId
8715 )
8716 mData->mCurrentSnapshot = pSnapshot;
8717
8718 // now create the children
8719 for (settings::SnapshotsList::const_iterator
8720 it = data.llChildSnapshots.begin();
8721 it != data.llChildSnapshots.end();
8722 ++it)
8723 {
8724 const settings::Snapshot &childData = *it;
8725 // recurse
8726 rc = i_loadSnapshot(childData,
8727 aCurSnapshotId,
8728 pSnapshot); // parent = the one we created above
8729 if (FAILED(rc)) return rc;
8730 }
8731
8732 return rc;
8733}
8734
8735/**
8736 * Loads settings into mHWData.
8737 *
8738 * @param puuidRegistry Registry ID.
8739 * @param puuidSnapshot Snapshot ID
8740 * @param data Reference to the hardware settings.
8741 * @param pDbg Pointer to the debugging settings.
8742 * @param pAutostart Pointer to the autostart settings.
8743 */
8744HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8745 const Guid *puuidSnapshot,
8746 const settings::Hardware &data,
8747 const settings::Debugging *pDbg,
8748 const settings::Autostart *pAutostart)
8749{
8750 AssertReturn(!i_isSessionMachine(), E_FAIL);
8751
8752 HRESULT rc = S_OK;
8753
8754 try
8755 {
8756 ComObjPtr<GuestOSType> pGuestOSType;
8757 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8758
8759 /* The hardware version attribute (optional). */
8760 mHWData->mHWVersion = data.strVersion;
8761 mHWData->mHardwareUUID = data.uuid;
8762
8763 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8764 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8765 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8766 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8767 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8768 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8769 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8770 mHWData->mPAEEnabled = data.fPAE;
8771 mHWData->mLongMode = data.enmLongMode;
8772 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8773 mHWData->mAPIC = data.fAPIC;
8774 mHWData->mX2APIC = data.fX2APIC;
8775 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8776 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8777 mHWData->mSpecCtrl = data.fSpecCtrl;
8778 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8779 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8780 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8781 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8782 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8783 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8784 mHWData->mCPUCount = data.cCPUs;
8785 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8786 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8787 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8788 mHWData->mCpuProfile = data.strCpuProfile;
8789
8790 // cpu
8791 if (mHWData->mCPUHotPlugEnabled)
8792 {
8793 for (settings::CpuList::const_iterator
8794 it = data.llCpus.begin();
8795 it != data.llCpus.end();
8796 ++it)
8797 {
8798 const settings::Cpu &cpu = *it;
8799
8800 mHWData->mCPUAttached[cpu.ulId] = true;
8801 }
8802 }
8803
8804 // cpuid leafs
8805 for (settings::CpuIdLeafsList::const_iterator
8806 it = data.llCpuIdLeafs.begin();
8807 it != data.llCpuIdLeafs.end();
8808 ++it)
8809 {
8810 const settings::CpuIdLeaf &rLeaf= *it;
8811 if ( rLeaf.idx < UINT32_C(0x20)
8812 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8813 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8814 mHWData->mCpuIdLeafList.push_back(rLeaf);
8815 /* else: just ignore */
8816 }
8817
8818 mHWData->mMemorySize = data.ulMemorySizeMB;
8819 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8820
8821 // boot order
8822 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8823 {
8824 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8825 if (it == data.mapBootOrder.end())
8826 mHWData->mBootOrder[i] = DeviceType_Null;
8827 else
8828 mHWData->mBootOrder[i] = it->second;
8829 }
8830
8831 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8832 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8833 mHWData->mMonitorCount = data.cMonitors;
8834 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8835 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8836 mHWData->mFirmwareType = data.firmwareType;
8837 mHWData->mPointingHIDType = data.pointingHIDType;
8838 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8839 mHWData->mChipsetType = data.chipsetType;
8840 mHWData->mParavirtProvider = data.paravirtProvider;
8841 mHWData->mParavirtDebug = data.strParavirtDebug;
8842 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8843 mHWData->mHPETEnabled = data.fHPETEnabled;
8844
8845 /* VRDEServer */
8846 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8847 if (FAILED(rc)) return rc;
8848
8849 /* BIOS */
8850 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8851 if (FAILED(rc)) return rc;
8852
8853 /* Recording settings */
8854 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8855 if (FAILED(rc)) return rc;
8856
8857 // Bandwidth control (must come before network adapters)
8858 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8859 if (FAILED(rc)) return rc;
8860
8861 /* USB controllers */
8862 for (settings::USBControllerList::const_iterator
8863 it = data.usbSettings.llUSBControllers.begin();
8864 it != data.usbSettings.llUSBControllers.end();
8865 ++it)
8866 {
8867 const settings::USBController &settingsCtrl = *it;
8868 ComObjPtr<USBController> newCtrl;
8869
8870 newCtrl.createObject();
8871 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8872 mUSBControllers->push_back(newCtrl);
8873 }
8874
8875 /* USB device filters */
8876 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8877 if (FAILED(rc)) return rc;
8878
8879 // network adapters (establish array size first and apply defaults, to
8880 // ensure reading the same settings as we saved, since the list skips
8881 // adapters having defaults)
8882 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8883 size_t oldCount = mNetworkAdapters.size();
8884 if (newCount > oldCount)
8885 {
8886 mNetworkAdapters.resize(newCount);
8887 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8888 {
8889 unconst(mNetworkAdapters[slot]).createObject();
8890 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8891 }
8892 }
8893 else if (newCount < oldCount)
8894 mNetworkAdapters.resize(newCount);
8895 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8896 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8897 for (settings::NetworkAdaptersList::const_iterator
8898 it = data.llNetworkAdapters.begin();
8899 it != data.llNetworkAdapters.end();
8900 ++it)
8901 {
8902 const settings::NetworkAdapter &nic = *it;
8903
8904 /* slot uniqueness is guaranteed by XML Schema */
8905 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8906 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8907 if (FAILED(rc)) return rc;
8908 }
8909
8910 // serial ports (establish defaults first, to ensure reading the same
8911 // settings as we saved, since the list skips ports having defaults)
8912 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8913 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8914 for (settings::SerialPortsList::const_iterator
8915 it = data.llSerialPorts.begin();
8916 it != data.llSerialPorts.end();
8917 ++it)
8918 {
8919 const settings::SerialPort &s = *it;
8920
8921 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8922 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8923 if (FAILED(rc)) return rc;
8924 }
8925
8926 // parallel ports (establish defaults first, to ensure reading the same
8927 // settings as we saved, since the list skips ports having defaults)
8928 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8929 mParallelPorts[i]->i_applyDefaults();
8930 for (settings::ParallelPortsList::const_iterator
8931 it = data.llParallelPorts.begin();
8932 it != data.llParallelPorts.end();
8933 ++it)
8934 {
8935 const settings::ParallelPort &p = *it;
8936
8937 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8938 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8939 if (FAILED(rc)) return rc;
8940 }
8941
8942 /* AudioAdapter */
8943 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8944 if (FAILED(rc)) return rc;
8945
8946 /* storage controllers */
8947 rc = i_loadStorageControllers(data.storage,
8948 puuidRegistry,
8949 puuidSnapshot);
8950 if (FAILED(rc)) return rc;
8951
8952 /* Shared folders */
8953 for (settings::SharedFoldersList::const_iterator
8954 it = data.llSharedFolders.begin();
8955 it != data.llSharedFolders.end();
8956 ++it)
8957 {
8958 const settings::SharedFolder &sf = *it;
8959
8960 ComObjPtr<SharedFolder> sharedFolder;
8961 /* Check for double entries. Not allowed! */
8962 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8963 if (SUCCEEDED(rc))
8964 return setError(VBOX_E_OBJECT_IN_USE,
8965 tr("Shared folder named '%s' already exists"),
8966 sf.strName.c_str());
8967
8968 /* Create the new shared folder. Don't break on error. This will be
8969 * reported when the machine starts. */
8970 sharedFolder.createObject();
8971 rc = sharedFolder->init(i_getMachine(),
8972 sf.strName,
8973 sf.strHostPath,
8974 RT_BOOL(sf.fWritable),
8975 RT_BOOL(sf.fAutoMount),
8976 sf.strAutoMountPoint,
8977 false /* fFailOnError */);
8978 if (FAILED(rc)) return rc;
8979 mHWData->mSharedFolders.push_back(sharedFolder);
8980 }
8981
8982 // Clipboard
8983 mHWData->mClipboardMode = data.clipboardMode;
8984
8985 // drag'n'drop
8986 mHWData->mDnDMode = data.dndMode;
8987
8988 // guest settings
8989 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8990
8991 // IO settings
8992 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8993 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8994
8995 // Host PCI devices
8996 for (settings::HostPCIDeviceAttachmentList::const_iterator
8997 it = data.pciAttachments.begin();
8998 it != data.pciAttachments.end();
8999 ++it)
9000 {
9001 const settings::HostPCIDeviceAttachment &hpda = *it;
9002 ComObjPtr<PCIDeviceAttachment> pda;
9003
9004 pda.createObject();
9005 pda->i_loadSettings(this, hpda);
9006 mHWData->mPCIDeviceAssignments.push_back(pda);
9007 }
9008
9009 /*
9010 * (The following isn't really real hardware, but it lives in HWData
9011 * for reasons of convenience.)
9012 */
9013
9014#ifdef VBOX_WITH_GUEST_PROPS
9015 /* Guest properties (optional) */
9016
9017 /* Only load transient guest properties for configs which have saved
9018 * state, because there shouldn't be any for powered off VMs. The same
9019 * logic applies for snapshots, as offline snapshots shouldn't have
9020 * any such properties. They confuse the code in various places.
9021 * Note: can't rely on the machine state, as it isn't set yet. */
9022 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9023 /* apologies for the hacky unconst() usage, but this needs hacking
9024 * actually inconsistent settings into consistency, otherwise there
9025 * will be some corner cases where the inconsistency survives
9026 * surprisingly long without getting fixed, especially for snapshots
9027 * as there are no config changes. */
9028 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9029 for (settings::GuestPropertiesList::iterator
9030 it = llGuestProperties.begin();
9031 it != llGuestProperties.end();
9032 /*nothing*/)
9033 {
9034 const settings::GuestProperty &prop = *it;
9035 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9036 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9037 if ( fSkipTransientGuestProperties
9038 && ( fFlags & GUEST_PROP_F_TRANSIENT
9039 || fFlags & GUEST_PROP_F_TRANSRESET))
9040 {
9041 it = llGuestProperties.erase(it);
9042 continue;
9043 }
9044 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9045 mHWData->mGuestProperties[prop.strName] = property;
9046 ++it;
9047 }
9048#endif /* VBOX_WITH_GUEST_PROPS defined */
9049
9050 rc = i_loadDebugging(pDbg);
9051 if (FAILED(rc))
9052 return rc;
9053
9054 mHWData->mAutostart = *pAutostart;
9055
9056 /* default frontend */
9057 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9058 }
9059 catch (std::bad_alloc &)
9060 {
9061 return E_OUTOFMEMORY;
9062 }
9063
9064 AssertComRC(rc);
9065 return rc;
9066}
9067
9068/**
9069 * Called from i_loadHardware() to load the debugging settings of the
9070 * machine.
9071 *
9072 * @param pDbg Pointer to the settings.
9073 */
9074HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9075{
9076 mHWData->mDebugging = *pDbg;
9077 /* no more processing currently required, this will probably change. */
9078 return S_OK;
9079}
9080
9081/**
9082 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9083 *
9084 * @param data storage settings.
9085 * @param puuidRegistry media registry ID to set media to or NULL;
9086 * see Machine::i_loadMachineDataFromSettings()
9087 * @param puuidSnapshot snapshot ID
9088 * @return
9089 */
9090HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9091 const Guid *puuidRegistry,
9092 const Guid *puuidSnapshot)
9093{
9094 AssertReturn(!i_isSessionMachine(), E_FAIL);
9095
9096 HRESULT rc = S_OK;
9097
9098 for (settings::StorageControllersList::const_iterator
9099 it = data.llStorageControllers.begin();
9100 it != data.llStorageControllers.end();
9101 ++it)
9102 {
9103 const settings::StorageController &ctlData = *it;
9104
9105 ComObjPtr<StorageController> pCtl;
9106 /* Try to find one with the name first. */
9107 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9108 if (SUCCEEDED(rc))
9109 return setError(VBOX_E_OBJECT_IN_USE,
9110 tr("Storage controller named '%s' already exists"),
9111 ctlData.strName.c_str());
9112
9113 pCtl.createObject();
9114 rc = pCtl->init(this,
9115 ctlData.strName,
9116 ctlData.storageBus,
9117 ctlData.ulInstance,
9118 ctlData.fBootable);
9119 if (FAILED(rc)) return rc;
9120
9121 mStorageControllers->push_back(pCtl);
9122
9123 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9124 if (FAILED(rc)) return rc;
9125
9126 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9127 if (FAILED(rc)) return rc;
9128
9129 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9130 if (FAILED(rc)) return rc;
9131
9132 /* Load the attached devices now. */
9133 rc = i_loadStorageDevices(pCtl,
9134 ctlData,
9135 puuidRegistry,
9136 puuidSnapshot);
9137 if (FAILED(rc)) return rc;
9138 }
9139
9140 return S_OK;
9141}
9142
9143/**
9144 * Called from i_loadStorageControllers for a controller's devices.
9145 *
9146 * @param aStorageController
9147 * @param data
9148 * @param puuidRegistry media registry ID to set media to or NULL; see
9149 * Machine::i_loadMachineDataFromSettings()
9150 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9151 * @return
9152 */
9153HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9154 const settings::StorageController &data,
9155 const Guid *puuidRegistry,
9156 const Guid *puuidSnapshot)
9157{
9158 HRESULT rc = S_OK;
9159
9160 /* paranoia: detect duplicate attachments */
9161 for (settings::AttachedDevicesList::const_iterator
9162 it = data.llAttachedDevices.begin();
9163 it != data.llAttachedDevices.end();
9164 ++it)
9165 {
9166 const settings::AttachedDevice &ad = *it;
9167
9168 for (settings::AttachedDevicesList::const_iterator it2 = it;
9169 it2 != data.llAttachedDevices.end();
9170 ++it2)
9171 {
9172 if (it == it2)
9173 continue;
9174
9175 const settings::AttachedDevice &ad2 = *it2;
9176
9177 if ( ad.lPort == ad2.lPort
9178 && ad.lDevice == ad2.lDevice)
9179 {
9180 return setError(E_FAIL,
9181 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9182 aStorageController->i_getName().c_str(),
9183 ad.lPort,
9184 ad.lDevice,
9185 mUserData->s.strName.c_str());
9186 }
9187 }
9188 }
9189
9190 for (settings::AttachedDevicesList::const_iterator
9191 it = data.llAttachedDevices.begin();
9192 it != data.llAttachedDevices.end();
9193 ++it)
9194 {
9195 const settings::AttachedDevice &dev = *it;
9196 ComObjPtr<Medium> medium;
9197
9198 switch (dev.deviceType)
9199 {
9200 case DeviceType_Floppy:
9201 case DeviceType_DVD:
9202 if (dev.strHostDriveSrc.isNotEmpty())
9203 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9204 false /* fRefresh */, medium);
9205 else
9206 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9207 dev.uuid,
9208 false /* fRefresh */,
9209 false /* aSetError */,
9210 medium);
9211 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9212 // This is not an error. The host drive or UUID might have vanished, so just go
9213 // ahead without this removeable medium attachment
9214 rc = S_OK;
9215 break;
9216
9217 case DeviceType_HardDisk:
9218 {
9219 /* find a hard disk by UUID */
9220 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9221 if (FAILED(rc))
9222 {
9223 if (i_isSnapshotMachine())
9224 {
9225 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9226 // so the user knows that the bad disk is in a snapshot somewhere
9227 com::ErrorInfo info;
9228 return setError(E_FAIL,
9229 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9230 puuidSnapshot->raw(),
9231 info.getText().raw());
9232 }
9233 else
9234 return rc;
9235 }
9236
9237 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9238
9239 if (medium->i_getType() == MediumType_Immutable)
9240 {
9241 if (i_isSnapshotMachine())
9242 return setError(E_FAIL,
9243 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9244 "of the virtual machine '%s' ('%s')"),
9245 medium->i_getLocationFull().c_str(),
9246 dev.uuid.raw(),
9247 puuidSnapshot->raw(),
9248 mUserData->s.strName.c_str(),
9249 mData->m_strConfigFileFull.c_str());
9250
9251 return setError(E_FAIL,
9252 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9253 medium->i_getLocationFull().c_str(),
9254 dev.uuid.raw(),
9255 mUserData->s.strName.c_str(),
9256 mData->m_strConfigFileFull.c_str());
9257 }
9258
9259 if (medium->i_getType() == MediumType_MultiAttach)
9260 {
9261 if (i_isSnapshotMachine())
9262 return setError(E_FAIL,
9263 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9264 "of the virtual machine '%s' ('%s')"),
9265 medium->i_getLocationFull().c_str(),
9266 dev.uuid.raw(),
9267 puuidSnapshot->raw(),
9268 mUserData->s.strName.c_str(),
9269 mData->m_strConfigFileFull.c_str());
9270
9271 return setError(E_FAIL,
9272 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9273 medium->i_getLocationFull().c_str(),
9274 dev.uuid.raw(),
9275 mUserData->s.strName.c_str(),
9276 mData->m_strConfigFileFull.c_str());
9277 }
9278
9279 if ( !i_isSnapshotMachine()
9280 && medium->i_getChildren().size() != 0
9281 )
9282 return setError(E_FAIL,
9283 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9284 "because it has %d differencing child hard disks"),
9285 medium->i_getLocationFull().c_str(),
9286 dev.uuid.raw(),
9287 mUserData->s.strName.c_str(),
9288 mData->m_strConfigFileFull.c_str(),
9289 medium->i_getChildren().size());
9290
9291 if (i_findAttachment(*mMediumAttachments.data(),
9292 medium))
9293 return setError(E_FAIL,
9294 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9295 medium->i_getLocationFull().c_str(),
9296 dev.uuid.raw(),
9297 mUserData->s.strName.c_str(),
9298 mData->m_strConfigFileFull.c_str());
9299
9300 break;
9301 }
9302
9303 default:
9304 return setError(E_FAIL,
9305 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9306 medium->i_getLocationFull().c_str(),
9307 mUserData->s.strName.c_str(),
9308 mData->m_strConfigFileFull.c_str());
9309 }
9310
9311 if (FAILED(rc))
9312 break;
9313
9314 /* Bandwidth groups are loaded at this point. */
9315 ComObjPtr<BandwidthGroup> pBwGroup;
9316
9317 if (!dev.strBwGroup.isEmpty())
9318 {
9319 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9320 if (FAILED(rc))
9321 return setError(E_FAIL,
9322 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9323 medium->i_getLocationFull().c_str(),
9324 dev.strBwGroup.c_str(),
9325 mUserData->s.strName.c_str(),
9326 mData->m_strConfigFileFull.c_str());
9327 pBwGroup->i_reference();
9328 }
9329
9330 const Utf8Str controllerName = aStorageController->i_getName();
9331 ComObjPtr<MediumAttachment> pAttachment;
9332 pAttachment.createObject();
9333 rc = pAttachment->init(this,
9334 medium,
9335 controllerName,
9336 dev.lPort,
9337 dev.lDevice,
9338 dev.deviceType,
9339 false,
9340 dev.fPassThrough,
9341 dev.fTempEject,
9342 dev.fNonRotational,
9343 dev.fDiscard,
9344 dev.fHotPluggable,
9345 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9346 if (FAILED(rc)) break;
9347
9348 /* associate the medium with this machine and snapshot */
9349 if (!medium.isNull())
9350 {
9351 AutoCaller medCaller(medium);
9352 if (FAILED(medCaller.rc())) return medCaller.rc();
9353 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9354
9355 if (i_isSnapshotMachine())
9356 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9357 else
9358 rc = medium->i_addBackReference(mData->mUuid);
9359 /* If the medium->addBackReference fails it sets an appropriate
9360 * error message, so no need to do any guesswork here. */
9361
9362 if (puuidRegistry)
9363 // caller wants registry ID to be set on all attached media (OVF import case)
9364 medium->i_addRegistry(*puuidRegistry);
9365 }
9366
9367 if (FAILED(rc))
9368 break;
9369
9370 /* back up mMediumAttachments to let registeredInit() properly rollback
9371 * on failure (= limited accessibility) */
9372 i_setModified(IsModified_Storage);
9373 mMediumAttachments.backup();
9374 mMediumAttachments->push_back(pAttachment);
9375 }
9376
9377 return rc;
9378}
9379
9380/**
9381 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9382 *
9383 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9384 * @param aSnapshot where to return the found snapshot
9385 * @param aSetError true to set extended error info on failure
9386 */
9387HRESULT Machine::i_findSnapshotById(const Guid &aId,
9388 ComObjPtr<Snapshot> &aSnapshot,
9389 bool aSetError /* = false */)
9390{
9391 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9392
9393 if (!mData->mFirstSnapshot)
9394 {
9395 if (aSetError)
9396 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9397 return E_FAIL;
9398 }
9399
9400 if (aId.isZero())
9401 aSnapshot = mData->mFirstSnapshot;
9402 else
9403 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9404
9405 if (!aSnapshot)
9406 {
9407 if (aSetError)
9408 return setError(E_FAIL,
9409 tr("Could not find a snapshot with UUID {%s}"),
9410 aId.toString().c_str());
9411 return E_FAIL;
9412 }
9413
9414 return S_OK;
9415}
9416
9417/**
9418 * Returns the snapshot with the given name or fails of no such snapshot.
9419 *
9420 * @param strName snapshot name to find
9421 * @param aSnapshot where to return the found snapshot
9422 * @param aSetError true to set extended error info on failure
9423 */
9424HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9425 ComObjPtr<Snapshot> &aSnapshot,
9426 bool aSetError /* = false */)
9427{
9428 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9429
9430 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9431
9432 if (!mData->mFirstSnapshot)
9433 {
9434 if (aSetError)
9435 return setError(VBOX_E_OBJECT_NOT_FOUND,
9436 tr("This machine does not have any snapshots"));
9437 return VBOX_E_OBJECT_NOT_FOUND;
9438 }
9439
9440 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9441
9442 if (!aSnapshot)
9443 {
9444 if (aSetError)
9445 return setError(VBOX_E_OBJECT_NOT_FOUND,
9446 tr("Could not find a snapshot named '%s'"), strName.c_str());
9447 return VBOX_E_OBJECT_NOT_FOUND;
9448 }
9449
9450 return S_OK;
9451}
9452
9453/**
9454 * Returns a storage controller object with the given name.
9455 *
9456 * @param aName storage controller name to find
9457 * @param aStorageController where to return the found storage controller
9458 * @param aSetError true to set extended error info on failure
9459 */
9460HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9461 ComObjPtr<StorageController> &aStorageController,
9462 bool aSetError /* = false */)
9463{
9464 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9465
9466 for (StorageControllerList::const_iterator
9467 it = mStorageControllers->begin();
9468 it != mStorageControllers->end();
9469 ++it)
9470 {
9471 if ((*it)->i_getName() == aName)
9472 {
9473 aStorageController = (*it);
9474 return S_OK;
9475 }
9476 }
9477
9478 if (aSetError)
9479 return setError(VBOX_E_OBJECT_NOT_FOUND,
9480 tr("Could not find a storage controller named '%s'"),
9481 aName.c_str());
9482 return VBOX_E_OBJECT_NOT_FOUND;
9483}
9484
9485/**
9486 * Returns a USB controller object with the given name.
9487 *
9488 * @param aName USB controller name to find
9489 * @param aUSBController where to return the found USB controller
9490 * @param aSetError true to set extended error info on failure
9491 */
9492HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9493 ComObjPtr<USBController> &aUSBController,
9494 bool aSetError /* = false */)
9495{
9496 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9497
9498 for (USBControllerList::const_iterator
9499 it = mUSBControllers->begin();
9500 it != mUSBControllers->end();
9501 ++it)
9502 {
9503 if ((*it)->i_getName() == aName)
9504 {
9505 aUSBController = (*it);
9506 return S_OK;
9507 }
9508 }
9509
9510 if (aSetError)
9511 return setError(VBOX_E_OBJECT_NOT_FOUND,
9512 tr("Could not find a storage controller named '%s'"),
9513 aName.c_str());
9514 return VBOX_E_OBJECT_NOT_FOUND;
9515}
9516
9517/**
9518 * Returns the number of USB controller instance of the given type.
9519 *
9520 * @param enmType USB controller type.
9521 */
9522ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9523{
9524 ULONG cCtrls = 0;
9525
9526 for (USBControllerList::const_iterator
9527 it = mUSBControllers->begin();
9528 it != mUSBControllers->end();
9529 ++it)
9530 {
9531 if ((*it)->i_getControllerType() == enmType)
9532 cCtrls++;
9533 }
9534
9535 return cCtrls;
9536}
9537
9538HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9539 MediumAttachmentList &atts)
9540{
9541 AutoCaller autoCaller(this);
9542 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9543
9544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9545
9546 for (MediumAttachmentList::const_iterator
9547 it = mMediumAttachments->begin();
9548 it != mMediumAttachments->end();
9549 ++it)
9550 {
9551 const ComObjPtr<MediumAttachment> &pAtt = *it;
9552 // should never happen, but deal with NULL pointers in the list.
9553 AssertContinue(!pAtt.isNull());
9554
9555 // getControllerName() needs caller+read lock
9556 AutoCaller autoAttCaller(pAtt);
9557 if (FAILED(autoAttCaller.rc()))
9558 {
9559 atts.clear();
9560 return autoAttCaller.rc();
9561 }
9562 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9563
9564 if (pAtt->i_getControllerName() == aName)
9565 atts.push_back(pAtt);
9566 }
9567
9568 return S_OK;
9569}
9570
9571
9572/**
9573 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9574 * file if the machine name was changed and about creating a new settings file
9575 * if this is a new machine.
9576 *
9577 * @note Must be never called directly but only from #saveSettings().
9578 */
9579HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9580{
9581 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9582
9583 HRESULT rc = S_OK;
9584
9585 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9586
9587 /// @todo need to handle primary group change, too
9588
9589 /* attempt to rename the settings file if machine name is changed */
9590 if ( mUserData->s.fNameSync
9591 && mUserData.isBackedUp()
9592 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9593 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9594 )
9595 {
9596 bool dirRenamed = false;
9597 bool fileRenamed = false;
9598
9599 Utf8Str configFile, newConfigFile;
9600 Utf8Str configFilePrev, newConfigFilePrev;
9601 Utf8Str configDir, newConfigDir;
9602
9603 do
9604 {
9605 int vrc = VINF_SUCCESS;
9606
9607 Utf8Str name = mUserData.backedUpData()->s.strName;
9608 Utf8Str newName = mUserData->s.strName;
9609 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9610 if (group == "/")
9611 group.setNull();
9612 Utf8Str newGroup = mUserData->s.llGroups.front();
9613 if (newGroup == "/")
9614 newGroup.setNull();
9615
9616 configFile = mData->m_strConfigFileFull;
9617
9618 /* first, rename the directory if it matches the group and machine name */
9619 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9620 group.c_str(), RTPATH_DELIMITER, name.c_str());
9621 /** @todo hack, make somehow use of ComposeMachineFilename */
9622 if (mUserData->s.fDirectoryIncludesUUID)
9623 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9624 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9625 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9626 /** @todo hack, make somehow use of ComposeMachineFilename */
9627 if (mUserData->s.fDirectoryIncludesUUID)
9628 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9629 configDir = configFile;
9630 configDir.stripFilename();
9631 newConfigDir = configDir;
9632 if ( configDir.length() >= groupPlusName.length()
9633 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9634 groupPlusName.c_str()))
9635 {
9636 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9637 Utf8Str newConfigBaseDir(newConfigDir);
9638 newConfigDir.append(newGroupPlusName);
9639 /* consistency: use \ if appropriate on the platform */
9640 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9641 /* new dir and old dir cannot be equal here because of 'if'
9642 * above and because name != newName */
9643 Assert(configDir != newConfigDir);
9644 if (!fSettingsFileIsNew)
9645 {
9646 /* perform real rename only if the machine is not new */
9647 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9648 if ( vrc == VERR_FILE_NOT_FOUND
9649 || vrc == VERR_PATH_NOT_FOUND)
9650 {
9651 /* create the parent directory, then retry renaming */
9652 Utf8Str parent(newConfigDir);
9653 parent.stripFilename();
9654 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9655 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9656 }
9657 if (RT_FAILURE(vrc))
9658 {
9659 rc = setErrorBoth(E_FAIL, vrc,
9660 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9661 configDir.c_str(),
9662 newConfigDir.c_str(),
9663 vrc);
9664 break;
9665 }
9666 /* delete subdirectories which are no longer needed */
9667 Utf8Str dir(configDir);
9668 dir.stripFilename();
9669 while (dir != newConfigBaseDir && dir != ".")
9670 {
9671 vrc = RTDirRemove(dir.c_str());
9672 if (RT_FAILURE(vrc))
9673 break;
9674 dir.stripFilename();
9675 }
9676 dirRenamed = true;
9677 }
9678 }
9679
9680 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9681 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9682
9683 /* then try to rename the settings file itself */
9684 if (newConfigFile != configFile)
9685 {
9686 /* get the path to old settings file in renamed directory */
9687 configFile = Utf8StrFmt("%s%c%s",
9688 newConfigDir.c_str(),
9689 RTPATH_DELIMITER,
9690 RTPathFilename(configFile.c_str()));
9691 if (!fSettingsFileIsNew)
9692 {
9693 /* perform real rename only if the machine is not new */
9694 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9695 if (RT_FAILURE(vrc))
9696 {
9697 rc = setErrorBoth(E_FAIL, vrc,
9698 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9699 configFile.c_str(),
9700 newConfigFile.c_str(),
9701 vrc);
9702 break;
9703 }
9704 fileRenamed = true;
9705 configFilePrev = configFile;
9706 configFilePrev += "-prev";
9707 newConfigFilePrev = newConfigFile;
9708 newConfigFilePrev += "-prev";
9709 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9710 }
9711 }
9712
9713 // update m_strConfigFileFull amd mConfigFile
9714 mData->m_strConfigFileFull = newConfigFile;
9715 // compute the relative path too
9716 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9717
9718 // store the old and new so that VirtualBox::i_saveSettings() can update
9719 // the media registry
9720 if ( mData->mRegistered
9721 && (configDir != newConfigDir || configFile != newConfigFile))
9722 {
9723 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9724
9725 if (pfNeedsGlobalSaveSettings)
9726 *pfNeedsGlobalSaveSettings = true;
9727 }
9728
9729 // in the saved state file path, replace the old directory with the new directory
9730 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9731 {
9732 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9733 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9734 }
9735
9736 // and do the same thing for the saved state file paths of all the online snapshots
9737 if (mData->mFirstSnapshot)
9738 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9739 newConfigDir.c_str());
9740 }
9741 while (0);
9742
9743 if (FAILED(rc))
9744 {
9745 /* silently try to rename everything back */
9746 if (fileRenamed)
9747 {
9748 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9749 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9750 }
9751 if (dirRenamed)
9752 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9753 }
9754
9755 if (FAILED(rc)) return rc;
9756 }
9757
9758 if (fSettingsFileIsNew)
9759 {
9760 /* create a virgin config file */
9761 int vrc = VINF_SUCCESS;
9762
9763 /* ensure the settings directory exists */
9764 Utf8Str path(mData->m_strConfigFileFull);
9765 path.stripFilename();
9766 if (!RTDirExists(path.c_str()))
9767 {
9768 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9769 if (RT_FAILURE(vrc))
9770 {
9771 return setErrorBoth(E_FAIL, vrc,
9772 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9773 path.c_str(),
9774 vrc);
9775 }
9776 }
9777
9778 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9779 path = Utf8Str(mData->m_strConfigFileFull);
9780 RTFILE f = NIL_RTFILE;
9781 vrc = RTFileOpen(&f, path.c_str(),
9782 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9783 if (RT_FAILURE(vrc))
9784 return setErrorBoth(E_FAIL, vrc,
9785 tr("Could not create the settings file '%s' (%Rrc)"),
9786 path.c_str(),
9787 vrc);
9788 RTFileClose(f);
9789 }
9790
9791 return rc;
9792}
9793
9794/**
9795 * Saves and commits machine data, user data and hardware data.
9796 *
9797 * Note that on failure, the data remains uncommitted.
9798 *
9799 * @a aFlags may combine the following flags:
9800 *
9801 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9802 * Used when saving settings after an operation that makes them 100%
9803 * correspond to the settings from the current snapshot.
9804 * - SaveS_Force: settings will be saved without doing a deep compare of the
9805 * settings structures. This is used when this is called because snapshots
9806 * have changed to avoid the overhead of the deep compare.
9807 *
9808 * @note Must be called from under this object's write lock. Locks children for
9809 * writing.
9810 *
9811 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9812 * initialized to false and that will be set to true by this function if
9813 * the caller must invoke VirtualBox::i_saveSettings() because the global
9814 * settings have changed. This will happen if a machine rename has been
9815 * saved and the global machine and media registries will therefore need
9816 * updating.
9817 * @param aFlags Flags.
9818 */
9819HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9820 int aFlags /*= 0*/)
9821{
9822 LogFlowThisFuncEnter();
9823
9824 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9825
9826 /* make sure child objects are unable to modify the settings while we are
9827 * saving them */
9828 i_ensureNoStateDependencies();
9829
9830 AssertReturn(!i_isSnapshotMachine(),
9831 E_FAIL);
9832
9833 if (!mData->mAccessible)
9834 return setError(VBOX_E_INVALID_VM_STATE,
9835 tr("The machine is not accessible, so cannot save settings"));
9836
9837 HRESULT rc = S_OK;
9838 bool fNeedsWrite = false;
9839
9840 /* First, prepare to save settings. It will care about renaming the
9841 * settings directory and file if the machine name was changed and about
9842 * creating a new settings file if this is a new machine. */
9843 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9844 if (FAILED(rc)) return rc;
9845
9846 // keep a pointer to the current settings structures
9847 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9848 settings::MachineConfigFile *pNewConfig = NULL;
9849
9850 try
9851 {
9852 // make a fresh one to have everyone write stuff into
9853 pNewConfig = new settings::MachineConfigFile(NULL);
9854 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9855
9856 // now go and copy all the settings data from COM to the settings structures
9857 // (this calls i_saveSettings() on all the COM objects in the machine)
9858 i_copyMachineDataToSettings(*pNewConfig);
9859
9860 if (aFlags & SaveS_ResetCurStateModified)
9861 {
9862 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9863 mData->mCurrentStateModified = FALSE;
9864 fNeedsWrite = true; // always, no need to compare
9865 }
9866 else if (aFlags & SaveS_Force)
9867 {
9868 fNeedsWrite = true; // always, no need to compare
9869 }
9870 else
9871 {
9872 if (!mData->mCurrentStateModified)
9873 {
9874 // do a deep compare of the settings that we just saved with the settings
9875 // previously stored in the config file; this invokes MachineConfigFile::operator==
9876 // which does a deep compare of all the settings, which is expensive but less expensive
9877 // than writing out XML in vain
9878 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9879
9880 // could still be modified if any settings changed
9881 mData->mCurrentStateModified = fAnySettingsChanged;
9882
9883 fNeedsWrite = fAnySettingsChanged;
9884 }
9885 else
9886 fNeedsWrite = true;
9887 }
9888
9889 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9890
9891 if (fNeedsWrite)
9892 // now spit it all out!
9893 pNewConfig->write(mData->m_strConfigFileFull);
9894
9895 mData->pMachineConfigFile = pNewConfig;
9896 delete pOldConfig;
9897 i_commit();
9898
9899 // after saving settings, we are no longer different from the XML on disk
9900 mData->flModifications = 0;
9901 }
9902 catch (HRESULT err)
9903 {
9904 // we assume that error info is set by the thrower
9905 rc = err;
9906
9907 // restore old config
9908 delete pNewConfig;
9909 mData->pMachineConfigFile = pOldConfig;
9910 }
9911 catch (...)
9912 {
9913 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9914 }
9915
9916 if (fNeedsWrite)
9917 {
9918 /* Fire the data change event, even on failure (since we've already
9919 * committed all data). This is done only for SessionMachines because
9920 * mutable Machine instances are always not registered (i.e. private
9921 * to the client process that creates them) and thus don't need to
9922 * inform callbacks. */
9923 if (i_isSessionMachine())
9924 mParent->i_onMachineDataChange(mData->mUuid);
9925 }
9926
9927 LogFlowThisFunc(("rc=%08X\n", rc));
9928 LogFlowThisFuncLeave();
9929 return rc;
9930}
9931
9932/**
9933 * Implementation for saving the machine settings into the given
9934 * settings::MachineConfigFile instance. This copies machine extradata
9935 * from the previous machine config file in the instance data, if any.
9936 *
9937 * This gets called from two locations:
9938 *
9939 * -- Machine::i_saveSettings(), during the regular XML writing;
9940 *
9941 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9942 * exported to OVF and we write the VirtualBox proprietary XML
9943 * into a <vbox:Machine> tag.
9944 *
9945 * This routine fills all the fields in there, including snapshots, *except*
9946 * for the following:
9947 *
9948 * -- fCurrentStateModified. There is some special logic associated with that.
9949 *
9950 * The caller can then call MachineConfigFile::write() or do something else
9951 * with it.
9952 *
9953 * Caller must hold the machine lock!
9954 *
9955 * This throws XML errors and HRESULT, so the caller must have a catch block!
9956 */
9957void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9958{
9959 // deep copy extradata, being extra careful with self assignment (the STL
9960 // map assignment on Mac OS X clang based Xcode isn't checking)
9961 if (&config != mData->pMachineConfigFile)
9962 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9963
9964 config.uuid = mData->mUuid;
9965
9966 // copy name, description, OS type, teleport, UTC etc.
9967 config.machineUserData = mUserData->s;
9968
9969 if ( mData->mMachineState == MachineState_Saved
9970 || mData->mMachineState == MachineState_Restoring
9971 // when doing certain snapshot operations we may or may not have
9972 // a saved state in the current state, so keep everything as is
9973 || ( ( mData->mMachineState == MachineState_Snapshotting
9974 || mData->mMachineState == MachineState_DeletingSnapshot
9975 || mData->mMachineState == MachineState_RestoringSnapshot)
9976 && (!mSSData->strStateFilePath.isEmpty())
9977 )
9978 )
9979 {
9980 Assert(!mSSData->strStateFilePath.isEmpty());
9981 /* try to make the file name relative to the settings file dir */
9982 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9983 }
9984 else
9985 {
9986 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9987 config.strStateFile.setNull();
9988 }
9989
9990 if (mData->mCurrentSnapshot)
9991 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9992 else
9993 config.uuidCurrentSnapshot.clear();
9994
9995 config.timeLastStateChange = mData->mLastStateChange;
9996 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9997 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9998
9999 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10000 if (FAILED(rc)) throw rc;
10001
10002 // save machine's media registry if this is VirtualBox 4.0 or later
10003 if (config.canHaveOwnMediaRegistry())
10004 {
10005 // determine machine folder
10006 Utf8Str strMachineFolder = i_getSettingsFileFull();
10007 strMachineFolder.stripFilename();
10008 mParent->i_saveMediaRegistry(config.mediaRegistry,
10009 i_getId(), // only media with registry ID == machine UUID
10010 strMachineFolder);
10011 // this throws HRESULT
10012 }
10013
10014 // save snapshots
10015 rc = i_saveAllSnapshots(config);
10016 if (FAILED(rc)) throw rc;
10017}
10018
10019/**
10020 * Saves all snapshots of the machine into the given machine config file. Called
10021 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10022 * @param config
10023 * @return
10024 */
10025HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10026{
10027 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10028
10029 HRESULT rc = S_OK;
10030
10031 try
10032 {
10033 config.llFirstSnapshot.clear();
10034
10035 if (mData->mFirstSnapshot)
10036 {
10037 // the settings use a list for "the first snapshot"
10038 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10039
10040 // get reference to the snapshot on the list and work on that
10041 // element straight in the list to avoid excessive copying later
10042 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10043 if (FAILED(rc)) throw rc;
10044 }
10045
10046// if (mType == IsSessionMachine)
10047// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10048
10049 }
10050 catch (HRESULT err)
10051 {
10052 /* we assume that error info is set by the thrower */
10053 rc = err;
10054 }
10055 catch (...)
10056 {
10057 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10058 }
10059
10060 return rc;
10061}
10062
10063/**
10064 * Saves the VM hardware configuration. It is assumed that the
10065 * given node is empty.
10066 *
10067 * @param data Reference to the settings object for the hardware config.
10068 * @param pDbg Pointer to the settings object for the debugging config
10069 * which happens to live in mHWData.
10070 * @param pAutostart Pointer to the settings object for the autostart config
10071 * which happens to live in mHWData.
10072 */
10073HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10074 settings::Autostart *pAutostart)
10075{
10076 HRESULT rc = S_OK;
10077
10078 try
10079 {
10080 /* The hardware version attribute (optional).
10081 Automatically upgrade from 1 to current default hardware version
10082 when there is no saved state. (ugly!) */
10083 if ( mHWData->mHWVersion == "1"
10084 && mSSData->strStateFilePath.isEmpty()
10085 )
10086 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10087
10088 data.strVersion = mHWData->mHWVersion;
10089 data.uuid = mHWData->mHardwareUUID;
10090
10091 // CPU
10092 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10093 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10094 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10095 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10096 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10097 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10098 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10099 data.fPAE = !!mHWData->mPAEEnabled;
10100 data.enmLongMode = mHWData->mLongMode;
10101 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10102 data.fAPIC = !!mHWData->mAPIC;
10103 data.fX2APIC = !!mHWData->mX2APIC;
10104 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10105 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10106 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10107 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10108 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10109 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10110 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10111 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10112 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10113 data.cCPUs = mHWData->mCPUCount;
10114 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10115 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10116 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10117 data.strCpuProfile = mHWData->mCpuProfile;
10118
10119 data.llCpus.clear();
10120 if (data.fCpuHotPlug)
10121 {
10122 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10123 {
10124 if (mHWData->mCPUAttached[idx])
10125 {
10126 settings::Cpu cpu;
10127 cpu.ulId = idx;
10128 data.llCpus.push_back(cpu);
10129 }
10130 }
10131 }
10132
10133 /* Standard and Extended CPUID leafs. */
10134 data.llCpuIdLeafs.clear();
10135 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10136
10137 // memory
10138 data.ulMemorySizeMB = mHWData->mMemorySize;
10139 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10140
10141 // firmware
10142 data.firmwareType = mHWData->mFirmwareType;
10143
10144 // HID
10145 data.pointingHIDType = mHWData->mPointingHIDType;
10146 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10147
10148 // chipset
10149 data.chipsetType = mHWData->mChipsetType;
10150
10151 // paravirt
10152 data.paravirtProvider = mHWData->mParavirtProvider;
10153 data.strParavirtDebug = mHWData->mParavirtDebug;
10154
10155 // emulated USB card reader
10156 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10157
10158 // HPET
10159 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10160
10161 // boot order
10162 data.mapBootOrder.clear();
10163 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10164 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10165
10166 // display
10167 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10168 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10169 data.cMonitors = mHWData->mMonitorCount;
10170 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10171 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10172
10173 /* VRDEServer settings (optional) */
10174 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10175 if (FAILED(rc)) throw rc;
10176
10177 /* BIOS settings (required) */
10178 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10179 if (FAILED(rc)) throw rc;
10180
10181 /* Recording settings (required) */
10182 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10183 if (FAILED(rc)) throw rc;
10184
10185 /* USB Controller (required) */
10186 data.usbSettings.llUSBControllers.clear();
10187 for (USBControllerList::const_iterator
10188 it = mUSBControllers->begin();
10189 it != mUSBControllers->end();
10190 ++it)
10191 {
10192 ComObjPtr<USBController> ctrl = *it;
10193 settings::USBController settingsCtrl;
10194
10195 settingsCtrl.strName = ctrl->i_getName();
10196 settingsCtrl.enmType = ctrl->i_getControllerType();
10197
10198 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10199 }
10200
10201 /* USB device filters (required) */
10202 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10203 if (FAILED(rc)) throw rc;
10204
10205 /* Network adapters (required) */
10206 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10207 data.llNetworkAdapters.clear();
10208 /* Write out only the nominal number of network adapters for this
10209 * chipset type. Since Machine::commit() hasn't been called there
10210 * may be extra NIC settings in the vector. */
10211 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10212 {
10213 settings::NetworkAdapter nic;
10214 nic.ulSlot = (uint32_t)slot;
10215 /* paranoia check... must not be NULL, but must not crash either. */
10216 if (mNetworkAdapters[slot])
10217 {
10218 if (mNetworkAdapters[slot]->i_hasDefaults())
10219 continue;
10220
10221 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10222 if (FAILED(rc)) throw rc;
10223
10224 data.llNetworkAdapters.push_back(nic);
10225 }
10226 }
10227
10228 /* Serial ports */
10229 data.llSerialPorts.clear();
10230 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10231 {
10232 if (mSerialPorts[slot]->i_hasDefaults())
10233 continue;
10234
10235 settings::SerialPort s;
10236 s.ulSlot = slot;
10237 rc = mSerialPorts[slot]->i_saveSettings(s);
10238 if (FAILED(rc)) return rc;
10239
10240 data.llSerialPorts.push_back(s);
10241 }
10242
10243 /* Parallel ports */
10244 data.llParallelPorts.clear();
10245 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10246 {
10247 if (mParallelPorts[slot]->i_hasDefaults())
10248 continue;
10249
10250 settings::ParallelPort p;
10251 p.ulSlot = slot;
10252 rc = mParallelPorts[slot]->i_saveSettings(p);
10253 if (FAILED(rc)) return rc;
10254
10255 data.llParallelPorts.push_back(p);
10256 }
10257
10258 /* Audio adapter */
10259 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10260 if (FAILED(rc)) return rc;
10261
10262 rc = i_saveStorageControllers(data.storage);
10263 if (FAILED(rc)) return rc;
10264
10265 /* Shared folders */
10266 data.llSharedFolders.clear();
10267 for (HWData::SharedFolderList::const_iterator
10268 it = mHWData->mSharedFolders.begin();
10269 it != mHWData->mSharedFolders.end();
10270 ++it)
10271 {
10272 SharedFolder *pSF = *it;
10273 AutoCaller sfCaller(pSF);
10274 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10275 settings::SharedFolder sf;
10276 sf.strName = pSF->i_getName();
10277 sf.strHostPath = pSF->i_getHostPath();
10278 sf.fWritable = !!pSF->i_isWritable();
10279 sf.fAutoMount = !!pSF->i_isAutoMounted();
10280 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10281
10282 data.llSharedFolders.push_back(sf);
10283 }
10284
10285 // clipboard
10286 data.clipboardMode = mHWData->mClipboardMode;
10287
10288 // drag'n'drop
10289 data.dndMode = mHWData->mDnDMode;
10290
10291 /* Guest */
10292 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10293
10294 // IO settings
10295 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10296 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10297
10298 /* BandwidthControl (required) */
10299 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10300 if (FAILED(rc)) throw rc;
10301
10302 /* Host PCI devices */
10303 data.pciAttachments.clear();
10304 for (HWData::PCIDeviceAssignmentList::const_iterator
10305 it = mHWData->mPCIDeviceAssignments.begin();
10306 it != mHWData->mPCIDeviceAssignments.end();
10307 ++it)
10308 {
10309 ComObjPtr<PCIDeviceAttachment> pda = *it;
10310 settings::HostPCIDeviceAttachment hpda;
10311
10312 rc = pda->i_saveSettings(hpda);
10313 if (FAILED(rc)) throw rc;
10314
10315 data.pciAttachments.push_back(hpda);
10316 }
10317
10318 // guest properties
10319 data.llGuestProperties.clear();
10320#ifdef VBOX_WITH_GUEST_PROPS
10321 for (HWData::GuestPropertyMap::const_iterator
10322 it = mHWData->mGuestProperties.begin();
10323 it != mHWData->mGuestProperties.end();
10324 ++it)
10325 {
10326 HWData::GuestProperty property = it->second;
10327
10328 /* Remove transient guest properties at shutdown unless we
10329 * are saving state. Note that restoring snapshot intentionally
10330 * keeps them, they will be removed if appropriate once the final
10331 * machine state is set (as crashes etc. need to work). */
10332 if ( ( mData->mMachineState == MachineState_PoweredOff
10333 || mData->mMachineState == MachineState_Aborted
10334 || mData->mMachineState == MachineState_Teleported)
10335 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10336 continue;
10337 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10338 prop.strName = it->first;
10339 prop.strValue = property.strValue;
10340 prop.timestamp = property.mTimestamp;
10341 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10342 GuestPropWriteFlags(property.mFlags, szFlags);
10343 prop.strFlags = szFlags;
10344
10345 data.llGuestProperties.push_back(prop);
10346 }
10347
10348 /* I presume this doesn't require a backup(). */
10349 mData->mGuestPropertiesModified = FALSE;
10350#endif /* VBOX_WITH_GUEST_PROPS defined */
10351
10352 *pDbg = mHWData->mDebugging;
10353 *pAutostart = mHWData->mAutostart;
10354
10355 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10356 }
10357 catch (std::bad_alloc &)
10358 {
10359 return E_OUTOFMEMORY;
10360 }
10361
10362 AssertComRC(rc);
10363 return rc;
10364}
10365
10366/**
10367 * Saves the storage controller configuration.
10368 *
10369 * @param data storage settings.
10370 */
10371HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10372{
10373 data.llStorageControllers.clear();
10374
10375 for (StorageControllerList::const_iterator
10376 it = mStorageControllers->begin();
10377 it != mStorageControllers->end();
10378 ++it)
10379 {
10380 HRESULT rc;
10381 ComObjPtr<StorageController> pCtl = *it;
10382
10383 settings::StorageController ctl;
10384 ctl.strName = pCtl->i_getName();
10385 ctl.controllerType = pCtl->i_getControllerType();
10386 ctl.storageBus = pCtl->i_getStorageBus();
10387 ctl.ulInstance = pCtl->i_getInstance();
10388 ctl.fBootable = pCtl->i_getBootable();
10389
10390 /* Save the port count. */
10391 ULONG portCount;
10392 rc = pCtl->COMGETTER(PortCount)(&portCount);
10393 ComAssertComRCRet(rc, rc);
10394 ctl.ulPortCount = portCount;
10395
10396 /* Save fUseHostIOCache */
10397 BOOL fUseHostIOCache;
10398 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10399 ComAssertComRCRet(rc, rc);
10400 ctl.fUseHostIOCache = !!fUseHostIOCache;
10401
10402 /* save the devices now. */
10403 rc = i_saveStorageDevices(pCtl, ctl);
10404 ComAssertComRCRet(rc, rc);
10405
10406 data.llStorageControllers.push_back(ctl);
10407 }
10408
10409 return S_OK;
10410}
10411
10412/**
10413 * Saves the hard disk configuration.
10414 */
10415HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10416 settings::StorageController &data)
10417{
10418 MediumAttachmentList atts;
10419
10420 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10421 if (FAILED(rc)) return rc;
10422
10423 data.llAttachedDevices.clear();
10424 for (MediumAttachmentList::const_iterator
10425 it = atts.begin();
10426 it != atts.end();
10427 ++it)
10428 {
10429 settings::AttachedDevice dev;
10430 IMediumAttachment *iA = *it;
10431 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10432 Medium *pMedium = pAttach->i_getMedium();
10433
10434 dev.deviceType = pAttach->i_getType();
10435 dev.lPort = pAttach->i_getPort();
10436 dev.lDevice = pAttach->i_getDevice();
10437 dev.fPassThrough = pAttach->i_getPassthrough();
10438 dev.fHotPluggable = pAttach->i_getHotPluggable();
10439 if (pMedium)
10440 {
10441 if (pMedium->i_isHostDrive())
10442 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10443 else
10444 dev.uuid = pMedium->i_getId();
10445 dev.fTempEject = pAttach->i_getTempEject();
10446 dev.fNonRotational = pAttach->i_getNonRotational();
10447 dev.fDiscard = pAttach->i_getDiscard();
10448 }
10449
10450 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10451
10452 data.llAttachedDevices.push_back(dev);
10453 }
10454
10455 return S_OK;
10456}
10457
10458/**
10459 * Saves machine state settings as defined by aFlags
10460 * (SaveSTS_* values).
10461 *
10462 * @param aFlags Combination of SaveSTS_* flags.
10463 *
10464 * @note Locks objects for writing.
10465 */
10466HRESULT Machine::i_saveStateSettings(int aFlags)
10467{
10468 if (aFlags == 0)
10469 return S_OK;
10470
10471 AutoCaller autoCaller(this);
10472 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10473
10474 /* This object's write lock is also necessary to serialize file access
10475 * (prevent concurrent reads and writes) */
10476 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10477
10478 HRESULT rc = S_OK;
10479
10480 Assert(mData->pMachineConfigFile);
10481
10482 try
10483 {
10484 if (aFlags & SaveSTS_CurStateModified)
10485 mData->pMachineConfigFile->fCurrentStateModified = true;
10486
10487 if (aFlags & SaveSTS_StateFilePath)
10488 {
10489 if (!mSSData->strStateFilePath.isEmpty())
10490 /* try to make the file name relative to the settings file dir */
10491 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10492 else
10493 mData->pMachineConfigFile->strStateFile.setNull();
10494 }
10495
10496 if (aFlags & SaveSTS_StateTimeStamp)
10497 {
10498 Assert( mData->mMachineState != MachineState_Aborted
10499 || mSSData->strStateFilePath.isEmpty());
10500
10501 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10502
10503 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10504/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10505 }
10506
10507 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10508 }
10509 catch (...)
10510 {
10511 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10512 }
10513
10514 return rc;
10515}
10516
10517/**
10518 * Ensures that the given medium is added to a media registry. If this machine
10519 * was created with 4.0 or later, then the machine registry is used. Otherwise
10520 * the global VirtualBox media registry is used.
10521 *
10522 * Caller must NOT hold machine lock, media tree or any medium locks!
10523 *
10524 * @param pMedium
10525 */
10526void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10527{
10528 /* Paranoia checks: do not hold machine or media tree locks. */
10529 AssertReturnVoid(!isWriteLockOnCurrentThread());
10530 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10531
10532 ComObjPtr<Medium> pBase;
10533 {
10534 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10535 pBase = pMedium->i_getBase();
10536 }
10537
10538 /* Paranoia checks: do not hold medium locks. */
10539 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10540 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10541
10542 // decide which medium registry to use now that the medium is attached:
10543 Guid uuid;
10544 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10545 if (fCanHaveOwnMediaRegistry)
10546 // machine XML is VirtualBox 4.0 or higher:
10547 uuid = i_getId(); // machine UUID
10548 else
10549 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10550
10551 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10552 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10553 if (pMedium->i_addRegistry(uuid))
10554 mParent->i_markRegistryModified(uuid);
10555
10556 /* For more complex hard disk structures it can happen that the base
10557 * medium isn't yet associated with any medium registry. Do that now. */
10558 if (pMedium != pBase)
10559 {
10560 /* Tree lock needed by Medium::addRegistry when recursing. */
10561 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10562 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10563 {
10564 treeLock.release();
10565 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10566 treeLock.acquire();
10567 }
10568 if (pBase->i_addRegistryRecursive(uuid))
10569 {
10570 treeLock.release();
10571 mParent->i_markRegistryModified(uuid);
10572 }
10573 }
10574}
10575
10576/**
10577 * Creates differencing hard disks for all normal hard disks attached to this
10578 * machine and a new set of attachments to refer to created disks.
10579 *
10580 * Used when taking a snapshot or when deleting the current state. Gets called
10581 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10582 *
10583 * This method assumes that mMediumAttachments contains the original hard disk
10584 * attachments it needs to create diffs for. On success, these attachments will
10585 * be replaced with the created diffs.
10586 *
10587 * Attachments with non-normal hard disks are left as is.
10588 *
10589 * If @a aOnline is @c false then the original hard disks that require implicit
10590 * diffs will be locked for reading. Otherwise it is assumed that they are
10591 * already locked for writing (when the VM was started). Note that in the latter
10592 * case it is responsibility of the caller to lock the newly created diffs for
10593 * writing if this method succeeds.
10594 *
10595 * @param aProgress Progress object to run (must contain at least as
10596 * many operations left as the number of hard disks
10597 * attached).
10598 * @param aWeight Weight of this operation.
10599 * @param aOnline Whether the VM was online prior to this operation.
10600 *
10601 * @note The progress object is not marked as completed, neither on success nor
10602 * on failure. This is a responsibility of the caller.
10603 *
10604 * @note Locks this object and the media tree for writing.
10605 */
10606HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10607 ULONG aWeight,
10608 bool aOnline)
10609{
10610 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10611
10612 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10613 AssertReturn(!!pProgressControl, E_INVALIDARG);
10614
10615 AutoCaller autoCaller(this);
10616 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10617
10618 AutoMultiWriteLock2 alock(this->lockHandle(),
10619 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10620
10621 /* must be in a protective state because we release the lock below */
10622 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10623 || mData->mMachineState == MachineState_OnlineSnapshotting
10624 || mData->mMachineState == MachineState_LiveSnapshotting
10625 || mData->mMachineState == MachineState_RestoringSnapshot
10626 || mData->mMachineState == MachineState_DeletingSnapshot
10627 , E_FAIL);
10628
10629 HRESULT rc = S_OK;
10630
10631 // use appropriate locked media map (online or offline)
10632 MediumLockListMap lockedMediaOffline;
10633 MediumLockListMap *lockedMediaMap;
10634 if (aOnline)
10635 lockedMediaMap = &mData->mSession.mLockedMedia;
10636 else
10637 lockedMediaMap = &lockedMediaOffline;
10638
10639 try
10640 {
10641 if (!aOnline)
10642 {
10643 /* lock all attached hard disks early to detect "in use"
10644 * situations before creating actual diffs */
10645 for (MediumAttachmentList::const_iterator
10646 it = mMediumAttachments->begin();
10647 it != mMediumAttachments->end();
10648 ++it)
10649 {
10650 MediumAttachment *pAtt = *it;
10651 if (pAtt->i_getType() == DeviceType_HardDisk)
10652 {
10653 Medium *pMedium = pAtt->i_getMedium();
10654 Assert(pMedium);
10655
10656 MediumLockList *pMediumLockList(new MediumLockList());
10657 alock.release();
10658 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10659 NULL /* pToLockWrite */,
10660 false /* fMediumLockWriteAll */,
10661 NULL,
10662 *pMediumLockList);
10663 alock.acquire();
10664 if (FAILED(rc))
10665 {
10666 delete pMediumLockList;
10667 throw rc;
10668 }
10669 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10670 if (FAILED(rc))
10671 {
10672 throw setError(rc,
10673 tr("Collecting locking information for all attached media failed"));
10674 }
10675 }
10676 }
10677
10678 /* Now lock all media. If this fails, nothing is locked. */
10679 alock.release();
10680 rc = lockedMediaMap->Lock();
10681 alock.acquire();
10682 if (FAILED(rc))
10683 {
10684 throw setError(rc,
10685 tr("Locking of attached media failed"));
10686 }
10687 }
10688
10689 /* remember the current list (note that we don't use backup() since
10690 * mMediumAttachments may be already backed up) */
10691 MediumAttachmentList atts = *mMediumAttachments.data();
10692
10693 /* start from scratch */
10694 mMediumAttachments->clear();
10695
10696 /* go through remembered attachments and create diffs for normal hard
10697 * disks and attach them */
10698 for (MediumAttachmentList::const_iterator
10699 it = atts.begin();
10700 it != atts.end();
10701 ++it)
10702 {
10703 MediumAttachment *pAtt = *it;
10704
10705 DeviceType_T devType = pAtt->i_getType();
10706 Medium *pMedium = pAtt->i_getMedium();
10707
10708 if ( devType != DeviceType_HardDisk
10709 || pMedium == NULL
10710 || pMedium->i_getType() != MediumType_Normal)
10711 {
10712 /* copy the attachment as is */
10713
10714 /** @todo the progress object created in SessionMachine::TakeSnaphot
10715 * only expects operations for hard disks. Later other
10716 * device types need to show up in the progress as well. */
10717 if (devType == DeviceType_HardDisk)
10718 {
10719 if (pMedium == NULL)
10720 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10721 aWeight); // weight
10722 else
10723 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10724 pMedium->i_getBase()->i_getName().c_str()).raw(),
10725 aWeight); // weight
10726 }
10727
10728 mMediumAttachments->push_back(pAtt);
10729 continue;
10730 }
10731
10732 /* need a diff */
10733 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10734 pMedium->i_getBase()->i_getName().c_str()).raw(),
10735 aWeight); // weight
10736
10737 Utf8Str strFullSnapshotFolder;
10738 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10739
10740 ComObjPtr<Medium> diff;
10741 diff.createObject();
10742 // store the diff in the same registry as the parent
10743 // (this cannot fail here because we can't create implicit diffs for
10744 // unregistered images)
10745 Guid uuidRegistryParent;
10746 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10747 Assert(fInRegistry); NOREF(fInRegistry);
10748 rc = diff->init(mParent,
10749 pMedium->i_getPreferredDiffFormat(),
10750 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10751 uuidRegistryParent,
10752 DeviceType_HardDisk);
10753 if (FAILED(rc)) throw rc;
10754
10755 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10756 * the push_back? Looks like we're going to release medium with the
10757 * wrong kind of lock (general issue with if we fail anywhere at all)
10758 * and an orphaned VDI in the snapshots folder. */
10759
10760 /* update the appropriate lock list */
10761 MediumLockList *pMediumLockList;
10762 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10763 AssertComRCThrowRC(rc);
10764 if (aOnline)
10765 {
10766 alock.release();
10767 /* The currently attached medium will be read-only, change
10768 * the lock type to read. */
10769 rc = pMediumLockList->Update(pMedium, false);
10770 alock.acquire();
10771 AssertComRCThrowRC(rc);
10772 }
10773
10774 /* release the locks before the potentially lengthy operation */
10775 alock.release();
10776 rc = pMedium->i_createDiffStorage(diff,
10777 pMedium->i_getPreferredDiffVariant(),
10778 pMediumLockList,
10779 NULL /* aProgress */,
10780 true /* aWait */,
10781 false /* aNotify */);
10782 alock.acquire();
10783 if (FAILED(rc)) throw rc;
10784
10785 /* actual lock list update is done in Machine::i_commitMedia */
10786
10787 rc = diff->i_addBackReference(mData->mUuid);
10788 AssertComRCThrowRC(rc);
10789
10790 /* add a new attachment */
10791 ComObjPtr<MediumAttachment> attachment;
10792 attachment.createObject();
10793 rc = attachment->init(this,
10794 diff,
10795 pAtt->i_getControllerName(),
10796 pAtt->i_getPort(),
10797 pAtt->i_getDevice(),
10798 DeviceType_HardDisk,
10799 true /* aImplicit */,
10800 false /* aPassthrough */,
10801 false /* aTempEject */,
10802 pAtt->i_getNonRotational(),
10803 pAtt->i_getDiscard(),
10804 pAtt->i_getHotPluggable(),
10805 pAtt->i_getBandwidthGroup());
10806 if (FAILED(rc)) throw rc;
10807
10808 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10809 AssertComRCThrowRC(rc);
10810 mMediumAttachments->push_back(attachment);
10811 }
10812 }
10813 catch (HRESULT aRC) { rc = aRC; }
10814
10815 /* unlock all hard disks we locked when there is no VM */
10816 if (!aOnline)
10817 {
10818 ErrorInfoKeeper eik;
10819
10820 HRESULT rc1 = lockedMediaMap->Clear();
10821 AssertComRC(rc1);
10822 }
10823
10824 return rc;
10825}
10826
10827/**
10828 * Deletes implicit differencing hard disks created either by
10829 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10830 * mMediumAttachments.
10831 *
10832 * Note that to delete hard disks created by #attachDevice() this method is
10833 * called from #i_rollbackMedia() when the changes are rolled back.
10834 *
10835 * @note Locks this object and the media tree for writing.
10836 */
10837HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10838{
10839 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10840
10841 AutoCaller autoCaller(this);
10842 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10843
10844 AutoMultiWriteLock2 alock(this->lockHandle(),
10845 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10846
10847 /* We absolutely must have backed up state. */
10848 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10849
10850 /* Check if there are any implicitly created diff images. */
10851 bool fImplicitDiffs = false;
10852 for (MediumAttachmentList::const_iterator
10853 it = mMediumAttachments->begin();
10854 it != mMediumAttachments->end();
10855 ++it)
10856 {
10857 const ComObjPtr<MediumAttachment> &pAtt = *it;
10858 if (pAtt->i_isImplicit())
10859 {
10860 fImplicitDiffs = true;
10861 break;
10862 }
10863 }
10864 /* If there is nothing to do, leave early. This saves lots of image locking
10865 * effort. It also avoids a MachineStateChanged event without real reason.
10866 * This is important e.g. when loading a VM config, because there should be
10867 * no events. Otherwise API clients can become thoroughly confused for
10868 * inaccessible VMs (the code for loading VM configs uses this method for
10869 * cleanup if the config makes no sense), as they take such events as an
10870 * indication that the VM is alive, and they would force the VM config to
10871 * be reread, leading to an endless loop. */
10872 if (!fImplicitDiffs)
10873 return S_OK;
10874
10875 HRESULT rc = S_OK;
10876 MachineState_T oldState = mData->mMachineState;
10877
10878 /* will release the lock before the potentially lengthy operation,
10879 * so protect with the special state (unless already protected) */
10880 if ( oldState != MachineState_Snapshotting
10881 && oldState != MachineState_OnlineSnapshotting
10882 && oldState != MachineState_LiveSnapshotting
10883 && oldState != MachineState_RestoringSnapshot
10884 && oldState != MachineState_DeletingSnapshot
10885 && oldState != MachineState_DeletingSnapshotOnline
10886 && oldState != MachineState_DeletingSnapshotPaused
10887 )
10888 i_setMachineState(MachineState_SettingUp);
10889
10890 // use appropriate locked media map (online or offline)
10891 MediumLockListMap lockedMediaOffline;
10892 MediumLockListMap *lockedMediaMap;
10893 if (aOnline)
10894 lockedMediaMap = &mData->mSession.mLockedMedia;
10895 else
10896 lockedMediaMap = &lockedMediaOffline;
10897
10898 try
10899 {
10900 if (!aOnline)
10901 {
10902 /* lock all attached hard disks early to detect "in use"
10903 * situations before deleting actual diffs */
10904 for (MediumAttachmentList::const_iterator
10905 it = mMediumAttachments->begin();
10906 it != mMediumAttachments->end();
10907 ++it)
10908 {
10909 MediumAttachment *pAtt = *it;
10910 if (pAtt->i_getType() == DeviceType_HardDisk)
10911 {
10912 Medium *pMedium = pAtt->i_getMedium();
10913 Assert(pMedium);
10914
10915 MediumLockList *pMediumLockList(new MediumLockList());
10916 alock.release();
10917 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10918 NULL /* pToLockWrite */,
10919 false /* fMediumLockWriteAll */,
10920 NULL,
10921 *pMediumLockList);
10922 alock.acquire();
10923
10924 if (FAILED(rc))
10925 {
10926 delete pMediumLockList;
10927 throw rc;
10928 }
10929
10930 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10931 if (FAILED(rc))
10932 throw rc;
10933 }
10934 }
10935
10936 if (FAILED(rc))
10937 throw rc;
10938 } // end of offline
10939
10940 /* Lock lists are now up to date and include implicitly created media */
10941
10942 /* Go through remembered attachments and delete all implicitly created
10943 * diffs and fix up the attachment information */
10944 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10945 MediumAttachmentList implicitAtts;
10946 for (MediumAttachmentList::const_iterator
10947 it = mMediumAttachments->begin();
10948 it != mMediumAttachments->end();
10949 ++it)
10950 {
10951 ComObjPtr<MediumAttachment> pAtt = *it;
10952 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10953 if (pMedium.isNull())
10954 continue;
10955
10956 // Implicit attachments go on the list for deletion and back references are removed.
10957 if (pAtt->i_isImplicit())
10958 {
10959 /* Deassociate and mark for deletion */
10960 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10961 rc = pMedium->i_removeBackReference(mData->mUuid);
10962 if (FAILED(rc))
10963 throw rc;
10964 implicitAtts.push_back(pAtt);
10965 continue;
10966 }
10967
10968 /* Was this medium attached before? */
10969 if (!i_findAttachment(oldAtts, pMedium))
10970 {
10971 /* no: de-associate */
10972 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10973 rc = pMedium->i_removeBackReference(mData->mUuid);
10974 if (FAILED(rc))
10975 throw rc;
10976 continue;
10977 }
10978 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10979 }
10980
10981 /* If there are implicit attachments to delete, throw away the lock
10982 * map contents (which will unlock all media) since the medium
10983 * attachments will be rolled back. Below we need to completely
10984 * recreate the lock map anyway since it is infinitely complex to
10985 * do this incrementally (would need reconstructing each attachment
10986 * change, which would be extremely hairy). */
10987 if (implicitAtts.size() != 0)
10988 {
10989 ErrorInfoKeeper eik;
10990
10991 HRESULT rc1 = lockedMediaMap->Clear();
10992 AssertComRC(rc1);
10993 }
10994
10995 /* rollback hard disk changes */
10996 mMediumAttachments.rollback();
10997
10998 MultiResult mrc(S_OK);
10999
11000 // Delete unused implicit diffs.
11001 if (implicitAtts.size() != 0)
11002 {
11003 alock.release();
11004
11005 for (MediumAttachmentList::const_iterator
11006 it = implicitAtts.begin();
11007 it != implicitAtts.end();
11008 ++it)
11009 {
11010 // Remove medium associated with this attachment.
11011 ComObjPtr<MediumAttachment> pAtt = *it;
11012 Assert(pAtt);
11013 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11014 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11015 Assert(pMedium);
11016
11017 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
11018 // continue on delete failure, just collect error messages
11019 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11020 pMedium->i_getLocationFull().c_str() ));
11021 mrc = rc;
11022 }
11023 // Clear the list of deleted implicit attachments now, while not
11024 // holding the lock, as it will ultimately trigger Medium::uninit()
11025 // calls which assume that the media tree lock isn't held.
11026 implicitAtts.clear();
11027
11028 alock.acquire();
11029
11030 /* if there is a VM recreate media lock map as mentioned above,
11031 * otherwise it is a waste of time and we leave things unlocked */
11032 if (aOnline)
11033 {
11034 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11035 /* must never be NULL, but better safe than sorry */
11036 if (!pMachine.isNull())
11037 {
11038 alock.release();
11039 rc = mData->mSession.mMachine->i_lockMedia();
11040 alock.acquire();
11041 if (FAILED(rc))
11042 throw rc;
11043 }
11044 }
11045 }
11046 }
11047 catch (HRESULT aRC) {rc = aRC;}
11048
11049 if (mData->mMachineState == MachineState_SettingUp)
11050 i_setMachineState(oldState);
11051
11052 /* unlock all hard disks we locked when there is no VM */
11053 if (!aOnline)
11054 {
11055 ErrorInfoKeeper eik;
11056
11057 HRESULT rc1 = lockedMediaMap->Clear();
11058 AssertComRC(rc1);
11059 }
11060
11061 return rc;
11062}
11063
11064
11065/**
11066 * Looks through the given list of media attachments for one with the given parameters
11067 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11068 * can be searched as well if needed.
11069 *
11070 * @param ll
11071 * @param aControllerName
11072 * @param aControllerPort
11073 * @param aDevice
11074 * @return
11075 */
11076MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11077 const Utf8Str &aControllerName,
11078 LONG aControllerPort,
11079 LONG aDevice)
11080{
11081 for (MediumAttachmentList::const_iterator
11082 it = ll.begin();
11083 it != ll.end();
11084 ++it)
11085 {
11086 MediumAttachment *pAttach = *it;
11087 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11088 return pAttach;
11089 }
11090
11091 return NULL;
11092}
11093
11094/**
11095 * Looks through the given list of media attachments for one with the given parameters
11096 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11097 * can be searched as well if needed.
11098 *
11099 * @param ll
11100 * @param pMedium
11101 * @return
11102 */
11103MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11104 ComObjPtr<Medium> pMedium)
11105{
11106 for (MediumAttachmentList::const_iterator
11107 it = ll.begin();
11108 it != ll.end();
11109 ++it)
11110 {
11111 MediumAttachment *pAttach = *it;
11112 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11113 if (pMediumThis == pMedium)
11114 return pAttach;
11115 }
11116
11117 return NULL;
11118}
11119
11120/**
11121 * Looks through the given list of media attachments for one with the given parameters
11122 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11123 * can be searched as well if needed.
11124 *
11125 * @param ll
11126 * @param id
11127 * @return
11128 */
11129MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11130 Guid &id)
11131{
11132 for (MediumAttachmentList::const_iterator
11133 it = ll.begin();
11134 it != ll.end();
11135 ++it)
11136 {
11137 MediumAttachment *pAttach = *it;
11138 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11139 if (pMediumThis->i_getId() == id)
11140 return pAttach;
11141 }
11142
11143 return NULL;
11144}
11145
11146/**
11147 * Main implementation for Machine::DetachDevice. This also gets called
11148 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11149 *
11150 * @param pAttach Medium attachment to detach.
11151 * @param writeLock Machine write lock which the caller must have locked once.
11152 * This may be released temporarily in here.
11153 * @param pSnapshot If NULL, then the detachment is for the current machine.
11154 * Otherwise this is for a SnapshotMachine, and this must be
11155 * its snapshot.
11156 * @return
11157 */
11158HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11159 AutoWriteLock &writeLock,
11160 Snapshot *pSnapshot)
11161{
11162 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11163 DeviceType_T mediumType = pAttach->i_getType();
11164
11165 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11166
11167 if (pAttach->i_isImplicit())
11168 {
11169 /* attempt to implicitly delete the implicitly created diff */
11170
11171 /// @todo move the implicit flag from MediumAttachment to Medium
11172 /// and forbid any hard disk operation when it is implicit. Or maybe
11173 /// a special media state for it to make it even more simple.
11174
11175 Assert(mMediumAttachments.isBackedUp());
11176
11177 /* will release the lock before the potentially lengthy operation, so
11178 * protect with the special state */
11179 MachineState_T oldState = mData->mMachineState;
11180 i_setMachineState(MachineState_SettingUp);
11181
11182 writeLock.release();
11183
11184 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11185 true /*aWait*/,
11186 false /*aNotify*/);
11187
11188 writeLock.acquire();
11189
11190 i_setMachineState(oldState);
11191
11192 if (FAILED(rc)) return rc;
11193 }
11194
11195 i_setModified(IsModified_Storage);
11196 mMediumAttachments.backup();
11197 mMediumAttachments->remove(pAttach);
11198
11199 if (!oldmedium.isNull())
11200 {
11201 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11202 if (pSnapshot)
11203 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11204 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11205 else if (mediumType != DeviceType_HardDisk)
11206 oldmedium->i_removeBackReference(mData->mUuid);
11207 }
11208
11209 return S_OK;
11210}
11211
11212/**
11213 * Goes thru all media of the given list and
11214 *
11215 * 1) calls i_detachDevice() on each of them for this machine and
11216 * 2) adds all Medium objects found in the process to the given list,
11217 * depending on cleanupMode.
11218 *
11219 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11220 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11221 * media to the list.
11222 *
11223 * This gets called from Machine::Unregister, both for the actual Machine and
11224 * the SnapshotMachine objects that might be found in the snapshots.
11225 *
11226 * Requires caller and locking. The machine lock must be passed in because it
11227 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11228 *
11229 * @param writeLock Machine lock from top-level caller; this gets passed to
11230 * i_detachDevice.
11231 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11232 * object if called for a SnapshotMachine.
11233 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11234 * added to llMedia; if Full, then all media get added;
11235 * otherwise no media get added.
11236 * @param llMedia Caller's list to receive Medium objects which got detached so
11237 * caller can close() them, depending on cleanupMode.
11238 * @return
11239 */
11240HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11241 Snapshot *pSnapshot,
11242 CleanupMode_T cleanupMode,
11243 MediaList &llMedia)
11244{
11245 Assert(isWriteLockOnCurrentThread());
11246
11247 HRESULT rc;
11248
11249 // make a temporary list because i_detachDevice invalidates iterators into
11250 // mMediumAttachments
11251 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11252
11253 for (MediumAttachmentList::iterator
11254 it = llAttachments2.begin();
11255 it != llAttachments2.end();
11256 ++it)
11257 {
11258 ComObjPtr<MediumAttachment> &pAttach = *it;
11259 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11260
11261 if (!pMedium.isNull())
11262 {
11263 AutoCaller mac(pMedium);
11264 if (FAILED(mac.rc())) return mac.rc();
11265 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11266 DeviceType_T devType = pMedium->i_getDeviceType();
11267 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11268 && devType == DeviceType_HardDisk)
11269 || (cleanupMode == CleanupMode_Full)
11270 )
11271 {
11272 llMedia.push_back(pMedium);
11273 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11274 /* Not allowed to keep this lock as below we need the parent
11275 * medium lock, and the lock order is parent to child. */
11276 lock.release();
11277 /*
11278 * Search for medias which are not attached to any machine, but
11279 * in the chain to an attached disk. Mediums are only consided
11280 * if they are:
11281 * - have only one child
11282 * - no references to any machines
11283 * - are of normal medium type
11284 */
11285 while (!pParent.isNull())
11286 {
11287 AutoCaller mac1(pParent);
11288 if (FAILED(mac1.rc())) return mac1.rc();
11289 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11290 if (pParent->i_getChildren().size() == 1)
11291 {
11292 if ( pParent->i_getMachineBackRefCount() == 0
11293 && pParent->i_getType() == MediumType_Normal
11294 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11295 llMedia.push_back(pParent);
11296 }
11297 else
11298 break;
11299 pParent = pParent->i_getParent();
11300 }
11301 }
11302 }
11303
11304 // real machine: then we need to use the proper method
11305 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11306
11307 if (FAILED(rc))
11308 return rc;
11309 }
11310
11311 return S_OK;
11312}
11313
11314/**
11315 * Perform deferred hard disk detachments.
11316 *
11317 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11318 * changed (not backed up).
11319 *
11320 * If @a aOnline is @c true then this method will also unlock the old hard
11321 * disks for which the new implicit diffs were created and will lock these new
11322 * diffs for writing.
11323 *
11324 * @param aOnline Whether the VM was online prior to this operation.
11325 *
11326 * @note Locks this object for writing!
11327 */
11328void Machine::i_commitMedia(bool aOnline /*= false*/)
11329{
11330 AutoCaller autoCaller(this);
11331 AssertComRCReturnVoid(autoCaller.rc());
11332
11333 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11334
11335 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11336
11337 HRESULT rc = S_OK;
11338
11339 /* no attach/detach operations -- nothing to do */
11340 if (!mMediumAttachments.isBackedUp())
11341 return;
11342
11343 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11344 bool fMediaNeedsLocking = false;
11345
11346 /* enumerate new attachments */
11347 for (MediumAttachmentList::const_iterator
11348 it = mMediumAttachments->begin();
11349 it != mMediumAttachments->end();
11350 ++it)
11351 {
11352 MediumAttachment *pAttach = *it;
11353
11354 pAttach->i_commit();
11355
11356 Medium *pMedium = pAttach->i_getMedium();
11357 bool fImplicit = pAttach->i_isImplicit();
11358
11359 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11360 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11361 fImplicit));
11362
11363 /** @todo convert all this Machine-based voodoo to MediumAttachment
11364 * based commit logic. */
11365 if (fImplicit)
11366 {
11367 /* convert implicit attachment to normal */
11368 pAttach->i_setImplicit(false);
11369
11370 if ( aOnline
11371 && pMedium
11372 && pAttach->i_getType() == DeviceType_HardDisk
11373 )
11374 {
11375 /* update the appropriate lock list */
11376 MediumLockList *pMediumLockList;
11377 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11378 AssertComRC(rc);
11379 if (pMediumLockList)
11380 {
11381 /* unlock if there's a need to change the locking */
11382 if (!fMediaNeedsLocking)
11383 {
11384 rc = mData->mSession.mLockedMedia.Unlock();
11385 AssertComRC(rc);
11386 fMediaNeedsLocking = true;
11387 }
11388 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11389 AssertComRC(rc);
11390 rc = pMediumLockList->Append(pMedium, true);
11391 AssertComRC(rc);
11392 }
11393 }
11394
11395 continue;
11396 }
11397
11398 if (pMedium)
11399 {
11400 /* was this medium attached before? */
11401 for (MediumAttachmentList::iterator
11402 oldIt = oldAtts.begin();
11403 oldIt != oldAtts.end();
11404 ++oldIt)
11405 {
11406 MediumAttachment *pOldAttach = *oldIt;
11407 if (pOldAttach->i_getMedium() == pMedium)
11408 {
11409 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11410
11411 /* yes: remove from old to avoid de-association */
11412 oldAtts.erase(oldIt);
11413 break;
11414 }
11415 }
11416 }
11417 }
11418
11419 /* enumerate remaining old attachments and de-associate from the
11420 * current machine state */
11421 for (MediumAttachmentList::const_iterator
11422 it = oldAtts.begin();
11423 it != oldAtts.end();
11424 ++it)
11425 {
11426 MediumAttachment *pAttach = *it;
11427 Medium *pMedium = pAttach->i_getMedium();
11428
11429 /* Detach only hard disks, since DVD/floppy media is detached
11430 * instantly in MountMedium. */
11431 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11432 {
11433 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11434
11435 /* now de-associate from the current machine state */
11436 rc = pMedium->i_removeBackReference(mData->mUuid);
11437 AssertComRC(rc);
11438
11439 if (aOnline)
11440 {
11441 /* unlock since medium is not used anymore */
11442 MediumLockList *pMediumLockList;
11443 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11444 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11445 {
11446 /* this happens for online snapshots, there the attachment
11447 * is changing, but only to a diff image created under
11448 * the old one, so there is no separate lock list */
11449 Assert(!pMediumLockList);
11450 }
11451 else
11452 {
11453 AssertComRC(rc);
11454 if (pMediumLockList)
11455 {
11456 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11457 AssertComRC(rc);
11458 }
11459 }
11460 }
11461 }
11462 }
11463
11464 /* take media locks again so that the locking state is consistent */
11465 if (fMediaNeedsLocking)
11466 {
11467 Assert(aOnline);
11468 rc = mData->mSession.mLockedMedia.Lock();
11469 AssertComRC(rc);
11470 }
11471
11472 /* commit the hard disk changes */
11473 mMediumAttachments.commit();
11474
11475 if (i_isSessionMachine())
11476 {
11477 /*
11478 * Update the parent machine to point to the new owner.
11479 * This is necessary because the stored parent will point to the
11480 * session machine otherwise and cause crashes or errors later
11481 * when the session machine gets invalid.
11482 */
11483 /** @todo Change the MediumAttachment class to behave like any other
11484 * class in this regard by creating peer MediumAttachment
11485 * objects for session machines and share the data with the peer
11486 * machine.
11487 */
11488 for (MediumAttachmentList::const_iterator
11489 it = mMediumAttachments->begin();
11490 it != mMediumAttachments->end();
11491 ++it)
11492 (*it)->i_updateParentMachine(mPeer);
11493
11494 /* attach new data to the primary machine and reshare it */
11495 mPeer->mMediumAttachments.attach(mMediumAttachments);
11496 }
11497
11498 return;
11499}
11500
11501/**
11502 * Perform deferred deletion of implicitly created diffs.
11503 *
11504 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11505 * changed (not backed up).
11506 *
11507 * @note Locks this object for writing!
11508 */
11509void Machine::i_rollbackMedia()
11510{
11511 AutoCaller autoCaller(this);
11512 AssertComRCReturnVoid(autoCaller.rc());
11513
11514 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11515 LogFlowThisFunc(("Entering rollbackMedia\n"));
11516
11517 HRESULT rc = S_OK;
11518
11519 /* no attach/detach operations -- nothing to do */
11520 if (!mMediumAttachments.isBackedUp())
11521 return;
11522
11523 /* enumerate new attachments */
11524 for (MediumAttachmentList::const_iterator
11525 it = mMediumAttachments->begin();
11526 it != mMediumAttachments->end();
11527 ++it)
11528 {
11529 MediumAttachment *pAttach = *it;
11530 /* Fix up the backrefs for DVD/floppy media. */
11531 if (pAttach->i_getType() != DeviceType_HardDisk)
11532 {
11533 Medium *pMedium = pAttach->i_getMedium();
11534 if (pMedium)
11535 {
11536 rc = pMedium->i_removeBackReference(mData->mUuid);
11537 AssertComRC(rc);
11538 }
11539 }
11540
11541 (*it)->i_rollback();
11542
11543 pAttach = *it;
11544 /* Fix up the backrefs for DVD/floppy media. */
11545 if (pAttach->i_getType() != DeviceType_HardDisk)
11546 {
11547 Medium *pMedium = pAttach->i_getMedium();
11548 if (pMedium)
11549 {
11550 rc = pMedium->i_addBackReference(mData->mUuid);
11551 AssertComRC(rc);
11552 }
11553 }
11554 }
11555
11556 /** @todo convert all this Machine-based voodoo to MediumAttachment
11557 * based rollback logic. */
11558 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11559
11560 return;
11561}
11562
11563/**
11564 * Returns true if the settings file is located in the directory named exactly
11565 * as the machine; this means, among other things, that the machine directory
11566 * should be auto-renamed.
11567 *
11568 * @param aSettingsDir if not NULL, the full machine settings file directory
11569 * name will be assigned there.
11570 *
11571 * @note Doesn't lock anything.
11572 * @note Not thread safe (must be called from this object's lock).
11573 */
11574bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11575{
11576 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11577 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11578 if (aSettingsDir)
11579 *aSettingsDir = strMachineDirName;
11580 strMachineDirName.stripPath(); // vmname
11581 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11582 strConfigFileOnly.stripPath() // vmname.vbox
11583 .stripSuffix(); // vmname
11584 /** @todo hack, make somehow use of ComposeMachineFilename */
11585 if (mUserData->s.fDirectoryIncludesUUID)
11586 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11587
11588 AssertReturn(!strMachineDirName.isEmpty(), false);
11589 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11590
11591 return strMachineDirName == strConfigFileOnly;
11592}
11593
11594/**
11595 * Discards all changes to machine settings.
11596 *
11597 * @param aNotify Whether to notify the direct session about changes or not.
11598 *
11599 * @note Locks objects for writing!
11600 */
11601void Machine::i_rollback(bool aNotify)
11602{
11603 AutoCaller autoCaller(this);
11604 AssertComRCReturn(autoCaller.rc(), (void)0);
11605
11606 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11607
11608 if (!mStorageControllers.isNull())
11609 {
11610 if (mStorageControllers.isBackedUp())
11611 {
11612 /* unitialize all new devices (absent in the backed up list). */
11613 StorageControllerList *backedList = mStorageControllers.backedUpData();
11614 for (StorageControllerList::const_iterator
11615 it = mStorageControllers->begin();
11616 it != mStorageControllers->end();
11617 ++it)
11618 {
11619 if ( std::find(backedList->begin(), backedList->end(), *it)
11620 == backedList->end()
11621 )
11622 {
11623 (*it)->uninit();
11624 }
11625 }
11626
11627 /* restore the list */
11628 mStorageControllers.rollback();
11629 }
11630
11631 /* rollback any changes to devices after restoring the list */
11632 if (mData->flModifications & IsModified_Storage)
11633 {
11634 for (StorageControllerList::const_iterator
11635 it = mStorageControllers->begin();
11636 it != mStorageControllers->end();
11637 ++it)
11638 {
11639 (*it)->i_rollback();
11640 }
11641 }
11642 }
11643
11644 if (!mUSBControllers.isNull())
11645 {
11646 if (mUSBControllers.isBackedUp())
11647 {
11648 /* unitialize all new devices (absent in the backed up list). */
11649 USBControllerList *backedList = mUSBControllers.backedUpData();
11650 for (USBControllerList::const_iterator
11651 it = mUSBControllers->begin();
11652 it != mUSBControllers->end();
11653 ++it)
11654 {
11655 if ( std::find(backedList->begin(), backedList->end(), *it)
11656 == backedList->end()
11657 )
11658 {
11659 (*it)->uninit();
11660 }
11661 }
11662
11663 /* restore the list */
11664 mUSBControllers.rollback();
11665 }
11666
11667 /* rollback any changes to devices after restoring the list */
11668 if (mData->flModifications & IsModified_USB)
11669 {
11670 for (USBControllerList::const_iterator
11671 it = mUSBControllers->begin();
11672 it != mUSBControllers->end();
11673 ++it)
11674 {
11675 (*it)->i_rollback();
11676 }
11677 }
11678 }
11679
11680 mUserData.rollback();
11681
11682 mHWData.rollback();
11683
11684 if (mData->flModifications & IsModified_Storage)
11685 i_rollbackMedia();
11686
11687 if (mBIOSSettings)
11688 mBIOSSettings->i_rollback();
11689
11690 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11691 mRecordingSettings->i_rollback();
11692
11693 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11694 mVRDEServer->i_rollback();
11695
11696 if (mAudioAdapter)
11697 mAudioAdapter->i_rollback();
11698
11699 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11700 mUSBDeviceFilters->i_rollback();
11701
11702 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11703 mBandwidthControl->i_rollback();
11704
11705 if (!mHWData.isNull())
11706 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11707 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11708 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11709 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11710
11711 if (mData->flModifications & IsModified_NetworkAdapters)
11712 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11713 if ( mNetworkAdapters[slot]
11714 && mNetworkAdapters[slot]->i_isModified())
11715 {
11716 mNetworkAdapters[slot]->i_rollback();
11717 networkAdapters[slot] = mNetworkAdapters[slot];
11718 }
11719
11720 if (mData->flModifications & IsModified_SerialPorts)
11721 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11722 if ( mSerialPorts[slot]
11723 && mSerialPorts[slot]->i_isModified())
11724 {
11725 mSerialPorts[slot]->i_rollback();
11726 serialPorts[slot] = mSerialPorts[slot];
11727 }
11728
11729 if (mData->flModifications & IsModified_ParallelPorts)
11730 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11731 if ( mParallelPorts[slot]
11732 && mParallelPorts[slot]->i_isModified())
11733 {
11734 mParallelPorts[slot]->i_rollback();
11735 parallelPorts[slot] = mParallelPorts[slot];
11736 }
11737
11738 if (aNotify)
11739 {
11740 /* inform the direct session about changes */
11741
11742 ComObjPtr<Machine> that = this;
11743 uint32_t flModifications = mData->flModifications;
11744 alock.release();
11745
11746 if (flModifications & IsModified_SharedFolders)
11747 that->i_onSharedFolderChange();
11748
11749 if (flModifications & IsModified_VRDEServer)
11750 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11751 if (flModifications & IsModified_USB)
11752 that->i_onUSBControllerChange();
11753
11754 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11755 if (networkAdapters[slot])
11756 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11757 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11758 if (serialPorts[slot])
11759 that->i_onSerialPortChange(serialPorts[slot]);
11760 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11761 if (parallelPorts[slot])
11762 that->i_onParallelPortChange(parallelPorts[slot]);
11763
11764 if (flModifications & IsModified_Storage)
11765 {
11766 for (StorageControllerList::const_iterator
11767 it = mStorageControllers->begin();
11768 it != mStorageControllers->end();
11769 ++it)
11770 {
11771 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11772 }
11773 }
11774
11775
11776#if 0
11777 if (flModifications & IsModified_BandwidthControl)
11778 that->onBandwidthControlChange();
11779#endif
11780 }
11781}
11782
11783/**
11784 * Commits all the changes to machine settings.
11785 *
11786 * Note that this operation is supposed to never fail.
11787 *
11788 * @note Locks this object and children for writing.
11789 */
11790void Machine::i_commit()
11791{
11792 AutoCaller autoCaller(this);
11793 AssertComRCReturnVoid(autoCaller.rc());
11794
11795 AutoCaller peerCaller(mPeer);
11796 AssertComRCReturnVoid(peerCaller.rc());
11797
11798 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11799
11800 /*
11801 * use safe commit to ensure Snapshot machines (that share mUserData)
11802 * will still refer to a valid memory location
11803 */
11804 mUserData.commitCopy();
11805
11806 mHWData.commit();
11807
11808 if (mMediumAttachments.isBackedUp())
11809 i_commitMedia(Global::IsOnline(mData->mMachineState));
11810
11811 mBIOSSettings->i_commit();
11812 mRecordingSettings->i_commit();
11813 mVRDEServer->i_commit();
11814 mAudioAdapter->i_commit();
11815 mUSBDeviceFilters->i_commit();
11816 mBandwidthControl->i_commit();
11817
11818 /* Since mNetworkAdapters is a list which might have been changed (resized)
11819 * without using the Backupable<> template we need to handle the copying
11820 * of the list entries manually, including the creation of peers for the
11821 * new objects. */
11822 bool commitNetworkAdapters = false;
11823 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11824 if (mPeer)
11825 {
11826 /* commit everything, even the ones which will go away */
11827 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11828 mNetworkAdapters[slot]->i_commit();
11829 /* copy over the new entries, creating a peer and uninit the original */
11830 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11831 for (size_t slot = 0; slot < newSize; slot++)
11832 {
11833 /* look if this adapter has a peer device */
11834 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11835 if (!peer)
11836 {
11837 /* no peer means the adapter is a newly created one;
11838 * create a peer owning data this data share it with */
11839 peer.createObject();
11840 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11841 }
11842 mPeer->mNetworkAdapters[slot] = peer;
11843 }
11844 /* uninit any no longer needed network adapters */
11845 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11846 mNetworkAdapters[slot]->uninit();
11847 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11848 {
11849 if (mPeer->mNetworkAdapters[slot])
11850 mPeer->mNetworkAdapters[slot]->uninit();
11851 }
11852 /* Keep the original network adapter count until this point, so that
11853 * discarding a chipset type change will not lose settings. */
11854 mNetworkAdapters.resize(newSize);
11855 mPeer->mNetworkAdapters.resize(newSize);
11856 }
11857 else
11858 {
11859 /* we have no peer (our parent is the newly created machine);
11860 * just commit changes to the network adapters */
11861 commitNetworkAdapters = true;
11862 }
11863 if (commitNetworkAdapters)
11864 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11865 mNetworkAdapters[slot]->i_commit();
11866
11867 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11868 mSerialPorts[slot]->i_commit();
11869 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11870 mParallelPorts[slot]->i_commit();
11871
11872 bool commitStorageControllers = false;
11873
11874 if (mStorageControllers.isBackedUp())
11875 {
11876 mStorageControllers.commit();
11877
11878 if (mPeer)
11879 {
11880 /* Commit all changes to new controllers (this will reshare data with
11881 * peers for those who have peers) */
11882 StorageControllerList *newList = new StorageControllerList();
11883 for (StorageControllerList::const_iterator
11884 it = mStorageControllers->begin();
11885 it != mStorageControllers->end();
11886 ++it)
11887 {
11888 (*it)->i_commit();
11889
11890 /* look if this controller has a peer device */
11891 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11892 if (!peer)
11893 {
11894 /* no peer means the device is a newly created one;
11895 * create a peer owning data this device share it with */
11896 peer.createObject();
11897 peer->init(mPeer, *it, true /* aReshare */);
11898 }
11899 else
11900 {
11901 /* remove peer from the old list */
11902 mPeer->mStorageControllers->remove(peer);
11903 }
11904 /* and add it to the new list */
11905 newList->push_back(peer);
11906 }
11907
11908 /* uninit old peer's controllers that are left */
11909 for (StorageControllerList::const_iterator
11910 it = mPeer->mStorageControllers->begin();
11911 it != mPeer->mStorageControllers->end();
11912 ++it)
11913 {
11914 (*it)->uninit();
11915 }
11916
11917 /* attach new list of controllers to our peer */
11918 mPeer->mStorageControllers.attach(newList);
11919 }
11920 else
11921 {
11922 /* we have no peer (our parent is the newly created machine);
11923 * just commit changes to devices */
11924 commitStorageControllers = true;
11925 }
11926 }
11927 else
11928 {
11929 /* the list of controllers itself is not changed,
11930 * just commit changes to controllers themselves */
11931 commitStorageControllers = true;
11932 }
11933
11934 if (commitStorageControllers)
11935 {
11936 for (StorageControllerList::const_iterator
11937 it = mStorageControllers->begin();
11938 it != mStorageControllers->end();
11939 ++it)
11940 {
11941 (*it)->i_commit();
11942 }
11943 }
11944
11945 bool commitUSBControllers = false;
11946
11947 if (mUSBControllers.isBackedUp())
11948 {
11949 mUSBControllers.commit();
11950
11951 if (mPeer)
11952 {
11953 /* Commit all changes to new controllers (this will reshare data with
11954 * peers for those who have peers) */
11955 USBControllerList *newList = new USBControllerList();
11956 for (USBControllerList::const_iterator
11957 it = mUSBControllers->begin();
11958 it != mUSBControllers->end();
11959 ++it)
11960 {
11961 (*it)->i_commit();
11962
11963 /* look if this controller has a peer device */
11964 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11965 if (!peer)
11966 {
11967 /* no peer means the device is a newly created one;
11968 * create a peer owning data this device share it with */
11969 peer.createObject();
11970 peer->init(mPeer, *it, true /* aReshare */);
11971 }
11972 else
11973 {
11974 /* remove peer from the old list */
11975 mPeer->mUSBControllers->remove(peer);
11976 }
11977 /* and add it to the new list */
11978 newList->push_back(peer);
11979 }
11980
11981 /* uninit old peer's controllers that are left */
11982 for (USBControllerList::const_iterator
11983 it = mPeer->mUSBControllers->begin();
11984 it != mPeer->mUSBControllers->end();
11985 ++it)
11986 {
11987 (*it)->uninit();
11988 }
11989
11990 /* attach new list of controllers to our peer */
11991 mPeer->mUSBControllers.attach(newList);
11992 }
11993 else
11994 {
11995 /* we have no peer (our parent is the newly created machine);
11996 * just commit changes to devices */
11997 commitUSBControllers = true;
11998 }
11999 }
12000 else
12001 {
12002 /* the list of controllers itself is not changed,
12003 * just commit changes to controllers themselves */
12004 commitUSBControllers = true;
12005 }
12006
12007 if (commitUSBControllers)
12008 {
12009 for (USBControllerList::const_iterator
12010 it = mUSBControllers->begin();
12011 it != mUSBControllers->end();
12012 ++it)
12013 {
12014 (*it)->i_commit();
12015 }
12016 }
12017
12018 if (i_isSessionMachine())
12019 {
12020 /* attach new data to the primary machine and reshare it */
12021 mPeer->mUserData.attach(mUserData);
12022 mPeer->mHWData.attach(mHWData);
12023 /* mmMediumAttachments is reshared by fixupMedia */
12024 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12025 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12026 }
12027}
12028
12029/**
12030 * Copies all the hardware data from the given machine.
12031 *
12032 * Currently, only called when the VM is being restored from a snapshot. In
12033 * particular, this implies that the VM is not running during this method's
12034 * call.
12035 *
12036 * @note This method must be called from under this object's lock.
12037 *
12038 * @note This method doesn't call #i_commit(), so all data remains backed up and
12039 * unsaved.
12040 */
12041void Machine::i_copyFrom(Machine *aThat)
12042{
12043 AssertReturnVoid(!i_isSnapshotMachine());
12044 AssertReturnVoid(aThat->i_isSnapshotMachine());
12045
12046 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12047
12048 mHWData.assignCopy(aThat->mHWData);
12049
12050 // create copies of all shared folders (mHWData after attaching a copy
12051 // contains just references to original objects)
12052 for (HWData::SharedFolderList::iterator
12053 it = mHWData->mSharedFolders.begin();
12054 it != mHWData->mSharedFolders.end();
12055 ++it)
12056 {
12057 ComObjPtr<SharedFolder> folder;
12058 folder.createObject();
12059 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12060 AssertComRC(rc);
12061 *it = folder;
12062 }
12063
12064 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12065 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12066 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12067 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12068 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12069 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12070
12071 /* create private copies of all controllers */
12072 mStorageControllers.backup();
12073 mStorageControllers->clear();
12074 for (StorageControllerList::const_iterator
12075 it = aThat->mStorageControllers->begin();
12076 it != aThat->mStorageControllers->end();
12077 ++it)
12078 {
12079 ComObjPtr<StorageController> ctrl;
12080 ctrl.createObject();
12081 ctrl->initCopy(this, *it);
12082 mStorageControllers->push_back(ctrl);
12083 }
12084
12085 /* create private copies of all USB controllers */
12086 mUSBControllers.backup();
12087 mUSBControllers->clear();
12088 for (USBControllerList::const_iterator
12089 it = aThat->mUSBControllers->begin();
12090 it != aThat->mUSBControllers->end();
12091 ++it)
12092 {
12093 ComObjPtr<USBController> ctrl;
12094 ctrl.createObject();
12095 ctrl->initCopy(this, *it);
12096 mUSBControllers->push_back(ctrl);
12097 }
12098
12099 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12100 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12101 {
12102 if (mNetworkAdapters[slot].isNotNull())
12103 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12104 else
12105 {
12106 unconst(mNetworkAdapters[slot]).createObject();
12107 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12108 }
12109 }
12110 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12111 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12112 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12113 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12114}
12115
12116/**
12117 * Returns whether the given storage controller is hotplug capable.
12118 *
12119 * @returns true if the controller supports hotplugging
12120 * false otherwise.
12121 * @param enmCtrlType The controller type to check for.
12122 */
12123bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12124{
12125 ComPtr<ISystemProperties> systemProperties;
12126 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12127 if (FAILED(rc))
12128 return false;
12129
12130 BOOL aHotplugCapable = FALSE;
12131 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12132
12133 return RT_BOOL(aHotplugCapable);
12134}
12135
12136#ifdef VBOX_WITH_RESOURCE_USAGE_API
12137
12138void Machine::i_getDiskList(MediaList &list)
12139{
12140 for (MediumAttachmentList::const_iterator
12141 it = mMediumAttachments->begin();
12142 it != mMediumAttachments->end();
12143 ++it)
12144 {
12145 MediumAttachment *pAttach = *it;
12146 /* just in case */
12147 AssertContinue(pAttach);
12148
12149 AutoCaller localAutoCallerA(pAttach);
12150 if (FAILED(localAutoCallerA.rc())) continue;
12151
12152 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12153
12154 if (pAttach->i_getType() == DeviceType_HardDisk)
12155 list.push_back(pAttach->i_getMedium());
12156 }
12157}
12158
12159void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12160{
12161 AssertReturnVoid(isWriteLockOnCurrentThread());
12162 AssertPtrReturnVoid(aCollector);
12163
12164 pm::CollectorHAL *hal = aCollector->getHAL();
12165 /* Create sub metrics */
12166 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12167 "Percentage of processor time spent in user mode by the VM process.");
12168 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12169 "Percentage of processor time spent in kernel mode by the VM process.");
12170 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12171 "Size of resident portion of VM process in memory.");
12172 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12173 "Actual size of all VM disks combined.");
12174 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12175 "Network receive rate.");
12176 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12177 "Network transmit rate.");
12178 /* Create and register base metrics */
12179 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12180 cpuLoadUser, cpuLoadKernel);
12181 aCollector->registerBaseMetric(cpuLoad);
12182 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12183 ramUsageUsed);
12184 aCollector->registerBaseMetric(ramUsage);
12185 MediaList disks;
12186 i_getDiskList(disks);
12187 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12188 diskUsageUsed);
12189 aCollector->registerBaseMetric(diskUsage);
12190
12191 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12192 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12193 new pm::AggregateAvg()));
12194 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12195 new pm::AggregateMin()));
12196 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12197 new pm::AggregateMax()));
12198 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12199 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12200 new pm::AggregateAvg()));
12201 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12202 new pm::AggregateMin()));
12203 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12204 new pm::AggregateMax()));
12205
12206 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12207 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12208 new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12210 new pm::AggregateMin()));
12211 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12212 new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12215 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12216 new pm::AggregateAvg()));
12217 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12218 new pm::AggregateMin()));
12219 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12220 new pm::AggregateMax()));
12221
12222
12223 /* Guest metrics collector */
12224 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12225 aCollector->registerGuest(mCollectorGuest);
12226 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12227
12228 /* Create sub metrics */
12229 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12230 "Percentage of processor time spent in user mode as seen by the guest.");
12231 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12232 "Percentage of processor time spent in kernel mode as seen by the guest.");
12233 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12234 "Percentage of processor time spent idling as seen by the guest.");
12235
12236 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12237 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12238 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12239 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12240 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12241 pm::SubMetric *guestMemCache = new pm::SubMetric(
12242 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12243
12244 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12245 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12246
12247 /* Create and register base metrics */
12248 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12249 machineNetRx, machineNetTx);
12250 aCollector->registerBaseMetric(machineNetRate);
12251
12252 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12253 guestLoadUser, guestLoadKernel, guestLoadIdle);
12254 aCollector->registerBaseMetric(guestCpuLoad);
12255
12256 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12257 guestMemTotal, guestMemFree,
12258 guestMemBalloon, guestMemShared,
12259 guestMemCache, guestPagedTotal);
12260 aCollector->registerBaseMetric(guestCpuMem);
12261
12262 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12263 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12264 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12265 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12266
12267 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12268 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12269 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12270 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12271
12272 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12273 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12274 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12276
12277 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12278 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12279 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12281
12282 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12283 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12284 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12285 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12286
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12288 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12291
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12293 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12294 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12295 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12296
12297 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12298 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12299 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12300 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12301
12302 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12303 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12304 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12305 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12306
12307 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12308 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12309 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12310 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12311
12312 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12313 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12314 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12315 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12316}
12317
12318void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12319{
12320 AssertReturnVoid(isWriteLockOnCurrentThread());
12321
12322 if (aCollector)
12323 {
12324 aCollector->unregisterMetricsFor(aMachine);
12325 aCollector->unregisterBaseMetricsFor(aMachine);
12326 }
12327}
12328
12329#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12330
12331
12332////////////////////////////////////////////////////////////////////////////////
12333
12334DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12335
12336HRESULT SessionMachine::FinalConstruct()
12337{
12338 LogFlowThisFunc(("\n"));
12339
12340 mClientToken = NULL;
12341
12342 return BaseFinalConstruct();
12343}
12344
12345void SessionMachine::FinalRelease()
12346{
12347 LogFlowThisFunc(("\n"));
12348
12349 Assert(!mClientToken);
12350 /* paranoia, should not hang around any more */
12351 if (mClientToken)
12352 {
12353 delete mClientToken;
12354 mClientToken = NULL;
12355 }
12356
12357 uninit(Uninit::Unexpected);
12358
12359 BaseFinalRelease();
12360}
12361
12362/**
12363 * @note Must be called only by Machine::LockMachine() from its own write lock.
12364 */
12365HRESULT SessionMachine::init(Machine *aMachine)
12366{
12367 LogFlowThisFuncEnter();
12368 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12369
12370 AssertReturn(aMachine, E_INVALIDARG);
12371
12372 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12373
12374 /* Enclose the state transition NotReady->InInit->Ready */
12375 AutoInitSpan autoInitSpan(this);
12376 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12377
12378 HRESULT rc = S_OK;
12379
12380 RT_ZERO(mAuthLibCtx);
12381
12382 /* create the machine client token */
12383 try
12384 {
12385 mClientToken = new ClientToken(aMachine, this);
12386 if (!mClientToken->isReady())
12387 {
12388 delete mClientToken;
12389 mClientToken = NULL;
12390 rc = E_FAIL;
12391 }
12392 }
12393 catch (std::bad_alloc &)
12394 {
12395 rc = E_OUTOFMEMORY;
12396 }
12397 if (FAILED(rc))
12398 return rc;
12399
12400 /* memorize the peer Machine */
12401 unconst(mPeer) = aMachine;
12402 /* share the parent pointer */
12403 unconst(mParent) = aMachine->mParent;
12404
12405 /* take the pointers to data to share */
12406 mData.share(aMachine->mData);
12407 mSSData.share(aMachine->mSSData);
12408
12409 mUserData.share(aMachine->mUserData);
12410 mHWData.share(aMachine->mHWData);
12411 mMediumAttachments.share(aMachine->mMediumAttachments);
12412
12413 mStorageControllers.allocate();
12414 for (StorageControllerList::const_iterator
12415 it = aMachine->mStorageControllers->begin();
12416 it != aMachine->mStorageControllers->end();
12417 ++it)
12418 {
12419 ComObjPtr<StorageController> ctl;
12420 ctl.createObject();
12421 ctl->init(this, *it);
12422 mStorageControllers->push_back(ctl);
12423 }
12424
12425 mUSBControllers.allocate();
12426 for (USBControllerList::const_iterator
12427 it = aMachine->mUSBControllers->begin();
12428 it != aMachine->mUSBControllers->end();
12429 ++it)
12430 {
12431 ComObjPtr<USBController> ctl;
12432 ctl.createObject();
12433 ctl->init(this, *it);
12434 mUSBControllers->push_back(ctl);
12435 }
12436
12437 unconst(mBIOSSettings).createObject();
12438 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12439 unconst(mRecordingSettings).createObject();
12440 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12441 /* create another VRDEServer object that will be mutable */
12442 unconst(mVRDEServer).createObject();
12443 mVRDEServer->init(this, aMachine->mVRDEServer);
12444 /* create another audio adapter object that will be mutable */
12445 unconst(mAudioAdapter).createObject();
12446 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12447 /* create a list of serial ports that will be mutable */
12448 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12449 {
12450 unconst(mSerialPorts[slot]).createObject();
12451 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12452 }
12453 /* create a list of parallel ports that will be mutable */
12454 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12455 {
12456 unconst(mParallelPorts[slot]).createObject();
12457 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12458 }
12459
12460 /* create another USB device filters object that will be mutable */
12461 unconst(mUSBDeviceFilters).createObject();
12462 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12463
12464 /* create a list of network adapters that will be mutable */
12465 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12466 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12467 {
12468 unconst(mNetworkAdapters[slot]).createObject();
12469 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12470 }
12471
12472 /* create another bandwidth control object that will be mutable */
12473 unconst(mBandwidthControl).createObject();
12474 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12475
12476 /* default is to delete saved state on Saved -> PoweredOff transition */
12477 mRemoveSavedState = true;
12478
12479 /* Confirm a successful initialization when it's the case */
12480 autoInitSpan.setSucceeded();
12481
12482 miNATNetworksStarted = 0;
12483
12484 LogFlowThisFuncLeave();
12485 return rc;
12486}
12487
12488/**
12489 * Uninitializes this session object. If the reason is other than
12490 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12491 * or the client watcher code.
12492 *
12493 * @param aReason uninitialization reason
12494 *
12495 * @note Locks mParent + this object for writing.
12496 */
12497void SessionMachine::uninit(Uninit::Reason aReason)
12498{
12499 LogFlowThisFuncEnter();
12500 LogFlowThisFunc(("reason=%d\n", aReason));
12501
12502 /*
12503 * Strongly reference ourselves to prevent this object deletion after
12504 * mData->mSession.mMachine.setNull() below (which can release the last
12505 * reference and call the destructor). Important: this must be done before
12506 * accessing any members (and before AutoUninitSpan that does it as well).
12507 * This self reference will be released as the very last step on return.
12508 */
12509 ComObjPtr<SessionMachine> selfRef;
12510 if (aReason != Uninit::Unexpected)
12511 selfRef = this;
12512
12513 /* Enclose the state transition Ready->InUninit->NotReady */
12514 AutoUninitSpan autoUninitSpan(this);
12515 if (autoUninitSpan.uninitDone())
12516 {
12517 LogFlowThisFunc(("Already uninitialized\n"));
12518 LogFlowThisFuncLeave();
12519 return;
12520 }
12521
12522 if (autoUninitSpan.initFailed())
12523 {
12524 /* We've been called by init() because it's failed. It's not really
12525 * necessary (nor it's safe) to perform the regular uninit sequence
12526 * below, the following is enough.
12527 */
12528 LogFlowThisFunc(("Initialization failed.\n"));
12529 /* destroy the machine client token */
12530 if (mClientToken)
12531 {
12532 delete mClientToken;
12533 mClientToken = NULL;
12534 }
12535 uninitDataAndChildObjects();
12536 mData.free();
12537 unconst(mParent) = NULL;
12538 unconst(mPeer) = NULL;
12539 LogFlowThisFuncLeave();
12540 return;
12541 }
12542
12543 MachineState_T lastState;
12544 {
12545 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12546 lastState = mData->mMachineState;
12547 }
12548 NOREF(lastState);
12549
12550#ifdef VBOX_WITH_USB
12551 // release all captured USB devices, but do this before requesting the locks below
12552 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12553 {
12554 /* Console::captureUSBDevices() is called in the VM process only after
12555 * setting the machine state to Starting or Restoring.
12556 * Console::detachAllUSBDevices() will be called upon successful
12557 * termination. So, we need to release USB devices only if there was
12558 * an abnormal termination of a running VM.
12559 *
12560 * This is identical to SessionMachine::DetachAllUSBDevices except
12561 * for the aAbnormal argument. */
12562 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12563 AssertComRC(rc);
12564 NOREF(rc);
12565
12566 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12567 if (service)
12568 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12569 }
12570#endif /* VBOX_WITH_USB */
12571
12572 // we need to lock this object in uninit() because the lock is shared
12573 // with mPeer (as well as data we modify below). mParent lock is needed
12574 // by several calls to it.
12575 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12576
12577#ifdef VBOX_WITH_RESOURCE_USAGE_API
12578 /*
12579 * It is safe to call Machine::i_unregisterMetrics() here because
12580 * PerformanceCollector::samplerCallback no longer accesses guest methods
12581 * holding the lock.
12582 */
12583 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12584 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12585 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12586 if (mCollectorGuest)
12587 {
12588 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12589 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12590 mCollectorGuest = NULL;
12591 }
12592#endif
12593
12594 if (aReason == Uninit::Abnormal)
12595 {
12596 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12597
12598 /* reset the state to Aborted */
12599 if (mData->mMachineState != MachineState_Aborted)
12600 i_setMachineState(MachineState_Aborted);
12601 }
12602
12603 // any machine settings modified?
12604 if (mData->flModifications)
12605 {
12606 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12607 i_rollback(false /* aNotify */);
12608 }
12609
12610 mData->mSession.mPID = NIL_RTPROCESS;
12611
12612 if (aReason == Uninit::Unexpected)
12613 {
12614 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12615 * client watcher thread to update the set of machines that have open
12616 * sessions. */
12617 mParent->i_updateClientWatcher();
12618 }
12619
12620 /* uninitialize all remote controls */
12621 if (mData->mSession.mRemoteControls.size())
12622 {
12623 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12624 mData->mSession.mRemoteControls.size()));
12625
12626 /* Always restart a the beginning, since the iterator is invalidated
12627 * by using erase(). */
12628 for (Data::Session::RemoteControlList::iterator
12629 it = mData->mSession.mRemoteControls.begin();
12630 it != mData->mSession.mRemoteControls.end();
12631 it = mData->mSession.mRemoteControls.begin())
12632 {
12633 ComPtr<IInternalSessionControl> pControl = *it;
12634 mData->mSession.mRemoteControls.erase(it);
12635 multilock.release();
12636 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12637 HRESULT rc = pControl->Uninitialize();
12638 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12639 if (FAILED(rc))
12640 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12641 multilock.acquire();
12642 }
12643 mData->mSession.mRemoteControls.clear();
12644 }
12645
12646 /* Remove all references to the NAT network service. The service will stop
12647 * if all references (also from other VMs) are removed. */
12648 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12649 {
12650 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12651 {
12652 BOOL enabled;
12653 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12654 if ( FAILED(hrc)
12655 || !enabled)
12656 continue;
12657
12658 NetworkAttachmentType_T type;
12659 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12660 if ( SUCCEEDED(hrc)
12661 && type == NetworkAttachmentType_NATNetwork)
12662 {
12663 Bstr name;
12664 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12665 if (SUCCEEDED(hrc))
12666 {
12667 multilock.release();
12668 Utf8Str strName(name);
12669 LogRel(("VM '%s' stops using NAT network '%s'\n",
12670 mUserData->s.strName.c_str(), strName.c_str()));
12671 mParent->i_natNetworkRefDec(strName);
12672 multilock.acquire();
12673 }
12674 }
12675 }
12676 }
12677
12678 /*
12679 * An expected uninitialization can come only from #i_checkForDeath().
12680 * Otherwise it means that something's gone really wrong (for example,
12681 * the Session implementation has released the VirtualBox reference
12682 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12683 * etc). However, it's also possible, that the client releases the IPC
12684 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12685 * but the VirtualBox release event comes first to the server process.
12686 * This case is practically possible, so we should not assert on an
12687 * unexpected uninit, just log a warning.
12688 */
12689
12690 if (aReason == Uninit::Unexpected)
12691 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12692
12693 if (aReason != Uninit::Normal)
12694 {
12695 mData->mSession.mDirectControl.setNull();
12696 }
12697 else
12698 {
12699 /* this must be null here (see #OnSessionEnd()) */
12700 Assert(mData->mSession.mDirectControl.isNull());
12701 Assert(mData->mSession.mState == SessionState_Unlocking);
12702 Assert(!mData->mSession.mProgress.isNull());
12703 }
12704 if (mData->mSession.mProgress)
12705 {
12706 if (aReason == Uninit::Normal)
12707 mData->mSession.mProgress->i_notifyComplete(S_OK);
12708 else
12709 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12710 COM_IIDOF(ISession),
12711 getComponentName(),
12712 tr("The VM session was aborted"));
12713 mData->mSession.mProgress.setNull();
12714 }
12715
12716 if (mConsoleTaskData.mProgress)
12717 {
12718 Assert(aReason == Uninit::Abnormal);
12719 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12720 COM_IIDOF(ISession),
12721 getComponentName(),
12722 tr("The VM session was aborted"));
12723 mConsoleTaskData.mProgress.setNull();
12724 }
12725
12726 /* remove the association between the peer machine and this session machine */
12727 Assert( (SessionMachine*)mData->mSession.mMachine == this
12728 || aReason == Uninit::Unexpected);
12729
12730 /* reset the rest of session data */
12731 mData->mSession.mLockType = LockType_Null;
12732 mData->mSession.mMachine.setNull();
12733 mData->mSession.mState = SessionState_Unlocked;
12734 mData->mSession.mName.setNull();
12735
12736 /* destroy the machine client token before leaving the exclusive lock */
12737 if (mClientToken)
12738 {
12739 delete mClientToken;
12740 mClientToken = NULL;
12741 }
12742
12743 /* fire an event */
12744 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12745
12746 uninitDataAndChildObjects();
12747
12748 /* free the essential data structure last */
12749 mData.free();
12750
12751 /* release the exclusive lock before setting the below two to NULL */
12752 multilock.release();
12753
12754 unconst(mParent) = NULL;
12755 unconst(mPeer) = NULL;
12756
12757 AuthLibUnload(&mAuthLibCtx);
12758
12759 LogFlowThisFuncLeave();
12760}
12761
12762// util::Lockable interface
12763////////////////////////////////////////////////////////////////////////////////
12764
12765/**
12766 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12767 * with the primary Machine instance (mPeer).
12768 */
12769RWLockHandle *SessionMachine::lockHandle() const
12770{
12771 AssertReturn(mPeer != NULL, NULL);
12772 return mPeer->lockHandle();
12773}
12774
12775// IInternalMachineControl methods
12776////////////////////////////////////////////////////////////////////////////////
12777
12778/**
12779 * Passes collected guest statistics to performance collector object
12780 */
12781HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12782 ULONG aCpuKernel, ULONG aCpuIdle,
12783 ULONG aMemTotal, ULONG aMemFree,
12784 ULONG aMemBalloon, ULONG aMemShared,
12785 ULONG aMemCache, ULONG aPageTotal,
12786 ULONG aAllocVMM, ULONG aFreeVMM,
12787 ULONG aBalloonedVMM, ULONG aSharedVMM,
12788 ULONG aVmNetRx, ULONG aVmNetTx)
12789{
12790#ifdef VBOX_WITH_RESOURCE_USAGE_API
12791 if (mCollectorGuest)
12792 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12793 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12794 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12795 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12796
12797 return S_OK;
12798#else
12799 NOREF(aValidStats);
12800 NOREF(aCpuUser);
12801 NOREF(aCpuKernel);
12802 NOREF(aCpuIdle);
12803 NOREF(aMemTotal);
12804 NOREF(aMemFree);
12805 NOREF(aMemBalloon);
12806 NOREF(aMemShared);
12807 NOREF(aMemCache);
12808 NOREF(aPageTotal);
12809 NOREF(aAllocVMM);
12810 NOREF(aFreeVMM);
12811 NOREF(aBalloonedVMM);
12812 NOREF(aSharedVMM);
12813 NOREF(aVmNetRx);
12814 NOREF(aVmNetTx);
12815 return E_NOTIMPL;
12816#endif
12817}
12818
12819////////////////////////////////////////////////////////////////////////////////
12820//
12821// SessionMachine task records
12822//
12823////////////////////////////////////////////////////////////////////////////////
12824
12825/**
12826 * Task record for saving the machine state.
12827 */
12828class SessionMachine::SaveStateTask
12829 : public Machine::Task
12830{
12831public:
12832 SaveStateTask(SessionMachine *m,
12833 Progress *p,
12834 const Utf8Str &t,
12835 Reason_T enmReason,
12836 const Utf8Str &strStateFilePath)
12837 : Task(m, p, t),
12838 m_enmReason(enmReason),
12839 m_strStateFilePath(strStateFilePath)
12840 {}
12841
12842private:
12843 void handler()
12844 {
12845 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12846 }
12847
12848 Reason_T m_enmReason;
12849 Utf8Str m_strStateFilePath;
12850
12851 friend class SessionMachine;
12852};
12853
12854/**
12855 * Task thread implementation for SessionMachine::SaveState(), called from
12856 * SessionMachine::taskHandler().
12857 *
12858 * @note Locks this object for writing.
12859 *
12860 * @param task
12861 * @return
12862 */
12863void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12864{
12865 LogFlowThisFuncEnter();
12866
12867 AutoCaller autoCaller(this);
12868 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12869 if (FAILED(autoCaller.rc()))
12870 {
12871 /* we might have been uninitialized because the session was accidentally
12872 * closed by the client, so don't assert */
12873 HRESULT rc = setError(E_FAIL,
12874 tr("The session has been accidentally closed"));
12875 task.m_pProgress->i_notifyComplete(rc);
12876 LogFlowThisFuncLeave();
12877 return;
12878 }
12879
12880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12881
12882 HRESULT rc = S_OK;
12883
12884 try
12885 {
12886 ComPtr<IInternalSessionControl> directControl;
12887 if (mData->mSession.mLockType == LockType_VM)
12888 directControl = mData->mSession.mDirectControl;
12889 if (directControl.isNull())
12890 throw setError(VBOX_E_INVALID_VM_STATE,
12891 tr("Trying to save state without a running VM"));
12892 alock.release();
12893 BOOL fSuspendedBySave;
12894 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12895 Assert(!fSuspendedBySave);
12896 alock.acquire();
12897
12898 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12899 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12900 throw E_FAIL);
12901
12902 if (SUCCEEDED(rc))
12903 {
12904 mSSData->strStateFilePath = task.m_strStateFilePath;
12905
12906 /* save all VM settings */
12907 rc = i_saveSettings(NULL);
12908 // no need to check whether VirtualBox.xml needs saving also since
12909 // we can't have a name change pending at this point
12910 }
12911 else
12912 {
12913 // On failure, set the state to the state we had at the beginning.
12914 i_setMachineState(task.m_machineStateBackup);
12915 i_updateMachineStateOnClient();
12916
12917 // Delete the saved state file (might have been already created).
12918 // No need to check whether this is shared with a snapshot here
12919 // because we certainly created a fresh saved state file here.
12920 RTFileDelete(task.m_strStateFilePath.c_str());
12921 }
12922 }
12923 catch (HRESULT aRC) { rc = aRC; }
12924
12925 task.m_pProgress->i_notifyComplete(rc);
12926
12927 LogFlowThisFuncLeave();
12928}
12929
12930/**
12931 * @note Locks this object for writing.
12932 */
12933HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12934{
12935 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12936}
12937
12938HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12939{
12940 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12941
12942 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12943 if (FAILED(rc)) return rc;
12944
12945 if ( mData->mMachineState != MachineState_Running
12946 && mData->mMachineState != MachineState_Paused
12947 )
12948 return setError(VBOX_E_INVALID_VM_STATE,
12949 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12950 Global::stringifyMachineState(mData->mMachineState));
12951
12952 ComObjPtr<Progress> pProgress;
12953 pProgress.createObject();
12954 rc = pProgress->init(i_getVirtualBox(),
12955 static_cast<IMachine *>(this) /* aInitiator */,
12956 tr("Saving the execution state of the virtual machine"),
12957 FALSE /* aCancelable */);
12958 if (FAILED(rc))
12959 return rc;
12960
12961 Utf8Str strStateFilePath;
12962 i_composeSavedStateFilename(strStateFilePath);
12963
12964 /* create and start the task on a separate thread (note that it will not
12965 * start working until we release alock) */
12966 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12967 rc = pTask->createThread();
12968 if (FAILED(rc))
12969 return rc;
12970
12971 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12972 i_setMachineState(MachineState_Saving);
12973 i_updateMachineStateOnClient();
12974
12975 pProgress.queryInterfaceTo(aProgress.asOutParam());
12976
12977 return S_OK;
12978}
12979
12980/**
12981 * @note Locks this object for writing.
12982 */
12983HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12984{
12985 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12986
12987 HRESULT rc = i_checkStateDependency(MutableStateDep);
12988 if (FAILED(rc)) return rc;
12989
12990 if ( mData->mMachineState != MachineState_PoweredOff
12991 && mData->mMachineState != MachineState_Teleported
12992 && mData->mMachineState != MachineState_Aborted
12993 )
12994 return setError(VBOX_E_INVALID_VM_STATE,
12995 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12996 Global::stringifyMachineState(mData->mMachineState));
12997
12998 com::Utf8Str stateFilePathFull;
12999 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
13000 if (RT_FAILURE(vrc))
13001 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
13002 tr("Invalid saved state file path '%s' (%Rrc)"),
13003 aSavedStateFile.c_str(),
13004 vrc);
13005
13006 mSSData->strStateFilePath = stateFilePathFull;
13007
13008 /* The below i_setMachineState() will detect the state transition and will
13009 * update the settings file */
13010
13011 return i_setMachineState(MachineState_Saved);
13012}
13013
13014/**
13015 * @note Locks this object for writing.
13016 */
13017HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
13018{
13019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13020
13021 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
13022 if (FAILED(rc)) return rc;
13023
13024 if (mData->mMachineState != MachineState_Saved)
13025 return setError(VBOX_E_INVALID_VM_STATE,
13026 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13027 Global::stringifyMachineState(mData->mMachineState));
13028
13029 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13030
13031 /*
13032 * Saved -> PoweredOff transition will be detected in the SessionMachine
13033 * and properly handled.
13034 */
13035 rc = i_setMachineState(MachineState_PoweredOff);
13036 return rc;
13037}
13038
13039
13040/**
13041 * @note Locks the same as #i_setMachineState() does.
13042 */
13043HRESULT SessionMachine::updateState(MachineState_T aState)
13044{
13045 return i_setMachineState(aState);
13046}
13047
13048/**
13049 * @note Locks this object for writing.
13050 */
13051HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13052{
13053 IProgress *pProgress(aProgress);
13054
13055 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13056
13057 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13058
13059 if (mData->mSession.mState != SessionState_Locked)
13060 return VBOX_E_INVALID_OBJECT_STATE;
13061
13062 if (!mData->mSession.mProgress.isNull())
13063 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13064
13065 /* If we didn't reference the NAT network service yet, add a reference to
13066 * force a start */
13067 if (miNATNetworksStarted < 1)
13068 {
13069 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13070 {
13071 BOOL enabled;
13072 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13073 if ( FAILED(hrc)
13074 || !enabled)
13075 continue;
13076
13077 NetworkAttachmentType_T type;
13078 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13079 if ( SUCCEEDED(hrc)
13080 && type == NetworkAttachmentType_NATNetwork)
13081 {
13082 Bstr name;
13083 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13084 if (SUCCEEDED(hrc))
13085 {
13086 Utf8Str strName(name);
13087 LogRel(("VM '%s' starts using NAT network '%s'\n",
13088 mUserData->s.strName.c_str(), strName.c_str()));
13089 mPeer->lockHandle()->unlockWrite();
13090 mParent->i_natNetworkRefInc(strName);
13091#ifdef RT_LOCK_STRICT
13092 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13093#else
13094 mPeer->lockHandle()->lockWrite();
13095#endif
13096 }
13097 }
13098 }
13099 miNATNetworksStarted++;
13100 }
13101
13102 LogFlowThisFunc(("returns S_OK.\n"));
13103 return S_OK;
13104}
13105
13106/**
13107 * @note Locks this object for writing.
13108 */
13109HRESULT SessionMachine::endPowerUp(LONG aResult)
13110{
13111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13112
13113 if (mData->mSession.mState != SessionState_Locked)
13114 return VBOX_E_INVALID_OBJECT_STATE;
13115
13116 /* Finalize the LaunchVMProcess progress object. */
13117 if (mData->mSession.mProgress)
13118 {
13119 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13120 mData->mSession.mProgress.setNull();
13121 }
13122
13123 if (SUCCEEDED((HRESULT)aResult))
13124 {
13125#ifdef VBOX_WITH_RESOURCE_USAGE_API
13126 /* The VM has been powered up successfully, so it makes sense
13127 * now to offer the performance metrics for a running machine
13128 * object. Doing it earlier wouldn't be safe. */
13129 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13130 mData->mSession.mPID);
13131#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13132 }
13133
13134 return S_OK;
13135}
13136
13137/**
13138 * @note Locks this object for writing.
13139 */
13140HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13141{
13142 LogFlowThisFuncEnter();
13143
13144 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13145
13146 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13147 E_FAIL);
13148
13149 /* create a progress object to track operation completion */
13150 ComObjPtr<Progress> pProgress;
13151 pProgress.createObject();
13152 pProgress->init(i_getVirtualBox(),
13153 static_cast<IMachine *>(this) /* aInitiator */,
13154 tr("Stopping the virtual machine"),
13155 FALSE /* aCancelable */);
13156
13157 /* fill in the console task data */
13158 mConsoleTaskData.mLastState = mData->mMachineState;
13159 mConsoleTaskData.mProgress = pProgress;
13160
13161 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13162 i_setMachineState(MachineState_Stopping);
13163
13164 pProgress.queryInterfaceTo(aProgress.asOutParam());
13165
13166 return S_OK;
13167}
13168
13169/**
13170 * @note Locks this object for writing.
13171 */
13172HRESULT SessionMachine::endPoweringDown(LONG aResult,
13173 const com::Utf8Str &aErrMsg)
13174{
13175 LogFlowThisFuncEnter();
13176
13177 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13178
13179 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13180 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13181 && mConsoleTaskData.mLastState != MachineState_Null,
13182 E_FAIL);
13183
13184 /*
13185 * On failure, set the state to the state we had when BeginPoweringDown()
13186 * was called (this is expected by Console::PowerDown() and the associated
13187 * task). On success the VM process already changed the state to
13188 * MachineState_PoweredOff, so no need to do anything.
13189 */
13190 if (FAILED(aResult))
13191 i_setMachineState(mConsoleTaskData.mLastState);
13192
13193 /* notify the progress object about operation completion */
13194 Assert(mConsoleTaskData.mProgress);
13195 if (SUCCEEDED(aResult))
13196 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13197 else
13198 {
13199 if (aErrMsg.length())
13200 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13201 COM_IIDOF(ISession),
13202 getComponentName(),
13203 aErrMsg.c_str());
13204 else
13205 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13206 }
13207
13208 /* clear out the temporary saved state data */
13209 mConsoleTaskData.mLastState = MachineState_Null;
13210 mConsoleTaskData.mProgress.setNull();
13211
13212 LogFlowThisFuncLeave();
13213 return S_OK;
13214}
13215
13216
13217/**
13218 * Goes through the USB filters of the given machine to see if the given
13219 * device matches any filter or not.
13220 *
13221 * @note Locks the same as USBController::hasMatchingFilter() does.
13222 */
13223HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13224 BOOL *aMatched,
13225 ULONG *aMaskedInterfaces)
13226{
13227 LogFlowThisFunc(("\n"));
13228
13229#ifdef VBOX_WITH_USB
13230 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13231#else
13232 NOREF(aDevice);
13233 NOREF(aMaskedInterfaces);
13234 *aMatched = FALSE;
13235#endif
13236
13237 return S_OK;
13238}
13239
13240/**
13241 * @note Locks the same as Host::captureUSBDevice() does.
13242 */
13243HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13244{
13245 LogFlowThisFunc(("\n"));
13246
13247#ifdef VBOX_WITH_USB
13248 /* if captureDeviceForVM() fails, it must have set extended error info */
13249 clearError();
13250 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13251 if (FAILED(rc)) return rc;
13252
13253 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13254 AssertReturn(service, E_FAIL);
13255 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13256#else
13257 NOREF(aId);
13258 return E_NOTIMPL;
13259#endif
13260}
13261
13262/**
13263 * @note Locks the same as Host::detachUSBDevice() does.
13264 */
13265HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13266 BOOL aDone)
13267{
13268 LogFlowThisFunc(("\n"));
13269
13270#ifdef VBOX_WITH_USB
13271 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13272 AssertReturn(service, E_FAIL);
13273 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13274#else
13275 NOREF(aId);
13276 NOREF(aDone);
13277 return E_NOTIMPL;
13278#endif
13279}
13280
13281/**
13282 * Inserts all machine filters to the USB proxy service and then calls
13283 * Host::autoCaptureUSBDevices().
13284 *
13285 * Called by Console from the VM process upon VM startup.
13286 *
13287 * @note Locks what called methods lock.
13288 */
13289HRESULT SessionMachine::autoCaptureUSBDevices()
13290{
13291 LogFlowThisFunc(("\n"));
13292
13293#ifdef VBOX_WITH_USB
13294 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13295 AssertComRC(rc);
13296 NOREF(rc);
13297
13298 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13299 AssertReturn(service, E_FAIL);
13300 return service->autoCaptureDevicesForVM(this);
13301#else
13302 return S_OK;
13303#endif
13304}
13305
13306/**
13307 * Removes all machine filters from the USB proxy service and then calls
13308 * Host::detachAllUSBDevices().
13309 *
13310 * Called by Console from the VM process upon normal VM termination or by
13311 * SessionMachine::uninit() upon abnormal VM termination (from under the
13312 * Machine/SessionMachine lock).
13313 *
13314 * @note Locks what called methods lock.
13315 */
13316HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13317{
13318 LogFlowThisFunc(("\n"));
13319
13320#ifdef VBOX_WITH_USB
13321 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13322 AssertComRC(rc);
13323 NOREF(rc);
13324
13325 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13326 AssertReturn(service, E_FAIL);
13327 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13328#else
13329 NOREF(aDone);
13330 return S_OK;
13331#endif
13332}
13333
13334/**
13335 * @note Locks this object for writing.
13336 */
13337HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13338 ComPtr<IProgress> &aProgress)
13339{
13340 LogFlowThisFuncEnter();
13341
13342 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13343 /*
13344 * We don't assert below because it might happen that a non-direct session
13345 * informs us it is closed right after we've been uninitialized -- it's ok.
13346 */
13347
13348 /* get IInternalSessionControl interface */
13349 ComPtr<IInternalSessionControl> control(aSession);
13350
13351 ComAssertRet(!control.isNull(), E_INVALIDARG);
13352
13353 /* Creating a Progress object requires the VirtualBox lock, and
13354 * thus locking it here is required by the lock order rules. */
13355 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13356
13357 if (control == mData->mSession.mDirectControl)
13358 {
13359 /* The direct session is being normally closed by the client process
13360 * ----------------------------------------------------------------- */
13361
13362 /* go to the closing state (essential for all open*Session() calls and
13363 * for #i_checkForDeath()) */
13364 Assert(mData->mSession.mState == SessionState_Locked);
13365 mData->mSession.mState = SessionState_Unlocking;
13366
13367 /* set direct control to NULL to release the remote instance */
13368 mData->mSession.mDirectControl.setNull();
13369 LogFlowThisFunc(("Direct control is set to NULL\n"));
13370
13371 if (mData->mSession.mProgress)
13372 {
13373 /* finalize the progress, someone might wait if a frontend
13374 * closes the session before powering on the VM. */
13375 mData->mSession.mProgress->notifyComplete(E_FAIL,
13376 COM_IIDOF(ISession),
13377 getComponentName(),
13378 tr("The VM session was closed before any attempt to power it on"));
13379 mData->mSession.mProgress.setNull();
13380 }
13381
13382 /* Create the progress object the client will use to wait until
13383 * #i_checkForDeath() is called to uninitialize this session object after
13384 * it releases the IPC semaphore.
13385 * Note! Because we're "reusing" mProgress here, this must be a proxy
13386 * object just like for LaunchVMProcess. */
13387 Assert(mData->mSession.mProgress.isNull());
13388 ComObjPtr<ProgressProxy> progress;
13389 progress.createObject();
13390 ComPtr<IUnknown> pPeer(mPeer);
13391 progress->init(mParent, pPeer,
13392 Bstr(tr("Closing session")).raw(),
13393 FALSE /* aCancelable */);
13394 progress.queryInterfaceTo(aProgress.asOutParam());
13395 mData->mSession.mProgress = progress;
13396 }
13397 else
13398 {
13399 /* the remote session is being normally closed */
13400 bool found = false;
13401 for (Data::Session::RemoteControlList::iterator
13402 it = mData->mSession.mRemoteControls.begin();
13403 it != mData->mSession.mRemoteControls.end();
13404 ++it)
13405 {
13406 if (control == *it)
13407 {
13408 found = true;
13409 // This MUST be erase(it), not remove(*it) as the latter
13410 // triggers a very nasty use after free due to the place where
13411 // the value "lives".
13412 mData->mSession.mRemoteControls.erase(it);
13413 break;
13414 }
13415 }
13416 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13417 E_INVALIDARG);
13418 }
13419
13420 /* signal the client watcher thread, because the client is going away */
13421 mParent->i_updateClientWatcher();
13422
13423 LogFlowThisFuncLeave();
13424 return S_OK;
13425}
13426
13427HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13428{
13429#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13430 ULONG uID;
13431 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13432 if (RT_SUCCESS(rc))
13433 {
13434 if (aID)
13435 *aID = uID;
13436 return S_OK;
13437 }
13438 return E_FAIL;
13439#else
13440 RT_NOREF(aParms, aID);
13441 ReturnComNotImplemented();
13442#endif
13443}
13444
13445HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13446{
13447#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13448 return mParent->i_onClipboardAreaUnregister(aID);
13449#else
13450 RT_NOREF(aID);
13451 ReturnComNotImplemented();
13452#endif
13453}
13454
13455HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13456{
13457#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13458 return mParent->i_onClipboardAreaAttach(aID);
13459#else
13460 RT_NOREF(aID);
13461 ReturnComNotImplemented();
13462#endif
13463}
13464HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13465{
13466#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13467 return mParent->i_onClipboardAreaDetach(aID);
13468#else
13469 RT_NOREF(aID);
13470 ReturnComNotImplemented();
13471#endif
13472}
13473
13474HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13475{
13476#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13477 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13478 if (aID)
13479 *aID = uID;
13480 return S_OK;
13481#else
13482 RT_NOREF(aID);
13483 ReturnComNotImplemented();
13484#endif
13485}
13486
13487HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13488{
13489#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13490 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13491 if (aRefCount)
13492 *aRefCount = uRefCount;
13493 return S_OK;
13494#else
13495 RT_NOREF(aID, aRefCount);
13496 ReturnComNotImplemented();
13497#endif
13498}
13499
13500HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13501 std::vector<com::Utf8Str> &aValues,
13502 std::vector<LONG64> &aTimestamps,
13503 std::vector<com::Utf8Str> &aFlags)
13504{
13505 LogFlowThisFunc(("\n"));
13506
13507#ifdef VBOX_WITH_GUEST_PROPS
13508 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13509
13510 size_t cEntries = mHWData->mGuestProperties.size();
13511 aNames.resize(cEntries);
13512 aValues.resize(cEntries);
13513 aTimestamps.resize(cEntries);
13514 aFlags.resize(cEntries);
13515
13516 size_t i = 0;
13517 for (HWData::GuestPropertyMap::const_iterator
13518 it = mHWData->mGuestProperties.begin();
13519 it != mHWData->mGuestProperties.end();
13520 ++it, ++i)
13521 {
13522 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13523 aNames[i] = it->first;
13524 aValues[i] = it->second.strValue;
13525 aTimestamps[i] = it->second.mTimestamp;
13526
13527 /* If it is NULL, keep it NULL. */
13528 if (it->second.mFlags)
13529 {
13530 GuestPropWriteFlags(it->second.mFlags, szFlags);
13531 aFlags[i] = szFlags;
13532 }
13533 else
13534 aFlags[i] = "";
13535 }
13536 return S_OK;
13537#else
13538 ReturnComNotImplemented();
13539#endif
13540}
13541
13542HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13543 const com::Utf8Str &aValue,
13544 LONG64 aTimestamp,
13545 const com::Utf8Str &aFlags)
13546{
13547 LogFlowThisFunc(("\n"));
13548
13549#ifdef VBOX_WITH_GUEST_PROPS
13550 try
13551 {
13552 /*
13553 * Convert input up front.
13554 */
13555 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13556 if (aFlags.length())
13557 {
13558 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13559 AssertRCReturn(vrc, E_INVALIDARG);
13560 }
13561
13562 /*
13563 * Now grab the object lock, validate the state and do the update.
13564 */
13565
13566 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13567
13568 if (!Global::IsOnline(mData->mMachineState))
13569 {
13570 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13571 VBOX_E_INVALID_VM_STATE);
13572 }
13573
13574 i_setModified(IsModified_MachineData);
13575 mHWData.backup();
13576
13577 bool fDelete = !aValue.length();
13578 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13579 if (it != mHWData->mGuestProperties.end())
13580 {
13581 if (!fDelete)
13582 {
13583 it->second.strValue = aValue;
13584 it->second.mTimestamp = aTimestamp;
13585 it->second.mFlags = fFlags;
13586 }
13587 else
13588 mHWData->mGuestProperties.erase(it);
13589
13590 mData->mGuestPropertiesModified = TRUE;
13591 }
13592 else if (!fDelete)
13593 {
13594 HWData::GuestProperty prop;
13595 prop.strValue = aValue;
13596 prop.mTimestamp = aTimestamp;
13597 prop.mFlags = fFlags;
13598
13599 mHWData->mGuestProperties[aName] = prop;
13600 mData->mGuestPropertiesModified = TRUE;
13601 }
13602
13603 alock.release();
13604
13605 mParent->i_onGuestPropertyChange(mData->mUuid,
13606 Bstr(aName).raw(),
13607 Bstr(aValue).raw(),
13608 Bstr(aFlags).raw());
13609 }
13610 catch (...)
13611 {
13612 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13613 }
13614 return S_OK;
13615#else
13616 ReturnComNotImplemented();
13617#endif
13618}
13619
13620
13621HRESULT SessionMachine::lockMedia()
13622{
13623 AutoMultiWriteLock2 alock(this->lockHandle(),
13624 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13625
13626 AssertReturn( mData->mMachineState == MachineState_Starting
13627 || mData->mMachineState == MachineState_Restoring
13628 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13629
13630 clearError();
13631 alock.release();
13632 return i_lockMedia();
13633}
13634
13635HRESULT SessionMachine::unlockMedia()
13636{
13637 HRESULT hrc = i_unlockMedia();
13638 return hrc;
13639}
13640
13641HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13642 ComPtr<IMediumAttachment> &aNewAttachment)
13643{
13644 // request the host lock first, since might be calling Host methods for getting host drives;
13645 // next, protect the media tree all the while we're in here, as well as our member variables
13646 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13647 this->lockHandle(),
13648 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13649
13650 IMediumAttachment *iAttach = aAttachment;
13651 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13652
13653 Utf8Str ctrlName;
13654 LONG lPort;
13655 LONG lDevice;
13656 bool fTempEject;
13657 {
13658 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13659
13660 /* Need to query the details first, as the IMediumAttachment reference
13661 * might be to the original settings, which we are going to change. */
13662 ctrlName = pAttach->i_getControllerName();
13663 lPort = pAttach->i_getPort();
13664 lDevice = pAttach->i_getDevice();
13665 fTempEject = pAttach->i_getTempEject();
13666 }
13667
13668 if (!fTempEject)
13669 {
13670 /* Remember previously mounted medium. The medium before taking the
13671 * backup is not necessarily the same thing. */
13672 ComObjPtr<Medium> oldmedium;
13673 oldmedium = pAttach->i_getMedium();
13674
13675 i_setModified(IsModified_Storage);
13676 mMediumAttachments.backup();
13677
13678 // The backup operation makes the pAttach reference point to the
13679 // old settings. Re-get the correct reference.
13680 pAttach = i_findAttachment(*mMediumAttachments.data(),
13681 ctrlName,
13682 lPort,
13683 lDevice);
13684
13685 {
13686 AutoCaller autoAttachCaller(this);
13687 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13688
13689 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13690 if (!oldmedium.isNull())
13691 oldmedium->i_removeBackReference(mData->mUuid);
13692
13693 pAttach->i_updateMedium(NULL);
13694 pAttach->i_updateEjected();
13695 }
13696
13697 i_setModified(IsModified_Storage);
13698 }
13699 else
13700 {
13701 {
13702 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13703 pAttach->i_updateEjected();
13704 }
13705 }
13706
13707 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13708
13709 return S_OK;
13710}
13711
13712HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13713 com::Utf8Str &aResult)
13714{
13715 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13716
13717 HRESULT hr = S_OK;
13718
13719 if (!mAuthLibCtx.hAuthLibrary)
13720 {
13721 /* Load the external authentication library. */
13722 Bstr authLibrary;
13723 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13724
13725 Utf8Str filename = authLibrary;
13726
13727 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13728 if (RT_FAILURE(vrc))
13729 hr = setErrorBoth(E_FAIL, vrc,
13730 tr("Could not load the external authentication library '%s' (%Rrc)"),
13731 filename.c_str(), vrc);
13732 }
13733
13734 /* The auth library might need the machine lock. */
13735 alock.release();
13736
13737 if (FAILED(hr))
13738 return hr;
13739
13740 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13741 {
13742 enum VRDEAuthParams
13743 {
13744 parmUuid = 1,
13745 parmGuestJudgement,
13746 parmUser,
13747 parmPassword,
13748 parmDomain,
13749 parmClientId
13750 };
13751
13752 AuthResult result = AuthResultAccessDenied;
13753
13754 Guid uuid(aAuthParams[parmUuid]);
13755 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13756 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13757
13758 result = AuthLibAuthenticate(&mAuthLibCtx,
13759 uuid.raw(), guestJudgement,
13760 aAuthParams[parmUser].c_str(),
13761 aAuthParams[parmPassword].c_str(),
13762 aAuthParams[parmDomain].c_str(),
13763 u32ClientId);
13764
13765 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13766 size_t cbPassword = aAuthParams[parmPassword].length();
13767 if (cbPassword)
13768 {
13769 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13770 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13771 }
13772
13773 if (result == AuthResultAccessGranted)
13774 aResult = "granted";
13775 else
13776 aResult = "denied";
13777
13778 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13779 aAuthParams[parmUser].c_str(), aResult.c_str()));
13780 }
13781 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13782 {
13783 enum VRDEAuthDisconnectParams
13784 {
13785 parmUuid = 1,
13786 parmClientId
13787 };
13788
13789 Guid uuid(aAuthParams[parmUuid]);
13790 uint32_t u32ClientId = 0;
13791 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13792 }
13793 else
13794 {
13795 hr = E_INVALIDARG;
13796 }
13797
13798 return hr;
13799}
13800
13801// public methods only for internal purposes
13802/////////////////////////////////////////////////////////////////////////////
13803
13804#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13805/**
13806 * Called from the client watcher thread to check for expected or unexpected
13807 * death of the client process that has a direct session to this machine.
13808 *
13809 * On Win32 and on OS/2, this method is called only when we've got the
13810 * mutex (i.e. the client has either died or terminated normally) so it always
13811 * returns @c true (the client is terminated, the session machine is
13812 * uninitialized).
13813 *
13814 * On other platforms, the method returns @c true if the client process has
13815 * terminated normally or abnormally and the session machine was uninitialized,
13816 * and @c false if the client process is still alive.
13817 *
13818 * @note Locks this object for writing.
13819 */
13820bool SessionMachine::i_checkForDeath()
13821{
13822 Uninit::Reason reason;
13823 bool terminated = false;
13824
13825 /* Enclose autoCaller with a block because calling uninit() from under it
13826 * will deadlock. */
13827 {
13828 AutoCaller autoCaller(this);
13829 if (!autoCaller.isOk())
13830 {
13831 /* return true if not ready, to cause the client watcher to exclude
13832 * the corresponding session from watching */
13833 LogFlowThisFunc(("Already uninitialized!\n"));
13834 return true;
13835 }
13836
13837 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13838
13839 /* Determine the reason of death: if the session state is Closing here,
13840 * everything is fine. Otherwise it means that the client did not call
13841 * OnSessionEnd() before it released the IPC semaphore. This may happen
13842 * either because the client process has abnormally terminated, or
13843 * because it simply forgot to call ISession::Close() before exiting. We
13844 * threat the latter also as an abnormal termination (see
13845 * Session::uninit() for details). */
13846 reason = mData->mSession.mState == SessionState_Unlocking ?
13847 Uninit::Normal :
13848 Uninit::Abnormal;
13849
13850 if (mClientToken)
13851 terminated = mClientToken->release();
13852 } /* AutoCaller block */
13853
13854 if (terminated)
13855 uninit(reason);
13856
13857 return terminated;
13858}
13859
13860void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13861{
13862 LogFlowThisFunc(("\n"));
13863
13864 strTokenId.setNull();
13865
13866 AutoCaller autoCaller(this);
13867 AssertComRCReturnVoid(autoCaller.rc());
13868
13869 Assert(mClientToken);
13870 if (mClientToken)
13871 mClientToken->getId(strTokenId);
13872}
13873#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13874IToken *SessionMachine::i_getToken()
13875{
13876 LogFlowThisFunc(("\n"));
13877
13878 AutoCaller autoCaller(this);
13879 AssertComRCReturn(autoCaller.rc(), NULL);
13880
13881 Assert(mClientToken);
13882 if (mClientToken)
13883 return mClientToken->getToken();
13884 else
13885 return NULL;
13886}
13887#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13888
13889Machine::ClientToken *SessionMachine::i_getClientToken()
13890{
13891 LogFlowThisFunc(("\n"));
13892
13893 AutoCaller autoCaller(this);
13894 AssertComRCReturn(autoCaller.rc(), NULL);
13895
13896 return mClientToken;
13897}
13898
13899
13900/**
13901 * @note Locks this object for reading.
13902 */
13903HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13904{
13905 LogFlowThisFunc(("\n"));
13906
13907 AutoCaller autoCaller(this);
13908 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13909
13910 ComPtr<IInternalSessionControl> directControl;
13911 {
13912 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13913 if (mData->mSession.mLockType == LockType_VM)
13914 directControl = mData->mSession.mDirectControl;
13915 }
13916
13917 /* ignore notifications sent after #OnSessionEnd() is called */
13918 if (!directControl)
13919 return S_OK;
13920
13921 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13922}
13923
13924/**
13925 * @note Locks this object for reading.
13926 */
13927HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13928 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13929 IN_BSTR aGuestIp, LONG aGuestPort)
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 if (mData->mSession.mLockType == LockType_VM)
13940 directControl = mData->mSession.mDirectControl;
13941 }
13942
13943 /* ignore notifications sent after #OnSessionEnd() is called */
13944 if (!directControl)
13945 return S_OK;
13946 /*
13947 * instead acting like callback we ask IVirtualBox deliver corresponding event
13948 */
13949
13950 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13951 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13952 return S_OK;
13953}
13954
13955/**
13956 * @note Locks this object for reading.
13957 */
13958HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13959{
13960 LogFlowThisFunc(("\n"));
13961
13962 AutoCaller autoCaller(this);
13963 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13964
13965 ComPtr<IInternalSessionControl> directControl;
13966 {
13967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13968 if (mData->mSession.mLockType == LockType_VM)
13969 directControl = mData->mSession.mDirectControl;
13970 }
13971
13972 /* ignore notifications sent after #OnSessionEnd() is called */
13973 if (!directControl)
13974 return S_OK;
13975
13976 return directControl->OnAudioAdapterChange(audioAdapter);
13977}
13978
13979/**
13980 * @note Locks this object for reading.
13981 */
13982HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13983{
13984 LogFlowThisFunc(("\n"));
13985
13986 AutoCaller autoCaller(this);
13987 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13988
13989 ComPtr<IInternalSessionControl> directControl;
13990 {
13991 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13992 if (mData->mSession.mLockType == LockType_VM)
13993 directControl = mData->mSession.mDirectControl;
13994 }
13995
13996 /* ignore notifications sent after #OnSessionEnd() is called */
13997 if (!directControl)
13998 return S_OK;
13999
14000 return directControl->OnSerialPortChange(serialPort);
14001}
14002
14003/**
14004 * @note Locks this object for reading.
14005 */
14006HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
14007{
14008 LogFlowThisFunc(("\n"));
14009
14010 AutoCaller autoCaller(this);
14011 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14012
14013 ComPtr<IInternalSessionControl> directControl;
14014 {
14015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14016 if (mData->mSession.mLockType == LockType_VM)
14017 directControl = mData->mSession.mDirectControl;
14018 }
14019
14020 /* ignore notifications sent after #OnSessionEnd() is called */
14021 if (!directControl)
14022 return S_OK;
14023
14024 return directControl->OnParallelPortChange(parallelPort);
14025}
14026
14027/**
14028 * @note Locks this object for reading.
14029 */
14030HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14031{
14032 LogFlowThisFunc(("\n"));
14033
14034 AutoCaller autoCaller(this);
14035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14036
14037 ComPtr<IInternalSessionControl> directControl;
14038 {
14039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14040 if (mData->mSession.mLockType == LockType_VM)
14041 directControl = mData->mSession.mDirectControl;
14042 }
14043
14044 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14045
14046 /* ignore notifications sent after #OnSessionEnd() is called */
14047 if (!directControl)
14048 return S_OK;
14049
14050 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14051}
14052
14053/**
14054 * @note Locks this object for reading.
14055 */
14056HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14057{
14058 LogFlowThisFunc(("\n"));
14059
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14062
14063 ComPtr<IInternalSessionControl> directControl;
14064 {
14065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14066 if (mData->mSession.mLockType == LockType_VM)
14067 directControl = mData->mSession.mDirectControl;
14068 }
14069
14070 mParent->i_onMediumChanged(aAttachment);
14071
14072 /* ignore notifications sent after #OnSessionEnd() is called */
14073 if (!directControl)
14074 return S_OK;
14075
14076 return directControl->OnMediumChange(aAttachment, aForce);
14077}
14078
14079HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14080{
14081 LogFlowThisFunc(("\n"));
14082
14083 AutoCaller autoCaller(this);
14084 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14085
14086 ComPtr<IInternalSessionControl> directControl;
14087 {
14088 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14089 if (mData->mSession.mLockType == LockType_VM)
14090 directControl = mData->mSession.mDirectControl;
14091 }
14092
14093 /* ignore notifications sent after #OnSessionEnd() is called */
14094 if (!directControl)
14095 return S_OK;
14096
14097 return directControl->OnVMProcessPriorityChange(aPriority);
14098}
14099
14100/**
14101 * @note Locks this object for reading.
14102 */
14103HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14104{
14105 LogFlowThisFunc(("\n"));
14106
14107 AutoCaller autoCaller(this);
14108 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14109
14110 ComPtr<IInternalSessionControl> directControl;
14111 {
14112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14113 if (mData->mSession.mLockType == LockType_VM)
14114 directControl = mData->mSession.mDirectControl;
14115 }
14116
14117 /* ignore notifications sent after #OnSessionEnd() is called */
14118 if (!directControl)
14119 return S_OK;
14120
14121 return directControl->OnCPUChange(aCPU, aRemove);
14122}
14123
14124HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14125{
14126 LogFlowThisFunc(("\n"));
14127
14128 AutoCaller autoCaller(this);
14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14130
14131 ComPtr<IInternalSessionControl> directControl;
14132 {
14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14134 if (mData->mSession.mLockType == LockType_VM)
14135 directControl = mData->mSession.mDirectControl;
14136 }
14137
14138 /* ignore notifications sent after #OnSessionEnd() is called */
14139 if (!directControl)
14140 return S_OK;
14141
14142 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14143}
14144
14145/**
14146 * @note Locks this object for reading.
14147 */
14148HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14154
14155 ComPtr<IInternalSessionControl> directControl;
14156 {
14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14158 if (mData->mSession.mLockType == LockType_VM)
14159 directControl = mData->mSession.mDirectControl;
14160 }
14161
14162 /* ignore notifications sent after #OnSessionEnd() is called */
14163 if (!directControl)
14164 return S_OK;
14165
14166 return directControl->OnVRDEServerChange(aRestart);
14167}
14168
14169/**
14170 * @note Locks this object for reading.
14171 */
14172HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14173{
14174 LogFlowThisFunc(("\n"));
14175
14176 AutoCaller autoCaller(this);
14177 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14178
14179 ComPtr<IInternalSessionControl> directControl;
14180 {
14181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14182 if (mData->mSession.mLockType == LockType_VM)
14183 directControl = mData->mSession.mDirectControl;
14184 }
14185
14186 /* ignore notifications sent after #OnSessionEnd() is called */
14187 if (!directControl)
14188 return S_OK;
14189
14190 return directControl->OnRecordingChange(aEnable);
14191}
14192
14193/**
14194 * @note Locks this object for reading.
14195 */
14196HRESULT SessionMachine::i_onUSBControllerChange()
14197{
14198 LogFlowThisFunc(("\n"));
14199
14200 AutoCaller autoCaller(this);
14201 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14202
14203 ComPtr<IInternalSessionControl> directControl;
14204 {
14205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14206 if (mData->mSession.mLockType == LockType_VM)
14207 directControl = mData->mSession.mDirectControl;
14208 }
14209
14210 /* ignore notifications sent after #OnSessionEnd() is called */
14211 if (!directControl)
14212 return S_OK;
14213
14214 return directControl->OnUSBControllerChange();
14215}
14216
14217/**
14218 * @note Locks this object for reading.
14219 */
14220HRESULT SessionMachine::i_onSharedFolderChange()
14221{
14222 LogFlowThisFunc(("\n"));
14223
14224 AutoCaller autoCaller(this);
14225 AssertComRCReturnRC(autoCaller.rc());
14226
14227 ComPtr<IInternalSessionControl> directControl;
14228 {
14229 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14230 if (mData->mSession.mLockType == LockType_VM)
14231 directControl = mData->mSession.mDirectControl;
14232 }
14233
14234 /* ignore notifications sent after #OnSessionEnd() is called */
14235 if (!directControl)
14236 return S_OK;
14237
14238 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturnRC(autoCaller.rc());
14250
14251 ComPtr<IInternalSessionControl> directControl;
14252 {
14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14254 if (mData->mSession.mLockType == LockType_VM)
14255 directControl = mData->mSession.mDirectControl;
14256 }
14257
14258 /* ignore notifications sent after #OnSessionEnd() is called */
14259 if (!directControl)
14260 return S_OK;
14261
14262 return directControl->OnClipboardModeChange(aClipboardMode);
14263}
14264
14265/**
14266 * @note Locks this object for reading.
14267 */
14268HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14269{
14270 LogFlowThisFunc(("\n"));
14271
14272 AutoCaller autoCaller(this);
14273 AssertComRCReturnRC(autoCaller.rc());
14274
14275 ComPtr<IInternalSessionControl> directControl;
14276 {
14277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14278 if (mData->mSession.mLockType == LockType_VM)
14279 directControl = mData->mSession.mDirectControl;
14280 }
14281
14282 /* ignore notifications sent after #OnSessionEnd() is called */
14283 if (!directControl)
14284 return S_OK;
14285
14286 return directControl->OnDnDModeChange(aDnDMode);
14287}
14288
14289/**
14290 * @note Locks this object for reading.
14291 */
14292HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14293{
14294 LogFlowThisFunc(("\n"));
14295
14296 AutoCaller autoCaller(this);
14297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14298
14299 ComPtr<IInternalSessionControl> directControl;
14300 {
14301 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14302 if (mData->mSession.mLockType == LockType_VM)
14303 directControl = mData->mSession.mDirectControl;
14304 }
14305
14306 /* ignore notifications sent after #OnSessionEnd() is called */
14307 if (!directControl)
14308 return S_OK;
14309
14310 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14311}
14312
14313/**
14314 * @note Locks this object for reading.
14315 */
14316HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14317{
14318 LogFlowThisFunc(("\n"));
14319
14320 AutoCaller autoCaller(this);
14321 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14322
14323 ComPtr<IInternalSessionControl> directControl;
14324 {
14325 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14326 if (mData->mSession.mLockType == LockType_VM)
14327 directControl = mData->mSession.mDirectControl;
14328 }
14329
14330 /* ignore notifications sent after #OnSessionEnd() is called */
14331 if (!directControl)
14332 return S_OK;
14333
14334 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14335}
14336
14337/**
14338 * Returns @c true if this machine's USB controller reports it has a matching
14339 * filter for the given USB device and @c false otherwise.
14340 *
14341 * @note locks this object for reading.
14342 */
14343bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14344{
14345 AutoCaller autoCaller(this);
14346 /* silently return if not ready -- this method may be called after the
14347 * direct machine session has been called */
14348 if (!autoCaller.isOk())
14349 return false;
14350
14351#ifdef VBOX_WITH_USB
14352 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14353
14354 switch (mData->mMachineState)
14355 {
14356 case MachineState_Starting:
14357 case MachineState_Restoring:
14358 case MachineState_TeleportingIn:
14359 case MachineState_Paused:
14360 case MachineState_Running:
14361 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14362 * elsewhere... */
14363 alock.release();
14364 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14365 default: break;
14366 }
14367#else
14368 NOREF(aDevice);
14369 NOREF(aMaskedIfs);
14370#endif
14371 return false;
14372}
14373
14374/**
14375 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14376 */
14377HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14378 IVirtualBoxErrorInfo *aError,
14379 ULONG aMaskedIfs,
14380 const com::Utf8Str &aCaptureFilename)
14381{
14382 LogFlowThisFunc(("\n"));
14383
14384 AutoCaller autoCaller(this);
14385
14386 /* This notification may happen after the machine object has been
14387 * uninitialized (the session was closed), so don't assert. */
14388 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14389
14390 ComPtr<IInternalSessionControl> directControl;
14391 {
14392 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14393 if (mData->mSession.mLockType == LockType_VM)
14394 directControl = mData->mSession.mDirectControl;
14395 }
14396
14397 /* fail on notifications sent after #OnSessionEnd() is called, it is
14398 * expected by the caller */
14399 if (!directControl)
14400 return E_FAIL;
14401
14402 /* No locks should be held at this point. */
14403 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14404 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14405
14406 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14407}
14408
14409/**
14410 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14411 */
14412HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14413 IVirtualBoxErrorInfo *aError)
14414{
14415 LogFlowThisFunc(("\n"));
14416
14417 AutoCaller autoCaller(this);
14418
14419 /* This notification may happen after the machine object has been
14420 * uninitialized (the session was closed), so don't assert. */
14421 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14422
14423 ComPtr<IInternalSessionControl> directControl;
14424 {
14425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14426 if (mData->mSession.mLockType == LockType_VM)
14427 directControl = mData->mSession.mDirectControl;
14428 }
14429
14430 /* fail on notifications sent after #OnSessionEnd() is called, it is
14431 * expected by the caller */
14432 if (!directControl)
14433 return E_FAIL;
14434
14435 /* No locks should be held at this point. */
14436 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14437 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14438
14439 return directControl->OnUSBDeviceDetach(aId, aError);
14440}
14441
14442// protected methods
14443/////////////////////////////////////////////////////////////////////////////
14444
14445/**
14446 * Deletes the given file if it is no longer in use by either the current machine state
14447 * (if the machine is "saved") or any of the machine's snapshots.
14448 *
14449 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14450 * but is different for each SnapshotMachine. When calling this, the order of calling this
14451 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14452 * is therefore critical. I know, it's all rather messy.
14453 *
14454 * @param strStateFile
14455 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14456 * the test for whether the saved state file is in use.
14457 */
14458void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14459 Snapshot *pSnapshotToIgnore)
14460{
14461 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14462 if ( (strStateFile.isNotEmpty())
14463 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14464 )
14465 // ... and it must also not be shared with other snapshots
14466 if ( !mData->mFirstSnapshot
14467 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14468 // this checks the SnapshotMachine's state file paths
14469 )
14470 RTFileDelete(strStateFile.c_str());
14471}
14472
14473/**
14474 * Locks the attached media.
14475 *
14476 * All attached hard disks are locked for writing and DVD/floppy are locked for
14477 * reading. Parents of attached hard disks (if any) are locked for reading.
14478 *
14479 * This method also performs accessibility check of all media it locks: if some
14480 * media is inaccessible, the method will return a failure and a bunch of
14481 * extended error info objects per each inaccessible medium.
14482 *
14483 * Note that this method is atomic: if it returns a success, all media are
14484 * locked as described above; on failure no media is locked at all (all
14485 * succeeded individual locks will be undone).
14486 *
14487 * The caller is responsible for doing the necessary state sanity checks.
14488 *
14489 * The locks made by this method must be undone by calling #unlockMedia() when
14490 * no more needed.
14491 */
14492HRESULT SessionMachine::i_lockMedia()
14493{
14494 AutoCaller autoCaller(this);
14495 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14496
14497 AutoMultiWriteLock2 alock(this->lockHandle(),
14498 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14499
14500 /* bail out if trying to lock things with already set up locking */
14501 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14502
14503 MultiResult mrc(S_OK);
14504
14505 /* Collect locking information for all medium objects attached to the VM. */
14506 for (MediumAttachmentList::const_iterator
14507 it = mMediumAttachments->begin();
14508 it != mMediumAttachments->end();
14509 ++it)
14510 {
14511 MediumAttachment *pAtt = *it;
14512 DeviceType_T devType = pAtt->i_getType();
14513 Medium *pMedium = pAtt->i_getMedium();
14514
14515 MediumLockList *pMediumLockList(new MediumLockList());
14516 // There can be attachments without a medium (floppy/dvd), and thus
14517 // it's impossible to create a medium lock list. It still makes sense
14518 // to have the empty medium lock list in the map in case a medium is
14519 // attached later.
14520 if (pMedium != NULL)
14521 {
14522 MediumType_T mediumType = pMedium->i_getType();
14523 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14524 || mediumType == MediumType_Shareable;
14525 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14526
14527 alock.release();
14528 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14529 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14530 false /* fMediumLockWriteAll */,
14531 NULL,
14532 *pMediumLockList);
14533 alock.acquire();
14534 if (FAILED(mrc))
14535 {
14536 delete pMediumLockList;
14537 mData->mSession.mLockedMedia.Clear();
14538 break;
14539 }
14540 }
14541
14542 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14543 if (FAILED(rc))
14544 {
14545 mData->mSession.mLockedMedia.Clear();
14546 mrc = setError(rc,
14547 tr("Collecting locking information for all attached media failed"));
14548 break;
14549 }
14550 }
14551
14552 if (SUCCEEDED(mrc))
14553 {
14554 /* Now lock all media. If this fails, nothing is locked. */
14555 alock.release();
14556 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14557 alock.acquire();
14558 if (FAILED(rc))
14559 {
14560 mrc = setError(rc,
14561 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14562 }
14563 }
14564
14565 return mrc;
14566}
14567
14568/**
14569 * Undoes the locks made by by #lockMedia().
14570 */
14571HRESULT SessionMachine::i_unlockMedia()
14572{
14573 AutoCaller autoCaller(this);
14574 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14575
14576 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14577
14578 /* we may be holding important error info on the current thread;
14579 * preserve it */
14580 ErrorInfoKeeper eik;
14581
14582 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14583 AssertComRC(rc);
14584 return rc;
14585}
14586
14587/**
14588 * Helper to change the machine state (reimplementation).
14589 *
14590 * @note Locks this object for writing.
14591 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14592 * it can cause crashes in random places due to unexpectedly committing
14593 * the current settings. The caller is responsible for that. The call
14594 * to saveStateSettings is fine, because this method does not commit.
14595 */
14596HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14597{
14598 LogFlowThisFuncEnter();
14599 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14600
14601 AutoCaller autoCaller(this);
14602 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14603
14604 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14605
14606 MachineState_T oldMachineState = mData->mMachineState;
14607
14608 AssertMsgReturn(oldMachineState != aMachineState,
14609 ("oldMachineState=%s, aMachineState=%s\n",
14610 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14611 E_FAIL);
14612
14613 HRESULT rc = S_OK;
14614
14615 int stsFlags = 0;
14616 bool deleteSavedState = false;
14617
14618 /* detect some state transitions */
14619
14620 if ( ( oldMachineState == MachineState_Saved
14621 && aMachineState == MachineState_Restoring)
14622 || ( ( oldMachineState == MachineState_PoweredOff
14623 || oldMachineState == MachineState_Teleported
14624 || oldMachineState == MachineState_Aborted
14625 )
14626 && ( aMachineState == MachineState_TeleportingIn
14627 || aMachineState == MachineState_Starting
14628 )
14629 )
14630 )
14631 {
14632 /* The EMT thread is about to start */
14633
14634 /* Nothing to do here for now... */
14635
14636 /// @todo NEWMEDIA don't let mDVDDrive and other children
14637 /// change anything when in the Starting/Restoring state
14638 }
14639 else if ( ( oldMachineState == MachineState_Running
14640 || oldMachineState == MachineState_Paused
14641 || oldMachineState == MachineState_Teleporting
14642 || oldMachineState == MachineState_OnlineSnapshotting
14643 || oldMachineState == MachineState_LiveSnapshotting
14644 || oldMachineState == MachineState_Stuck
14645 || oldMachineState == MachineState_Starting
14646 || oldMachineState == MachineState_Stopping
14647 || oldMachineState == MachineState_Saving
14648 || oldMachineState == MachineState_Restoring
14649 || oldMachineState == MachineState_TeleportingPausedVM
14650 || oldMachineState == MachineState_TeleportingIn
14651 )
14652 && ( aMachineState == MachineState_PoweredOff
14653 || aMachineState == MachineState_Saved
14654 || aMachineState == MachineState_Teleported
14655 || aMachineState == MachineState_Aborted
14656 )
14657 )
14658 {
14659 /* The EMT thread has just stopped, unlock attached media. Note that as
14660 * opposed to locking that is done from Console, we do unlocking here
14661 * because the VM process may have aborted before having a chance to
14662 * properly unlock all media it locked. */
14663
14664 unlockMedia();
14665 }
14666
14667 if (oldMachineState == MachineState_Restoring)
14668 {
14669 if (aMachineState != MachineState_Saved)
14670 {
14671 /*
14672 * delete the saved state file once the machine has finished
14673 * restoring from it (note that Console sets the state from
14674 * Restoring to Saved if the VM couldn't restore successfully,
14675 * to give the user an ability to fix an error and retry --
14676 * we keep the saved state file in this case)
14677 */
14678 deleteSavedState = true;
14679 }
14680 }
14681 else if ( oldMachineState == MachineState_Saved
14682 && ( aMachineState == MachineState_PoweredOff
14683 || aMachineState == MachineState_Aborted
14684 || aMachineState == MachineState_Teleported
14685 )
14686 )
14687 {
14688 /*
14689 * delete the saved state after SessionMachine::ForgetSavedState() is called
14690 * or if the VM process (owning a direct VM session) crashed while the
14691 * VM was Saved
14692 */
14693
14694 /// @todo (dmik)
14695 // Not sure that deleting the saved state file just because of the
14696 // client death before it attempted to restore the VM is a good
14697 // thing. But when it crashes we need to go to the Aborted state
14698 // which cannot have the saved state file associated... The only
14699 // way to fix this is to make the Aborted condition not a VM state
14700 // but a bool flag: i.e., when a crash occurs, set it to true and
14701 // change the state to PoweredOff or Saved depending on the
14702 // saved state presence.
14703
14704 deleteSavedState = true;
14705 mData->mCurrentStateModified = TRUE;
14706 stsFlags |= SaveSTS_CurStateModified;
14707 }
14708
14709 if ( aMachineState == MachineState_Starting
14710 || aMachineState == MachineState_Restoring
14711 || aMachineState == MachineState_TeleportingIn
14712 )
14713 {
14714 /* set the current state modified flag to indicate that the current
14715 * state is no more identical to the state in the
14716 * current snapshot */
14717 if (!mData->mCurrentSnapshot.isNull())
14718 {
14719 mData->mCurrentStateModified = TRUE;
14720 stsFlags |= SaveSTS_CurStateModified;
14721 }
14722 }
14723
14724 if (deleteSavedState)
14725 {
14726 if (mRemoveSavedState)
14727 {
14728 Assert(!mSSData->strStateFilePath.isEmpty());
14729
14730 // it is safe to delete the saved state file if ...
14731 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14732 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14733 // ... none of the snapshots share the saved state file
14734 )
14735 RTFileDelete(mSSData->strStateFilePath.c_str());
14736 }
14737
14738 mSSData->strStateFilePath.setNull();
14739 stsFlags |= SaveSTS_StateFilePath;
14740 }
14741
14742 /* redirect to the underlying peer machine */
14743 mPeer->i_setMachineState(aMachineState);
14744
14745 if ( oldMachineState != MachineState_RestoringSnapshot
14746 && ( aMachineState == MachineState_PoweredOff
14747 || aMachineState == MachineState_Teleported
14748 || aMachineState == MachineState_Aborted
14749 || aMachineState == MachineState_Saved))
14750 {
14751 /* the machine has stopped execution
14752 * (or the saved state file was adopted) */
14753 stsFlags |= SaveSTS_StateTimeStamp;
14754 }
14755
14756 if ( ( oldMachineState == MachineState_PoweredOff
14757 || oldMachineState == MachineState_Aborted
14758 || oldMachineState == MachineState_Teleported
14759 )
14760 && aMachineState == MachineState_Saved)
14761 {
14762 /* the saved state file was adopted */
14763 Assert(!mSSData->strStateFilePath.isEmpty());
14764 stsFlags |= SaveSTS_StateFilePath;
14765 }
14766
14767#ifdef VBOX_WITH_GUEST_PROPS
14768 if ( aMachineState == MachineState_PoweredOff
14769 || aMachineState == MachineState_Aborted
14770 || aMachineState == MachineState_Teleported)
14771 {
14772 /* Make sure any transient guest properties get removed from the
14773 * property store on shutdown. */
14774 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14775
14776 /* remove it from the settings representation */
14777 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14778 for (settings::GuestPropertiesList::iterator
14779 it = llGuestProperties.begin();
14780 it != llGuestProperties.end();
14781 /*nothing*/)
14782 {
14783 const settings::GuestProperty &prop = *it;
14784 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14785 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14786 {
14787 it = llGuestProperties.erase(it);
14788 fNeedsSaving = true;
14789 }
14790 else
14791 {
14792 ++it;
14793 }
14794 }
14795
14796 /* Additionally remove it from the HWData representation. Required to
14797 * keep everything in sync, as this is what the API keeps using. */
14798 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14799 for (HWData::GuestPropertyMap::iterator
14800 it = llHWGuestProperties.begin();
14801 it != llHWGuestProperties.end();
14802 /*nothing*/)
14803 {
14804 uint32_t fFlags = it->second.mFlags;
14805 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14806 {
14807 /* iterator where we need to continue after the erase call
14808 * (C++03 is a fact still, and it doesn't return the iterator
14809 * which would allow continuing) */
14810 HWData::GuestPropertyMap::iterator it2 = it;
14811 ++it2;
14812 llHWGuestProperties.erase(it);
14813 it = it2;
14814 fNeedsSaving = true;
14815 }
14816 else
14817 {
14818 ++it;
14819 }
14820 }
14821
14822 if (fNeedsSaving)
14823 {
14824 mData->mCurrentStateModified = TRUE;
14825 stsFlags |= SaveSTS_CurStateModified;
14826 }
14827 }
14828#endif /* VBOX_WITH_GUEST_PROPS */
14829
14830 rc = i_saveStateSettings(stsFlags);
14831
14832 if ( ( oldMachineState != MachineState_PoweredOff
14833 && oldMachineState != MachineState_Aborted
14834 && oldMachineState != MachineState_Teleported
14835 )
14836 && ( aMachineState == MachineState_PoweredOff
14837 || aMachineState == MachineState_Aborted
14838 || aMachineState == MachineState_Teleported
14839 )
14840 )
14841 {
14842 /* we've been shut down for any reason */
14843 /* no special action so far */
14844 }
14845
14846 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14847 LogFlowThisFuncLeave();
14848 return rc;
14849}
14850
14851/**
14852 * Sends the current machine state value to the VM process.
14853 *
14854 * @note Locks this object for reading, then calls a client process.
14855 */
14856HRESULT SessionMachine::i_updateMachineStateOnClient()
14857{
14858 AutoCaller autoCaller(this);
14859 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14860
14861 ComPtr<IInternalSessionControl> directControl;
14862 {
14863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14864 AssertReturn(!!mData, E_FAIL);
14865 if (mData->mSession.mLockType == LockType_VM)
14866 directControl = mData->mSession.mDirectControl;
14867
14868 /* directControl may be already set to NULL here in #OnSessionEnd()
14869 * called too early by the direct session process while there is still
14870 * some operation (like deleting the snapshot) in progress. The client
14871 * process in this case is waiting inside Session::close() for the
14872 * "end session" process object to complete, while #uninit() called by
14873 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14874 * operation to complete. For now, we accept this inconsistent behavior
14875 * and simply do nothing here. */
14876
14877 if (mData->mSession.mState == SessionState_Unlocking)
14878 return S_OK;
14879 }
14880
14881 /* ignore notifications sent after #OnSessionEnd() is called */
14882 if (!directControl)
14883 return S_OK;
14884
14885 return directControl->UpdateMachineState(mData->mMachineState);
14886}
14887
14888
14889/*static*/
14890HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14891{
14892 va_list args;
14893 va_start(args, pcszMsg);
14894 HRESULT rc = setErrorInternal(aResultCode,
14895 getStaticClassIID(),
14896 getStaticComponentName(),
14897 Utf8Str(pcszMsg, args),
14898 false /* aWarning */,
14899 true /* aLogIt */);
14900 va_end(args);
14901 return rc;
14902}
14903
14904
14905HRESULT Machine::updateState(MachineState_T aState)
14906{
14907 NOREF(aState);
14908 ReturnComNotImplemented();
14909}
14910
14911HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14912{
14913 NOREF(aProgress);
14914 ReturnComNotImplemented();
14915}
14916
14917HRESULT Machine::endPowerUp(LONG aResult)
14918{
14919 NOREF(aResult);
14920 ReturnComNotImplemented();
14921}
14922
14923HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14924{
14925 NOREF(aProgress);
14926 ReturnComNotImplemented();
14927}
14928
14929HRESULT Machine::endPoweringDown(LONG aResult,
14930 const com::Utf8Str &aErrMsg)
14931{
14932 NOREF(aResult);
14933 NOREF(aErrMsg);
14934 ReturnComNotImplemented();
14935}
14936
14937HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14938 BOOL *aMatched,
14939 ULONG *aMaskedInterfaces)
14940{
14941 NOREF(aDevice);
14942 NOREF(aMatched);
14943 NOREF(aMaskedInterfaces);
14944 ReturnComNotImplemented();
14945
14946}
14947
14948HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14949{
14950 NOREF(aId); NOREF(aCaptureFilename);
14951 ReturnComNotImplemented();
14952}
14953
14954HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14955 BOOL aDone)
14956{
14957 NOREF(aId);
14958 NOREF(aDone);
14959 ReturnComNotImplemented();
14960}
14961
14962HRESULT Machine::autoCaptureUSBDevices()
14963{
14964 ReturnComNotImplemented();
14965}
14966
14967HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14968{
14969 NOREF(aDone);
14970 ReturnComNotImplemented();
14971}
14972
14973HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14974 ComPtr<IProgress> &aProgress)
14975{
14976 NOREF(aSession);
14977 NOREF(aProgress);
14978 ReturnComNotImplemented();
14979}
14980
14981HRESULT Machine::finishOnlineMergeMedium()
14982{
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14987{
14988 RT_NOREF(aParms, aID);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14993{
14994 RT_NOREF(aID);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::clipboardAreaAttach(ULONG aID)
14999{
15000 RT_NOREF(aID);
15001 ReturnComNotImplemented();
15002}
15003HRESULT Machine::clipboardAreaDetach(ULONG aID)
15004{
15005 RT_NOREF(aID);
15006 ReturnComNotImplemented();
15007}
15008
15009HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
15010{
15011 RT_NOREF(aID);
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
15016{
15017 RT_NOREF(aID, aRefCount);
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
15022 std::vector<com::Utf8Str> &aValues,
15023 std::vector<LONG64> &aTimestamps,
15024 std::vector<com::Utf8Str> &aFlags)
15025{
15026 NOREF(aNames);
15027 NOREF(aValues);
15028 NOREF(aTimestamps);
15029 NOREF(aFlags);
15030 ReturnComNotImplemented();
15031}
15032
15033HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15034 const com::Utf8Str &aValue,
15035 LONG64 aTimestamp,
15036 const com::Utf8Str &aFlags)
15037{
15038 NOREF(aName);
15039 NOREF(aValue);
15040 NOREF(aTimestamp);
15041 NOREF(aFlags);
15042 ReturnComNotImplemented();
15043}
15044
15045HRESULT Machine::lockMedia()
15046{
15047 ReturnComNotImplemented();
15048}
15049
15050HRESULT Machine::unlockMedia()
15051{
15052 ReturnComNotImplemented();
15053}
15054
15055HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15056 ComPtr<IMediumAttachment> &aNewAttachment)
15057{
15058 NOREF(aAttachment);
15059 NOREF(aNewAttachment);
15060 ReturnComNotImplemented();
15061}
15062
15063HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15064 ULONG aCpuUser,
15065 ULONG aCpuKernel,
15066 ULONG aCpuIdle,
15067 ULONG aMemTotal,
15068 ULONG aMemFree,
15069 ULONG aMemBalloon,
15070 ULONG aMemShared,
15071 ULONG aMemCache,
15072 ULONG aPagedTotal,
15073 ULONG aMemAllocTotal,
15074 ULONG aMemFreeTotal,
15075 ULONG aMemBalloonTotal,
15076 ULONG aMemSharedTotal,
15077 ULONG aVmNetRx,
15078 ULONG aVmNetTx)
15079{
15080 NOREF(aValidStats);
15081 NOREF(aCpuUser);
15082 NOREF(aCpuKernel);
15083 NOREF(aCpuIdle);
15084 NOREF(aMemTotal);
15085 NOREF(aMemFree);
15086 NOREF(aMemBalloon);
15087 NOREF(aMemShared);
15088 NOREF(aMemCache);
15089 NOREF(aPagedTotal);
15090 NOREF(aMemAllocTotal);
15091 NOREF(aMemFreeTotal);
15092 NOREF(aMemBalloonTotal);
15093 NOREF(aMemSharedTotal);
15094 NOREF(aVmNetRx);
15095 NOREF(aVmNetTx);
15096 ReturnComNotImplemented();
15097}
15098
15099HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15100 com::Utf8Str &aResult)
15101{
15102 NOREF(aAuthParams);
15103 NOREF(aResult);
15104 ReturnComNotImplemented();
15105}
15106
15107com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15108{
15109 com::Utf8Str strControllerName = "Unknown";
15110 switch (aBusType)
15111 {
15112 case StorageBus_IDE:
15113 {
15114 strControllerName = "IDE";
15115 break;
15116 }
15117 case StorageBus_SATA:
15118 {
15119 strControllerName = "SATA";
15120 break;
15121 }
15122 case StorageBus_SCSI:
15123 {
15124 strControllerName = "SCSI";
15125 break;
15126 }
15127 case StorageBus_Floppy:
15128 {
15129 strControllerName = "Floppy";
15130 break;
15131 }
15132 case StorageBus_SAS:
15133 {
15134 strControllerName = "SAS";
15135 break;
15136 }
15137 case StorageBus_USB:
15138 {
15139 strControllerName = "USB";
15140 break;
15141 }
15142 default:
15143 break;
15144 }
15145 return strControllerName;
15146}
15147
15148HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15149{
15150 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15151
15152 AutoCaller autoCaller(this);
15153 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15154
15155 HRESULT rc = S_OK;
15156
15157 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15158 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15159 rc = getUSBDeviceFilters(usbDeviceFilters);
15160 if (FAILED(rc)) return rc;
15161
15162 NOREF(aFlags);
15163 com::Utf8Str osTypeId;
15164 ComObjPtr<GuestOSType> osType = NULL;
15165
15166 /* Get the guest os type as a string from the VB. */
15167 rc = getOSTypeId(osTypeId);
15168 if (FAILED(rc)) return rc;
15169
15170 /* Get the os type obj that coresponds, can be used to get
15171 * the defaults for this guest OS. */
15172 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15173 if (FAILED(rc)) return rc;
15174
15175 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15176
15177 /* Let the OS type select 64-bit ness. */
15178 mHWData->mLongMode = osType->i_is64Bit()
15179 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15180
15181 /* Let the OS type enable the X2APIC */
15182 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15183
15184 /* This one covers IOAPICEnabled. */
15185 mBIOSSettings->i_applyDefaults(osType);
15186
15187 /* Initialize default record settings. */
15188 mRecordingSettings->i_applyDefaults();
15189
15190 /* Initialize default BIOS settings here */
15191 /* Hardware virtualization must be ON by default */
15192 mHWData->mAPIC = true;
15193 mHWData->mHWVirtExEnabled = true;
15194
15195 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15196 if (FAILED(rc)) return rc;
15197
15198 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15199 if (FAILED(rc)) return rc;
15200
15201 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15202 if (FAILED(rc)) return rc;
15203
15204 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15205 if (FAILED(rc)) return rc;
15206
15207 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15208 if (FAILED(rc)) return rc;
15209
15210 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15211 if (FAILED(rc)) return rc;
15212
15213 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15214 if (FAILED(rc)) return rc;
15215
15216 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15217 if (FAILED(rc)) return rc;
15218
15219 BOOL mRTCUseUTC;
15220 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15221 if (FAILED(rc)) return rc;
15222
15223 setRTCUseUTC(mRTCUseUTC);
15224 if (FAILED(rc)) return rc;
15225
15226 /* the setter does more than just the assignment, so use it */
15227 ChipsetType_T enmChipsetType;
15228 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = COMSETTER(ChipsetType)(enmChipsetType);
15232 if (FAILED(rc)) return rc;
15233
15234 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15235 if (FAILED(rc)) return rc;
15236
15237 /* Apply network adapters defaults */
15238 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15239 mNetworkAdapters[slot]->i_applyDefaults(osType);
15240
15241 /* Apply serial port defaults */
15242 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15243 mSerialPorts[slot]->i_applyDefaults(osType);
15244
15245 /* Apply parallel port defaults - not OS dependent*/
15246 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15247 mParallelPorts[slot]->i_applyDefaults();
15248
15249 /* Audio stuff. */
15250 AudioControllerType_T audioController;
15251 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15252 if (FAILED(rc)) return rc;
15253
15254 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15255 if (FAILED(rc)) return rc;
15256
15257 AudioCodecType_T audioCodec;
15258 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15259 if (FAILED(rc)) return rc;
15260
15261 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15262 if (FAILED(rc)) return rc;
15263
15264 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15265 if (FAILED(rc)) return rc;
15266
15267 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15268 if (FAILED(rc)) return rc;
15269
15270 /* Storage Controllers */
15271 StorageControllerType_T hdStorageControllerType;
15272 StorageBus_T hdStorageBusType;
15273 StorageControllerType_T dvdStorageControllerType;
15274 StorageBus_T dvdStorageBusType;
15275 BOOL recommendedFloppy;
15276 ComPtr<IStorageController> floppyController;
15277 ComPtr<IStorageController> hdController;
15278 ComPtr<IStorageController> dvdController;
15279 Utf8Str strFloppyName, strDVDName, strHDName;
15280
15281 /* GUI auto generates controller names using bus type. Do the same*/
15282 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15283
15284 /* Floppy recommended? add one. */
15285 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15286 if (FAILED(rc)) return rc;
15287 if (recommendedFloppy)
15288 {
15289 rc = addStorageController(strFloppyName,
15290 StorageBus_Floppy,
15291 floppyController);
15292 if (FAILED(rc)) return rc;
15293 }
15294
15295 /* Setup one DVD storage controller. */
15296 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15297 if (FAILED(rc)) return rc;
15298
15299 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15300 if (FAILED(rc)) return rc;
15301
15302 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15303
15304 rc = addStorageController(strDVDName,
15305 dvdStorageBusType,
15306 dvdController);
15307 if (FAILED(rc)) return rc;
15308
15309 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15310 if (FAILED(rc)) return rc;
15311
15312 /* Setup one HDD storage controller. */
15313 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15314 if (FAILED(rc)) return rc;
15315
15316 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15317 if (FAILED(rc)) return rc;
15318
15319 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15320
15321 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15322 {
15323 rc = addStorageController(strHDName,
15324 hdStorageBusType,
15325 hdController);
15326 if (FAILED(rc)) return rc;
15327
15328 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15329 if (FAILED(rc)) return rc;
15330 }
15331 else
15332 {
15333 /* The HD controller is the same as DVD: */
15334 hdController = dvdController;
15335 }
15336
15337 /* Limit the AHCI port count if it's used because windows has trouble with
15338 * too many ports and other guest (OS X in particular) may take extra long
15339 * boot: */
15340
15341 // pParent = static_cast<Medium*>(aP)
15342 IStorageController *temp = hdController;
15343 ComObjPtr<StorageController> storageController;
15344 storageController = static_cast<StorageController *>(temp);
15345
15346 // tempHDController = aHDController;
15347 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15348 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15349 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15350 storageController->COMSETTER(PortCount)(1);
15351
15352 /* USB stuff */
15353
15354 bool ohciEnabled = false;
15355
15356 ComPtr<IUSBController> usbController;
15357 BOOL recommendedUSB3;
15358 BOOL recommendedUSB;
15359 BOOL usbProxyAvailable;
15360
15361 getUSBProxyAvailable(&usbProxyAvailable);
15362 if (FAILED(rc)) return rc;
15363
15364 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15365 if (FAILED(rc)) return rc;
15366 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15367 if (FAILED(rc)) return rc;
15368
15369 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15370 {
15371#ifdef VBOX_WITH_EXTPACK
15372 /* USB 3.0 is only available if the proper ExtPack is installed. */
15373 ExtPackManager *aManager = mParent->i_getExtPackManager();
15374 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15375 {
15376 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15377 if (FAILED(rc)) return rc;
15378
15379 /* xHci includes OHCI */
15380 ohciEnabled = true;
15381 }
15382#endif
15383 }
15384 if ( !ohciEnabled
15385 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15386 {
15387 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15388 if (FAILED(rc)) return rc;
15389 ohciEnabled = true;
15390
15391#ifdef VBOX_WITH_EXTPACK
15392 /* USB 2.0 is only available if the proper ExtPack is installed.
15393 * Note. Configuring EHCI here and providing messages about
15394 * the missing extpack isn't exactly clean, but it is a
15395 * necessary evil to patch over legacy compatability issues
15396 * introduced by the new distribution model. */
15397 ExtPackManager *manager = mParent->i_getExtPackManager();
15398 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15399 {
15400 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15401 if (FAILED(rc)) return rc;
15402 }
15403#endif
15404 }
15405
15406 /* Set recommended human interface device types: */
15407 BOOL recommendedUSBHID;
15408 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15409 if (FAILED(rc)) return rc;
15410
15411 if (recommendedUSBHID)
15412 {
15413 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15414 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15415 if (!ohciEnabled && !usbDeviceFilters.isNull())
15416 {
15417 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15418 if (FAILED(rc)) return rc;
15419 }
15420 }
15421
15422 BOOL recommendedUSBTablet;
15423 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15424 if (FAILED(rc)) return rc;
15425
15426 if (recommendedUSBTablet)
15427 {
15428 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15429 if (!ohciEnabled && !usbDeviceFilters.isNull())
15430 {
15431 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15432 if (FAILED(rc)) return rc;
15433 }
15434 }
15435 return S_OK;
15436}
15437
15438/* This isn't handled entirely by the wrapper generator yet. */
15439#ifdef VBOX_WITH_XPCOM
15440NS_DECL_CLASSINFO(SessionMachine)
15441NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15442
15443NS_DECL_CLASSINFO(SnapshotMachine)
15444NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15445#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