VirtualBox

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

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

Main: Try harder using the Utf8Str versions of the event stuff where possible. bugref:9790

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.4 KB
Line 
1/* $Id: MachineImpl.cpp 85309 2020-07-13 12:56:56Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#ifdef VBOX_WITH_SHARED_CLIPBOARD
91# include <VBox/HostServices/VBoxClipboardSvc.h>
92#endif
93
94#include "VBox/com/MultiResult.h"
95
96#include <algorithm>
97
98#ifdef VBOX_WITH_DTRACE_R3_MAIN
99# include "dtrace/VBoxAPI.h"
100#endif
101
102#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
103# define HOSTSUFF_EXE ".exe"
104#else /* !RT_OS_WINDOWS */
105# define HOSTSUFF_EXE ""
106#endif /* !RT_OS_WINDOWS */
107
108// defines / prototypes
109/////////////////////////////////////////////////////////////////////////////
110
111/////////////////////////////////////////////////////////////////////////////
112// Machine::Data structure
113/////////////////////////////////////////////////////////////////////////////
114
115Machine::Data::Data()
116{
117 mRegistered = FALSE;
118 pMachineConfigFile = NULL;
119 /* Contains hints on what has changed when the user is using the VM (config
120 * changes, running the VM, ...). This is used to decide if a config needs
121 * to be written to disk. */
122 flModifications = 0;
123 /* VM modification usually also trigger setting the current state to
124 * "Modified". Although this is not always the case. An e.g. is the VM
125 * initialization phase or when snapshot related data is changed. The
126 * actually behavior is controlled by the following flag. */
127 m_fAllowStateModification = false;
128 mAccessible = FALSE;
129 /* mUuid is initialized in Machine::init() */
130
131 mMachineState = MachineState_PoweredOff;
132 RTTimeNow(&mLastStateChange);
133
134 mMachineStateDeps = 0;
135 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
136 mMachineStateChangePending = 0;
137
138 mCurrentStateModified = TRUE;
139 mGuestPropertiesModified = FALSE;
140
141 mSession.mPID = NIL_RTPROCESS;
142 mSession.mLockType = LockType_Null;
143 mSession.mState = SessionState_Unlocked;
144}
145
146Machine::Data::~Data()
147{
148 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
149 {
150 RTSemEventMultiDestroy(mMachineStateDepsSem);
151 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
152 }
153 if (pMachineConfigFile)
154 {
155 delete pMachineConfigFile;
156 pMachineConfigFile = NULL;
157 }
158}
159
160/////////////////////////////////////////////////////////////////////////////
161// Machine::HWData structure
162/////////////////////////////////////////////////////////////////////////////
163
164Machine::HWData::HWData()
165{
166 /* default values for a newly created machine */
167 mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
168 mMemorySize = 128;
169 mCPUCount = 1;
170 mCPUHotPlugEnabled = false;
171 mMemoryBalloonSize = 0;
172 mPageFusionEnabled = false;
173 mHWVirtExEnabled = true;
174 mHWVirtExNestedPagingEnabled = true;
175#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
176 mHWVirtExLargePagesEnabled = true;
177#else
178 /* Not supported on 32 bits hosts. */
179 mHWVirtExLargePagesEnabled = false;
180#endif
181 mHWVirtExVPIDEnabled = true;
182 mHWVirtExUXEnabled = true;
183 mHWVirtExForceEnabled = false;
184 mHWVirtExUseNativeApi = false;
185#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
186 mPAEEnabled = true;
187#else
188 mPAEEnabled = false;
189#endif
190 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
191 mTripleFaultReset = false;
192 mAPIC = true;
193 mX2APIC = false;
194 mIBPBOnVMExit = false;
195 mIBPBOnVMEntry = false;
196 mSpecCtrl = false;
197 mSpecCtrlByHost = false;
198 mL1DFlushOnSched = true;
199 mL1DFlushOnVMEntry = false;
200 mMDSClearOnSched = true;
201 mMDSClearOnVMEntry = false;
202 mNestedHWVirt = false;
203 mHPETEnabled = false;
204 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
205 mCpuIdPortabilityLevel = 0;
206 mCpuProfile = "host";
207
208 /* default boot order: floppy - DVD - HDD */
209 mBootOrder[0] = DeviceType_Floppy;
210 mBootOrder[1] = DeviceType_DVD;
211 mBootOrder[2] = DeviceType_HardDisk;
212 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
213 mBootOrder[i] = DeviceType_Null;
214
215 mClipboardMode = ClipboardMode_Disabled;
216 mClipboardFileTransfersEnabled = FALSE;
217
218 mDnDMode = DnDMode_Disabled;
219
220 mFirmwareType = FirmwareType_BIOS;
221 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
222 mPointingHIDType = PointingHIDType_PS2Mouse;
223 mChipsetType = ChipsetType_PIIX3;
224 mParavirtProvider = ParavirtProvider_Default;
225 mEmulatedUSBCardReaderEnabled = FALSE;
226
227 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
228 mCPUAttached[i] = false;
229
230 mIOCacheEnabled = true;
231 mIOCacheSize = 5; /* 5MB */
232}
233
234Machine::HWData::~HWData()
235{
236}
237
238/////////////////////////////////////////////////////////////////////////////
239// Machine class
240/////////////////////////////////////////////////////////////////////////////
241
242// constructor / destructor
243/////////////////////////////////////////////////////////////////////////////
244
245Machine::Machine() :
246#ifdef VBOX_WITH_RESOURCE_USAGE_API
247 mCollectorGuest(NULL),
248#endif
249 mPeer(NULL),
250 mParent(NULL),
251 mSerialPorts(),
252 mParallelPorts(),
253 uRegistryNeedsSaving(0)
254{}
255
256Machine::~Machine()
257{}
258
259HRESULT Machine::FinalConstruct()
260{
261 LogFlowThisFunc(("\n"));
262 return BaseFinalConstruct();
263}
264
265void Machine::FinalRelease()
266{
267 LogFlowThisFunc(("\n"));
268 uninit();
269 BaseFinalRelease();
270}
271
272/**
273 * Initializes a new machine instance; this init() variant creates a new, empty machine.
274 * This gets called from VirtualBox::CreateMachine().
275 *
276 * @param aParent Associated parent object
277 * @param strConfigFile Local file system path to the VM settings file (can
278 * be relative to the VirtualBox config directory).
279 * @param strName name for the machine
280 * @param llGroups list of groups for the machine
281 * @param strOsType OS Type string (stored as is if aOsType is NULL).
282 * @param aOsType OS Type of this machine or NULL.
283 * @param aId UUID for the new machine.
284 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
285 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
286 * scheme (includes the UUID).
287 *
288 * @return Success indicator. if not S_OK, the machine object is invalid
289 */
290HRESULT Machine::init(VirtualBox *aParent,
291 const Utf8Str &strConfigFile,
292 const Utf8Str &strName,
293 const StringsList &llGroups,
294 const Utf8Str &strOsType,
295 GuestOSType *aOsType,
296 const Guid &aId,
297 bool fForceOverwrite,
298 bool fDirectoryIncludesUUID)
299{
300 LogFlowThisFuncEnter();
301 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
302
303 /* Enclose the state transition NotReady->InInit->Ready */
304 AutoInitSpan autoInitSpan(this);
305 AssertReturn(autoInitSpan.isOk(), E_FAIL);
306
307 HRESULT rc = initImpl(aParent, strConfigFile);
308 if (FAILED(rc)) return rc;
309
310 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
311 if (FAILED(rc)) return rc;
312
313 if (SUCCEEDED(rc))
314 {
315 // create an empty machine config
316 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
317
318 rc = initDataAndChildObjects();
319 }
320
321 if (SUCCEEDED(rc))
322 {
323 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
324 mData->mAccessible = TRUE;
325
326 unconst(mData->mUuid) = aId;
327
328 mUserData->s.strName = strName;
329
330 if (llGroups.size())
331 mUserData->s.llGroups = llGroups;
332
333 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
334 // the "name sync" flag determines whether the machine directory gets renamed along
335 // with the machine file; say so if the settings file name is the same as the
336 // settings file parent directory (machine directory)
337 mUserData->s.fNameSync = i_isInOwnDir();
338
339 // initialize the default snapshots folder
340 rc = COMSETTER(SnapshotFolder)(NULL);
341 AssertComRC(rc);
342
343 if (aOsType)
344 {
345 /* Store OS type */
346 mUserData->s.strOsType = aOsType->i_id();
347
348 /* Let the OS type select 64-bit ness. */
349 mHWData->mLongMode = aOsType->i_is64Bit()
350 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
351
352 /* Let the OS type enable the X2APIC */
353 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
354 }
355 else if (!strOsType.isEmpty())
356 {
357 /* Store OS type */
358 mUserData->s.strOsType = strOsType;
359
360 /* No guest OS type object. Pick some plausible defaults which the
361 * host can handle. There's no way to know or validate anything. */
362 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
363 mHWData->mX2APIC = false;
364 }
365
366 /* Apply BIOS defaults. */
367 mBIOSSettings->i_applyDefaults(aOsType);
368
369 /* Apply record defaults. */
370 mRecordingSettings->i_applyDefaults();
371
372 /* Apply network adapters defaults */
373 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
374 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
375
376 /* Apply serial port defaults */
377 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
378 mSerialPorts[slot]->i_applyDefaults(aOsType);
379
380 /* Apply parallel port defaults */
381 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
382 mParallelPorts[slot]->i_applyDefaults();
383
384 /* At this point the changing of the current state modification
385 * flag is allowed. */
386 i_allowStateModification();
387
388 /* commit all changes made during the initialization */
389 i_commit();
390 }
391
392 /* Confirm a successful initialization when it's the case */
393 if (SUCCEEDED(rc))
394 {
395 if (mData->mAccessible)
396 autoInitSpan.setSucceeded();
397 else
398 autoInitSpan.setLimited();
399 }
400
401 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
402 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
403 mData->mRegistered,
404 mData->mAccessible,
405 rc));
406
407 LogFlowThisFuncLeave();
408
409 return rc;
410}
411
412/**
413 * Initializes a new instance with data from machine XML (formerly Init_Registered).
414 * Gets called in two modes:
415 *
416 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
417 * UUID is specified and we mark the machine as "registered";
418 *
419 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
420 * and the machine remains unregistered until RegisterMachine() is called.
421 *
422 * @param aParent Associated parent object
423 * @param strConfigFile Local file system path to the VM settings file (can
424 * be relative to the VirtualBox config directory).
425 * @param aId UUID of the machine or NULL (see above).
426 *
427 * @return Success indicator. if not S_OK, the machine object is invalid
428 */
429HRESULT Machine::initFromSettings(VirtualBox *aParent,
430 const Utf8Str &strConfigFile,
431 const Guid *aId)
432{
433 LogFlowThisFuncEnter();
434 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
435
436 /* Enclose the state transition NotReady->InInit->Ready */
437 AutoInitSpan autoInitSpan(this);
438 AssertReturn(autoInitSpan.isOk(), E_FAIL);
439
440 HRESULT rc = initImpl(aParent, strConfigFile);
441 if (FAILED(rc)) return rc;
442
443 if (aId)
444 {
445 // loading a registered VM:
446 unconst(mData->mUuid) = *aId;
447 mData->mRegistered = TRUE;
448 // now load the settings from XML:
449 rc = i_registeredInit();
450 // this calls initDataAndChildObjects() and loadSettings()
451 }
452 else
453 {
454 // opening an unregistered VM (VirtualBox::OpenMachine()):
455 rc = initDataAndChildObjects();
456
457 if (SUCCEEDED(rc))
458 {
459 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
460 mData->mAccessible = TRUE;
461
462 try
463 {
464 // load and parse machine XML; this will throw on XML or logic errors
465 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
466
467 // reject VM UUID duplicates, they can happen if someone
468 // tries to register an already known VM config again
469 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
470 true /* fPermitInaccessible */,
471 false /* aDoSetError */,
472 NULL) != VBOX_E_OBJECT_NOT_FOUND)
473 {
474 throw setError(E_FAIL,
475 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
476 mData->m_strConfigFile.c_str());
477 }
478
479 // use UUID from machine config
480 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
481
482 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
483 NULL /* puuidRegistry */);
484 if (FAILED(rc)) throw rc;
485
486 /* At this point the changing of the current state modification
487 * flag is allowed. */
488 i_allowStateModification();
489
490 i_commit();
491 }
492 catch (HRESULT err)
493 {
494 /* we assume that error info is set by the thrower */
495 rc = err;
496 }
497 catch (...)
498 {
499 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
500 }
501 }
502 }
503
504 /* Confirm a successful initialization when it's the case */
505 if (SUCCEEDED(rc))
506 {
507 if (mData->mAccessible)
508 autoInitSpan.setSucceeded();
509 else
510 {
511 autoInitSpan.setLimited();
512
513 // uninit media from this machine's media registry, or else
514 // reloading the settings will fail
515 mParent->i_unregisterMachineMedia(i_getId());
516 }
517 }
518
519 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
520 "rc=%08X\n",
521 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
522 mData->mRegistered, mData->mAccessible, rc));
523
524 LogFlowThisFuncLeave();
525
526 return rc;
527}
528
529/**
530 * Initializes a new instance from a machine config that is already in memory
531 * (import OVF case). Since we are importing, the UUID in the machine
532 * config is ignored and we always generate a fresh one.
533 *
534 * @param aParent Associated parent object.
535 * @param strName Name for the new machine; this overrides what is specified in config.
536 * @param strSettingsFilename File name of .vbox file.
537 * @param config Machine configuration loaded and parsed from XML.
538 *
539 * @return Success indicator. if not S_OK, the machine object is invalid
540 */
541HRESULT Machine::init(VirtualBox *aParent,
542 const Utf8Str &strName,
543 const Utf8Str &strSettingsFilename,
544 const settings::MachineConfigFile &config)
545{
546 LogFlowThisFuncEnter();
547
548 /* Enclose the state transition NotReady->InInit->Ready */
549 AutoInitSpan autoInitSpan(this);
550 AssertReturn(autoInitSpan.isOk(), E_FAIL);
551
552 HRESULT rc = initImpl(aParent, strSettingsFilename);
553 if (FAILED(rc)) return rc;
554
555 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
556 if (FAILED(rc)) return rc;
557
558 rc = initDataAndChildObjects();
559
560 if (SUCCEEDED(rc))
561 {
562 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
563 mData->mAccessible = TRUE;
564
565 // create empty machine config for instance data
566 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
567
568 // generate fresh UUID, ignore machine config
569 unconst(mData->mUuid).create();
570
571 rc = i_loadMachineDataFromSettings(config,
572 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
573
574 // override VM name as well, it may be different
575 mUserData->s.strName = strName;
576
577 if (SUCCEEDED(rc))
578 {
579 /* At this point the changing of the current state modification
580 * flag is allowed. */
581 i_allowStateModification();
582
583 /* commit all changes made during the initialization */
584 i_commit();
585 }
586 }
587
588 /* Confirm a successful initialization when it's the case */
589 if (SUCCEEDED(rc))
590 {
591 if (mData->mAccessible)
592 autoInitSpan.setSucceeded();
593 else
594 {
595 /* Ignore all errors from unregistering, they would destroy
596- * the more interesting error information we already have,
597- * pinpointing the issue with the VM config. */
598 ErrorInfoKeeper eik;
599
600 autoInitSpan.setLimited();
601
602 // uninit media from this machine's media registry, or else
603 // reloading the settings will fail
604 mParent->i_unregisterMachineMedia(i_getId());
605 }
606 }
607
608 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
609 "rc=%08X\n",
610 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
611 mData->mRegistered, mData->mAccessible, rc));
612
613 LogFlowThisFuncLeave();
614
615 return rc;
616}
617
618/**
619 * Shared code between the various init() implementations.
620 * @param aParent The VirtualBox object.
621 * @param strConfigFile Settings file.
622 * @return
623 */
624HRESULT Machine::initImpl(VirtualBox *aParent,
625 const Utf8Str &strConfigFile)
626{
627 LogFlowThisFuncEnter();
628
629 AssertReturn(aParent, E_INVALIDARG);
630 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
631
632 HRESULT rc = S_OK;
633
634 /* share the parent weakly */
635 unconst(mParent) = aParent;
636
637 /* allocate the essential machine data structure (the rest will be
638 * allocated later by initDataAndChildObjects() */
639 mData.allocate();
640
641 /* memorize the config file name (as provided) */
642 mData->m_strConfigFile = strConfigFile;
643
644 /* get the full file name */
645 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
646 if (RT_FAILURE(vrc1))
647 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
648 tr("Invalid machine settings file name '%s' (%Rrc)"),
649 strConfigFile.c_str(),
650 vrc1);
651
652 LogFlowThisFuncLeave();
653
654 return rc;
655}
656
657/**
658 * Tries to create a machine settings file in the path stored in the machine
659 * instance data. Used when a new machine is created to fail gracefully if
660 * the settings file could not be written (e.g. because machine dir is read-only).
661 * @return
662 */
663HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
664{
665 HRESULT rc = S_OK;
666
667 // when we create a new machine, we must be able to create the settings file
668 RTFILE f = NIL_RTFILE;
669 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
670 if ( RT_SUCCESS(vrc)
671 || vrc == VERR_SHARING_VIOLATION
672 )
673 {
674 if (RT_SUCCESS(vrc))
675 RTFileClose(f);
676 if (!fForceOverwrite)
677 rc = setError(VBOX_E_FILE_ERROR,
678 tr("Machine settings file '%s' already exists"),
679 mData->m_strConfigFileFull.c_str());
680 else
681 {
682 /* try to delete the config file, as otherwise the creation
683 * of a new settings file will fail. */
684 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
685 if (RT_FAILURE(vrc2))
686 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
687 tr("Could not delete the existing settings file '%s' (%Rrc)"),
688 mData->m_strConfigFileFull.c_str(), vrc2);
689 }
690 }
691 else if ( vrc != VERR_FILE_NOT_FOUND
692 && vrc != VERR_PATH_NOT_FOUND
693 )
694 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
695 tr("Invalid machine settings file name '%s' (%Rrc)"),
696 mData->m_strConfigFileFull.c_str(),
697 vrc);
698 return rc;
699}
700
701/**
702 * Initializes the registered machine by loading the settings file.
703 * This method is separated from #init() in order to make it possible to
704 * retry the operation after VirtualBox startup instead of refusing to
705 * startup the whole VirtualBox server in case if the settings file of some
706 * registered VM is invalid or inaccessible.
707 *
708 * @note Must be always called from this object's write lock
709 * (unless called from #init() that doesn't need any locking).
710 * @note Locks the mUSBController method for writing.
711 * @note Subclasses must not call this method.
712 */
713HRESULT Machine::i_registeredInit()
714{
715 AssertReturn(!i_isSessionMachine(), E_FAIL);
716 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
717 AssertReturn(mData->mUuid.isValid(), E_FAIL);
718 AssertReturn(!mData->mAccessible, E_FAIL);
719
720 HRESULT rc = initDataAndChildObjects();
721
722 if (SUCCEEDED(rc))
723 {
724 /* Temporarily reset the registered flag in order to let setters
725 * potentially called from loadSettings() succeed (isMutable() used in
726 * all setters will return FALSE for a Machine instance if mRegistered
727 * is TRUE). */
728 mData->mRegistered = FALSE;
729
730 try
731 {
732 // load and parse machine XML; this will throw on XML or logic errors
733 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
734
735 if (mData->mUuid != mData->pMachineConfigFile->uuid)
736 throw setError(E_FAIL,
737 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
738 mData->pMachineConfigFile->uuid.raw(),
739 mData->m_strConfigFileFull.c_str(),
740 mData->mUuid.toString().c_str(),
741 mParent->i_settingsFilePath().c_str());
742
743 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
744 NULL /* const Guid *puuidRegistry */);
745 if (FAILED(rc)) throw rc;
746 }
747 catch (HRESULT err)
748 {
749 /* we assume that error info is set by the thrower */
750 rc = err;
751 }
752 catch (...)
753 {
754 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
755 }
756
757 /* Restore the registered flag (even on failure) */
758 mData->mRegistered = TRUE;
759 }
760
761 if (SUCCEEDED(rc))
762 {
763 /* Set mAccessible to TRUE only if we successfully locked and loaded
764 * the settings file */
765 mData->mAccessible = TRUE;
766
767 /* commit all changes made during loading the settings file */
768 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
769 /// @todo r=klaus for some reason the settings loading logic backs up
770 // the settings, and therefore a commit is needed. Should probably be changed.
771 }
772 else
773 {
774 /* If the machine is registered, then, instead of returning a
775 * failure, we mark it as inaccessible and set the result to
776 * success to give it a try later */
777
778 /* fetch the current error info */
779 mData->mAccessError = com::ErrorInfo();
780 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
781
782 /* rollback all changes */
783 i_rollback(false /* aNotify */);
784
785 // uninit media from this machine's media registry, or else
786 // reloading the settings will fail
787 mParent->i_unregisterMachineMedia(i_getId());
788
789 /* uninitialize the common part to make sure all data is reset to
790 * default (null) values */
791 uninitDataAndChildObjects();
792
793 rc = S_OK;
794 }
795
796 return rc;
797}
798
799/**
800 * Uninitializes the instance.
801 * Called either from FinalRelease() or by the parent when it gets destroyed.
802 *
803 * @note The caller of this method must make sure that this object
804 * a) doesn't have active callers on the current thread and b) is not locked
805 * by the current thread; otherwise uninit() will hang either a) due to
806 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
807 * a dead-lock caused by this thread waiting for all callers on the other
808 * threads are done but preventing them from doing so by holding a lock.
809 */
810void Machine::uninit()
811{
812 LogFlowThisFuncEnter();
813
814 Assert(!isWriteLockOnCurrentThread());
815
816 Assert(!uRegistryNeedsSaving);
817 if (uRegistryNeedsSaving)
818 {
819 AutoCaller autoCaller(this);
820 if (SUCCEEDED(autoCaller.rc()))
821 {
822 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
823 i_saveSettings(NULL, Machine::SaveS_Force);
824 }
825 }
826
827 /* Enclose the state transition Ready->InUninit->NotReady */
828 AutoUninitSpan autoUninitSpan(this);
829 if (autoUninitSpan.uninitDone())
830 return;
831
832 Assert(!i_isSnapshotMachine());
833 Assert(!i_isSessionMachine());
834 Assert(!!mData);
835
836 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
837 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
838
839 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
840
841 if (!mData->mSession.mMachine.isNull())
842 {
843 /* Theoretically, this can only happen if the VirtualBox server has been
844 * terminated while there were clients running that owned open direct
845 * sessions. Since in this case we are definitely called by
846 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
847 * won't happen on the client watcher thread (because it has a
848 * VirtualBox caller for the duration of the
849 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
850 * cannot happen until the VirtualBox caller is released). This is
851 * important, because SessionMachine::uninit() cannot correctly operate
852 * after we return from this method (it expects the Machine instance is
853 * still valid). We'll call it ourselves below.
854 */
855 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
856 (SessionMachine*)mData->mSession.mMachine));
857
858 if (Global::IsOnlineOrTransient(mData->mMachineState))
859 {
860 Log1WarningThisFunc(("Setting state to Aborted!\n"));
861 /* set machine state using SessionMachine reimplementation */
862 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
863 }
864
865 /*
866 * Uninitialize SessionMachine using public uninit() to indicate
867 * an unexpected uninitialization.
868 */
869 mData->mSession.mMachine->uninit();
870 /* SessionMachine::uninit() must set mSession.mMachine to null */
871 Assert(mData->mSession.mMachine.isNull());
872 }
873
874 // uninit media from this machine's media registry, if they're still there
875 Guid uuidMachine(i_getId());
876
877 /* the lock is no more necessary (SessionMachine is uninitialized) */
878 alock.release();
879
880 /* XXX This will fail with
881 * "cannot be closed because it is still attached to 1 virtual machines"
882 * because at this point we did not call uninitDataAndChildObjects() yet
883 * and therefore also removeBackReference() for all these mediums was not called! */
884
885 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
886 mParent->i_unregisterMachineMedia(uuidMachine);
887
888 // has machine been modified?
889 if (mData->flModifications)
890 {
891 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
892 i_rollback(false /* aNotify */);
893 }
894
895 if (mData->mAccessible)
896 uninitDataAndChildObjects();
897
898 /* free the essential data structure last */
899 mData.free();
900
901 LogFlowThisFuncLeave();
902}
903
904// Wrapped IMachine properties
905/////////////////////////////////////////////////////////////////////////////
906HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
907{
908 /* mParent is constant during life time, no need to lock */
909 ComObjPtr<VirtualBox> pVirtualBox(mParent);
910 aParent = pVirtualBox;
911
912 return S_OK;
913}
914
915
916HRESULT Machine::getAccessible(BOOL *aAccessible)
917{
918 /* In some cases (medium registry related), it is necessary to be able to
919 * go through the list of all machines. Happens when an inaccessible VM
920 * has a sensible medium registry. */
921 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
923
924 HRESULT rc = S_OK;
925
926 if (!mData->mAccessible)
927 {
928 /* try to initialize the VM once more if not accessible */
929
930 AutoReinitSpan autoReinitSpan(this);
931 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
932
933#ifdef DEBUG
934 LogFlowThisFunc(("Dumping media backreferences\n"));
935 mParent->i_dumpAllBackRefs();
936#endif
937
938 if (mData->pMachineConfigFile)
939 {
940 // reset the XML file to force loadSettings() (called from i_registeredInit())
941 // to parse it again; the file might have changed
942 delete mData->pMachineConfigFile;
943 mData->pMachineConfigFile = NULL;
944 }
945
946 rc = i_registeredInit();
947
948 if (SUCCEEDED(rc) && mData->mAccessible)
949 {
950 autoReinitSpan.setSucceeded();
951
952 /* make sure interesting parties will notice the accessibility
953 * state change */
954 mParent->i_onMachineStateChanged(mData->mUuid, mData->mMachineState);
955 mParent->i_onMachineDataChanged(mData->mUuid);
956 }
957 }
958
959 if (SUCCEEDED(rc))
960 *aAccessible = mData->mAccessible;
961
962 LogFlowThisFuncLeave();
963
964 return rc;
965}
966
967HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
968{
969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
970
971 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
972 {
973 /* return shortly */
974 aAccessError = NULL;
975 return S_OK;
976 }
977
978 HRESULT rc = S_OK;
979
980 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
981 rc = errorInfo.createObject();
982 if (SUCCEEDED(rc))
983 {
984 errorInfo->init(mData->mAccessError.getResultCode(),
985 mData->mAccessError.getInterfaceID().ref(),
986 Utf8Str(mData->mAccessError.getComponent()).c_str(),
987 Utf8Str(mData->mAccessError.getText()));
988 aAccessError = errorInfo;
989 }
990
991 return rc;
992}
993
994HRESULT Machine::getName(com::Utf8Str &aName)
995{
996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
997
998 aName = mUserData->s.strName;
999
1000 return S_OK;
1001}
1002
1003HRESULT Machine::setName(const com::Utf8Str &aName)
1004{
1005 // prohibit setting a UUID only as the machine name, or else it can
1006 // never be found by findMachine()
1007 Guid test(aName);
1008
1009 if (test.isValid())
1010 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1011
1012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1013
1014 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1015 if (FAILED(rc)) return rc;
1016
1017 i_setModified(IsModified_MachineData);
1018 mUserData.backup();
1019 mUserData->s.strName = aName;
1020
1021 return S_OK;
1022}
1023
1024HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1025{
1026 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1027
1028 aDescription = mUserData->s.strDescription;
1029
1030 return S_OK;
1031}
1032
1033HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1034{
1035 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1036
1037 // this can be done in principle in any state as it doesn't affect the VM
1038 // significantly, but play safe by not messing around while complex
1039 // activities are going on
1040 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1041 if (FAILED(rc)) return rc;
1042
1043 i_setModified(IsModified_MachineData);
1044 mUserData.backup();
1045 mUserData->s.strDescription = aDescription;
1046
1047 return S_OK;
1048}
1049
1050HRESULT Machine::getId(com::Guid &aId)
1051{
1052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1053
1054 aId = mData->mUuid;
1055
1056 return S_OK;
1057}
1058
1059HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1060{
1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1062 aGroups.resize(mUserData->s.llGroups.size());
1063 size_t i = 0;
1064 for (StringsList::const_iterator
1065 it = mUserData->s.llGroups.begin();
1066 it != mUserData->s.llGroups.end();
1067 ++it, ++i)
1068 aGroups[i] = (*it);
1069
1070 return S_OK;
1071}
1072
1073HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1074{
1075 StringsList llGroups;
1076 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1077 if (FAILED(rc))
1078 return rc;
1079
1080 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1081
1082 rc = i_checkStateDependency(MutableOrSavedStateDep);
1083 if (FAILED(rc)) return rc;
1084
1085 i_setModified(IsModified_MachineData);
1086 mUserData.backup();
1087 mUserData->s.llGroups = llGroups;
1088
1089 return S_OK;
1090}
1091
1092HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1093{
1094 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 aOSTypeId = mUserData->s.strOsType;
1097
1098 return S_OK;
1099}
1100
1101HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1102{
1103 /* look up the object by Id to check it is valid */
1104 ComObjPtr<GuestOSType> pGuestOSType;
1105 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1106
1107 /* when setting, always use the "etalon" value for consistency -- lookup
1108 * by ID is case-insensitive and the input value may have different case */
1109 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1110
1111 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1112
1113 HRESULT rc = i_checkStateDependency(MutableStateDep);
1114 if (FAILED(rc)) return rc;
1115
1116 i_setModified(IsModified_MachineData);
1117 mUserData.backup();
1118 mUserData->s.strOsType = osTypeId;
1119
1120 return S_OK;
1121}
1122
1123HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1124{
1125 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1126
1127 *aFirmwareType = mHWData->mFirmwareType;
1128
1129 return S_OK;
1130}
1131
1132HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1133{
1134 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1135
1136 HRESULT rc = i_checkStateDependency(MutableStateDep);
1137 if (FAILED(rc)) return rc;
1138
1139 i_setModified(IsModified_MachineData);
1140 mHWData.backup();
1141 mHWData->mFirmwareType = aFirmwareType;
1142 Utf8Str strNVRAM = i_getDefaultNVRAMFilename();
1143 alock.release();
1144
1145 mBIOSSettings->i_updateNonVolatileStorageFile(strNVRAM);
1146
1147 return S_OK;
1148}
1149
1150HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1151{
1152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1153
1154 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1155
1156 return S_OK;
1157}
1158
1159HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1160{
1161 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1162
1163 HRESULT rc = i_checkStateDependency(MutableStateDep);
1164 if (FAILED(rc)) return rc;
1165
1166 i_setModified(IsModified_MachineData);
1167 mHWData.backup();
1168 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1169
1170 return S_OK;
1171}
1172
1173HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1174{
1175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1176
1177 *aPointingHIDType = mHWData->mPointingHIDType;
1178
1179 return S_OK;
1180}
1181
1182HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1183{
1184 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1185
1186 HRESULT rc = i_checkStateDependency(MutableStateDep);
1187 if (FAILED(rc)) return rc;
1188
1189 i_setModified(IsModified_MachineData);
1190 mHWData.backup();
1191 mHWData->mPointingHIDType = aPointingHIDType;
1192
1193 return S_OK;
1194}
1195
1196HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1197{
1198 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1199
1200 *aChipsetType = mHWData->mChipsetType;
1201
1202 return S_OK;
1203}
1204
1205HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1206{
1207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1208
1209 HRESULT rc = i_checkStateDependency(MutableStateDep);
1210 if (FAILED(rc)) return rc;
1211
1212 if (aChipsetType != mHWData->mChipsetType)
1213 {
1214 i_setModified(IsModified_MachineData);
1215 mHWData.backup();
1216 mHWData->mChipsetType = aChipsetType;
1217
1218 // Resize network adapter array, to be finalized on commit/rollback.
1219 // We must not throw away entries yet, otherwise settings are lost
1220 // without a way to roll back.
1221 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1222 size_t oldCount = mNetworkAdapters.size();
1223 if (newCount > oldCount)
1224 {
1225 mNetworkAdapters.resize(newCount);
1226 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1227 {
1228 unconst(mNetworkAdapters[slot]).createObject();
1229 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1230 }
1231 }
1232 }
1233
1234 return S_OK;
1235}
1236
1237HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1238{
1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1240
1241 aParavirtDebug = mHWData->mParavirtDebug;
1242 return S_OK;
1243}
1244
1245HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1246{
1247 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1248
1249 HRESULT rc = i_checkStateDependency(MutableStateDep);
1250 if (FAILED(rc)) return rc;
1251
1252 /** @todo Parse/validate options? */
1253 if (aParavirtDebug != mHWData->mParavirtDebug)
1254 {
1255 i_setModified(IsModified_MachineData);
1256 mHWData.backup();
1257 mHWData->mParavirtDebug = aParavirtDebug;
1258 }
1259
1260 return S_OK;
1261}
1262
1263HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1264{
1265 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1266
1267 *aParavirtProvider = mHWData->mParavirtProvider;
1268
1269 return S_OK;
1270}
1271
1272HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1273{
1274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1275
1276 HRESULT rc = i_checkStateDependency(MutableStateDep);
1277 if (FAILED(rc)) return rc;
1278
1279 if (aParavirtProvider != mHWData->mParavirtProvider)
1280 {
1281 i_setModified(IsModified_MachineData);
1282 mHWData.backup();
1283 mHWData->mParavirtProvider = aParavirtProvider;
1284 }
1285
1286 return S_OK;
1287}
1288
1289HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1290{
1291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1292
1293 *aParavirtProvider = mHWData->mParavirtProvider;
1294 switch (mHWData->mParavirtProvider)
1295 {
1296 case ParavirtProvider_None:
1297 case ParavirtProvider_HyperV:
1298 case ParavirtProvider_KVM:
1299 case ParavirtProvider_Minimal:
1300 break;
1301
1302 /* Resolve dynamic provider types to the effective types. */
1303 default:
1304 {
1305 ComObjPtr<GuestOSType> pGuestOSType;
1306 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1307 pGuestOSType);
1308 if (FAILED(hrc2) || pGuestOSType.isNull())
1309 {
1310 *aParavirtProvider = ParavirtProvider_None;
1311 break;
1312 }
1313
1314 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1315 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1316
1317 switch (mHWData->mParavirtProvider)
1318 {
1319 case ParavirtProvider_Legacy:
1320 {
1321 if (fOsXGuest)
1322 *aParavirtProvider = ParavirtProvider_Minimal;
1323 else
1324 *aParavirtProvider = ParavirtProvider_None;
1325 break;
1326 }
1327
1328 case ParavirtProvider_Default:
1329 {
1330 if (fOsXGuest)
1331 *aParavirtProvider = ParavirtProvider_Minimal;
1332 else if ( mUserData->s.strOsType == "Windows10"
1333 || mUserData->s.strOsType == "Windows10_64"
1334 || mUserData->s.strOsType == "Windows81"
1335 || mUserData->s.strOsType == "Windows81_64"
1336 || mUserData->s.strOsType == "Windows8"
1337 || mUserData->s.strOsType == "Windows8_64"
1338 || mUserData->s.strOsType == "Windows7"
1339 || mUserData->s.strOsType == "Windows7_64"
1340 || mUserData->s.strOsType == "WindowsVista"
1341 || mUserData->s.strOsType == "WindowsVista_64"
1342 || mUserData->s.strOsType == "Windows2012"
1343 || mUserData->s.strOsType == "Windows2012_64"
1344 || mUserData->s.strOsType == "Windows2008"
1345 || mUserData->s.strOsType == "Windows2008_64")
1346 {
1347 *aParavirtProvider = ParavirtProvider_HyperV;
1348 }
1349 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1350 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1351 || mUserData->s.strOsType == "Linux"
1352 || mUserData->s.strOsType == "Linux_64"
1353 || mUserData->s.strOsType == "ArchLinux"
1354 || mUserData->s.strOsType == "ArchLinux_64"
1355 || mUserData->s.strOsType == "Debian"
1356 || mUserData->s.strOsType == "Debian_64"
1357 || mUserData->s.strOsType == "Fedora"
1358 || mUserData->s.strOsType == "Fedora_64"
1359 || mUserData->s.strOsType == "Gentoo"
1360 || mUserData->s.strOsType == "Gentoo_64"
1361 || mUserData->s.strOsType == "Mandriva"
1362 || mUserData->s.strOsType == "Mandriva_64"
1363 || mUserData->s.strOsType == "OpenSUSE"
1364 || mUserData->s.strOsType == "OpenSUSE_64"
1365 || mUserData->s.strOsType == "Oracle"
1366 || mUserData->s.strOsType == "Oracle_64"
1367 || mUserData->s.strOsType == "RedHat"
1368 || mUserData->s.strOsType == "RedHat_64"
1369 || mUserData->s.strOsType == "Turbolinux"
1370 || mUserData->s.strOsType == "Turbolinux_64"
1371 || mUserData->s.strOsType == "Ubuntu"
1372 || mUserData->s.strOsType == "Ubuntu_64"
1373 || mUserData->s.strOsType == "Xandros"
1374 || mUserData->s.strOsType == "Xandros_64")
1375 {
1376 *aParavirtProvider = ParavirtProvider_KVM;
1377 }
1378 else
1379 *aParavirtProvider = ParavirtProvider_None;
1380 break;
1381 }
1382
1383 default: AssertFailedBreak(); /* Shut up MSC. */
1384 }
1385 break;
1386 }
1387 }
1388
1389 Assert( *aParavirtProvider == ParavirtProvider_None
1390 || *aParavirtProvider == ParavirtProvider_Minimal
1391 || *aParavirtProvider == ParavirtProvider_HyperV
1392 || *aParavirtProvider == ParavirtProvider_KVM);
1393 return S_OK;
1394}
1395
1396HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1397{
1398 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1399
1400 aHardwareVersion = mHWData->mHWVersion;
1401
1402 return S_OK;
1403}
1404
1405HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1406{
1407 /* check known version */
1408 Utf8Str hwVersion = aHardwareVersion;
1409 if ( hwVersion.compare("1") != 0
1410 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1411 return setError(E_INVALIDARG,
1412 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1413
1414 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1415
1416 HRESULT rc = i_checkStateDependency(MutableStateDep);
1417 if (FAILED(rc)) return rc;
1418
1419 i_setModified(IsModified_MachineData);
1420 mHWData.backup();
1421 mHWData->mHWVersion = aHardwareVersion;
1422
1423 return S_OK;
1424}
1425
1426HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1427{
1428 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1429
1430 if (!mHWData->mHardwareUUID.isZero())
1431 aHardwareUUID = mHWData->mHardwareUUID;
1432 else
1433 aHardwareUUID = mData->mUuid;
1434
1435 return S_OK;
1436}
1437
1438HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1439{
1440 if (!aHardwareUUID.isValid())
1441 return E_INVALIDARG;
1442
1443 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1444
1445 HRESULT rc = i_checkStateDependency(MutableStateDep);
1446 if (FAILED(rc)) return rc;
1447
1448 i_setModified(IsModified_MachineData);
1449 mHWData.backup();
1450 if (aHardwareUUID == mData->mUuid)
1451 mHWData->mHardwareUUID.clear();
1452 else
1453 mHWData->mHardwareUUID = aHardwareUUID;
1454
1455 return S_OK;
1456}
1457
1458HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1459{
1460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1461
1462 *aMemorySize = mHWData->mMemorySize;
1463
1464 return S_OK;
1465}
1466
1467HRESULT Machine::setMemorySize(ULONG aMemorySize)
1468{
1469 /* check RAM limits */
1470 if ( aMemorySize < MM_RAM_MIN_IN_MB
1471 || aMemorySize > MM_RAM_MAX_IN_MB
1472 )
1473 return setError(E_INVALIDARG,
1474 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1475 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1476
1477 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1478
1479 HRESULT rc = i_checkStateDependency(MutableStateDep);
1480 if (FAILED(rc)) return rc;
1481
1482 i_setModified(IsModified_MachineData);
1483 mHWData.backup();
1484 mHWData->mMemorySize = aMemorySize;
1485
1486 return S_OK;
1487}
1488
1489HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1490{
1491 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1492
1493 *aCPUCount = mHWData->mCPUCount;
1494
1495 return S_OK;
1496}
1497
1498HRESULT Machine::setCPUCount(ULONG aCPUCount)
1499{
1500 /* check CPU limits */
1501 if ( aCPUCount < SchemaDefs::MinCPUCount
1502 || aCPUCount > SchemaDefs::MaxCPUCount
1503 )
1504 return setError(E_INVALIDARG,
1505 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1506 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1507
1508 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1509
1510 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1511 if (mHWData->mCPUHotPlugEnabled)
1512 {
1513 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1514 {
1515 if (mHWData->mCPUAttached[idx])
1516 return setError(E_INVALIDARG,
1517 tr("There is still a CPU attached to socket %lu."
1518 "Detach the CPU before removing the socket"),
1519 aCPUCount, idx+1);
1520 }
1521 }
1522
1523 HRESULT rc = i_checkStateDependency(MutableStateDep);
1524 if (FAILED(rc)) return rc;
1525
1526 i_setModified(IsModified_MachineData);
1527 mHWData.backup();
1528 mHWData->mCPUCount = aCPUCount;
1529
1530 return S_OK;
1531}
1532
1533HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1534{
1535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1536
1537 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1538
1539 return S_OK;
1540}
1541
1542HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1543{
1544 HRESULT rc = S_OK;
1545
1546 /* check throttle limits */
1547 if ( aCPUExecutionCap < 1
1548 || aCPUExecutionCap > 100
1549 )
1550 return setError(E_INVALIDARG,
1551 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1552 aCPUExecutionCap, 1, 100);
1553
1554 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1555
1556 alock.release();
1557 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1558 alock.acquire();
1559 if (FAILED(rc)) return rc;
1560
1561 i_setModified(IsModified_MachineData);
1562 mHWData.backup();
1563 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1564
1565 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1566 if (Global::IsOnline(mData->mMachineState))
1567 i_saveSettings(NULL);
1568
1569 return S_OK;
1570}
1571
1572HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1573{
1574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1575
1576 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1577
1578 return S_OK;
1579}
1580
1581HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1582{
1583 HRESULT rc = S_OK;
1584
1585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1586
1587 rc = i_checkStateDependency(MutableStateDep);
1588 if (FAILED(rc)) return rc;
1589
1590 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1591 {
1592 if (aCPUHotPlugEnabled)
1593 {
1594 i_setModified(IsModified_MachineData);
1595 mHWData.backup();
1596
1597 /* Add the amount of CPUs currently attached */
1598 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1599 mHWData->mCPUAttached[i] = true;
1600 }
1601 else
1602 {
1603 /*
1604 * We can disable hotplug only if the amount of maximum CPUs is equal
1605 * to the amount of attached CPUs
1606 */
1607 unsigned cCpusAttached = 0;
1608 unsigned iHighestId = 0;
1609
1610 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1611 {
1612 if (mHWData->mCPUAttached[i])
1613 {
1614 cCpusAttached++;
1615 iHighestId = i;
1616 }
1617 }
1618
1619 if ( (cCpusAttached != mHWData->mCPUCount)
1620 || (iHighestId >= mHWData->mCPUCount))
1621 return setError(E_INVALIDARG,
1622 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1623
1624 i_setModified(IsModified_MachineData);
1625 mHWData.backup();
1626 }
1627 }
1628
1629 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1630
1631 return rc;
1632}
1633
1634HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1635{
1636 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1637
1638 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1639
1640 return S_OK;
1641}
1642
1643HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1644{
1645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1646
1647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1648 if (SUCCEEDED(hrc))
1649 {
1650 i_setModified(IsModified_MachineData);
1651 mHWData.backup();
1652 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1653 }
1654 return hrc;
1655}
1656
1657HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1658{
1659 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1660 aCPUProfile = mHWData->mCpuProfile;
1661 return S_OK;
1662}
1663
1664HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1665{
1666 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1667 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1668 if (SUCCEEDED(hrc))
1669 {
1670 i_setModified(IsModified_MachineData);
1671 mHWData.backup();
1672 /* Empty equals 'host'. */
1673 if (aCPUProfile.isNotEmpty())
1674 mHWData->mCpuProfile = aCPUProfile;
1675 else
1676 mHWData->mCpuProfile = "host";
1677 }
1678 return hrc;
1679}
1680
1681HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1682{
1683#ifdef VBOX_WITH_USB_CARDREADER
1684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1685
1686 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1687
1688 return S_OK;
1689#else
1690 NOREF(aEmulatedUSBCardReaderEnabled);
1691 return E_NOTIMPL;
1692#endif
1693}
1694
1695HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1696{
1697#ifdef VBOX_WITH_USB_CARDREADER
1698 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1699
1700 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1701 if (FAILED(rc)) return rc;
1702
1703 i_setModified(IsModified_MachineData);
1704 mHWData.backup();
1705 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1706
1707 return S_OK;
1708#else
1709 NOREF(aEmulatedUSBCardReaderEnabled);
1710 return E_NOTIMPL;
1711#endif
1712}
1713
1714HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1715{
1716 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1717
1718 *aHPETEnabled = mHWData->mHPETEnabled;
1719
1720 return S_OK;
1721}
1722
1723HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1724{
1725 HRESULT rc = S_OK;
1726
1727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1728
1729 rc = i_checkStateDependency(MutableStateDep);
1730 if (FAILED(rc)) return rc;
1731
1732 i_setModified(IsModified_MachineData);
1733 mHWData.backup();
1734
1735 mHWData->mHPETEnabled = aHPETEnabled;
1736
1737 return rc;
1738}
1739
1740/** @todo this method should not be public */
1741HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1742{
1743 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1744
1745 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1746
1747 return S_OK;
1748}
1749
1750/**
1751 * Set the memory balloon size.
1752 *
1753 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1754 * we have to make sure that we never call IGuest from here.
1755 */
1756HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1757{
1758 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1759#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1760 /* check limits */
1761 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1762 return setError(E_INVALIDARG,
1763 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1764 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1765
1766 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1767
1768 i_setModified(IsModified_MachineData);
1769 mHWData.backup();
1770 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1771
1772 return S_OK;
1773#else
1774 NOREF(aMemoryBalloonSize);
1775 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1776#endif
1777}
1778
1779HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1780{
1781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1782
1783 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1784 return S_OK;
1785}
1786
1787HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1788{
1789#ifdef VBOX_WITH_PAGE_SHARING
1790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1796 return S_OK;
1797#else
1798 NOREF(aPageFusionEnabled);
1799 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1800#endif
1801}
1802
1803HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1804{
1805 /* mBIOSSettings is constant during life time, no need to lock */
1806 aBIOSSettings = mBIOSSettings;
1807
1808 return S_OK;
1809}
1810
1811HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1812{
1813 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1814
1815 aRecordingSettings = mRecordingSettings;
1816
1817 return S_OK;
1818}
1819
1820HRESULT Machine::getGraphicsAdapter(ComPtr<IGraphicsAdapter> &aGraphicsAdapter)
1821{
1822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1823
1824 aGraphicsAdapter = mGraphicsAdapter;
1825
1826 return S_OK;
1827}
1828
1829HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1830{
1831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1832
1833 switch (aProperty)
1834 {
1835 case CPUPropertyType_PAE:
1836 *aValue = mHWData->mPAEEnabled;
1837 break;
1838
1839 case CPUPropertyType_LongMode:
1840 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1841 *aValue = TRUE;
1842 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1843 *aValue = FALSE;
1844#if HC_ARCH_BITS == 64
1845 else
1846 *aValue = TRUE;
1847#else
1848 else
1849 {
1850 *aValue = FALSE;
1851
1852 ComObjPtr<GuestOSType> pGuestOSType;
1853 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1854 pGuestOSType);
1855 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1856 {
1857 if (pGuestOSType->i_is64Bit())
1858 {
1859 ComObjPtr<Host> pHost = mParent->i_host();
1860 alock.release();
1861
1862 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1863 if (FAILED(hrc2))
1864 *aValue = FALSE;
1865 }
1866 }
1867 }
1868#endif
1869 break;
1870
1871 case CPUPropertyType_TripleFaultReset:
1872 *aValue = mHWData->mTripleFaultReset;
1873 break;
1874
1875 case CPUPropertyType_APIC:
1876 *aValue = mHWData->mAPIC;
1877 break;
1878
1879 case CPUPropertyType_X2APIC:
1880 *aValue = mHWData->mX2APIC;
1881 break;
1882
1883 case CPUPropertyType_IBPBOnVMExit:
1884 *aValue = mHWData->mIBPBOnVMExit;
1885 break;
1886
1887 case CPUPropertyType_IBPBOnVMEntry:
1888 *aValue = mHWData->mIBPBOnVMEntry;
1889 break;
1890
1891 case CPUPropertyType_SpecCtrl:
1892 *aValue = mHWData->mSpecCtrl;
1893 break;
1894
1895 case CPUPropertyType_SpecCtrlByHost:
1896 *aValue = mHWData->mSpecCtrlByHost;
1897 break;
1898
1899 case CPUPropertyType_HWVirt:
1900 *aValue = mHWData->mNestedHWVirt;
1901 break;
1902
1903 case CPUPropertyType_L1DFlushOnEMTScheduling:
1904 *aValue = mHWData->mL1DFlushOnSched;
1905 break;
1906
1907 case CPUPropertyType_L1DFlushOnVMEntry:
1908 *aValue = mHWData->mL1DFlushOnVMEntry;
1909 break;
1910
1911 case CPUPropertyType_MDSClearOnEMTScheduling:
1912 *aValue = mHWData->mMDSClearOnSched;
1913 break;
1914
1915 case CPUPropertyType_MDSClearOnVMEntry:
1916 *aValue = mHWData->mMDSClearOnVMEntry;
1917 break;
1918
1919 default:
1920 return E_INVALIDARG;
1921 }
1922 return S_OK;
1923}
1924
1925HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
1926{
1927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1928
1929 HRESULT rc = i_checkStateDependency(MutableStateDep);
1930 if (FAILED(rc)) return rc;
1931
1932 switch (aProperty)
1933 {
1934 case CPUPropertyType_PAE:
1935 i_setModified(IsModified_MachineData);
1936 mHWData.backup();
1937 mHWData->mPAEEnabled = !!aValue;
1938 break;
1939
1940 case CPUPropertyType_LongMode:
1941 i_setModified(IsModified_MachineData);
1942 mHWData.backup();
1943 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
1944 break;
1945
1946 case CPUPropertyType_TripleFaultReset:
1947 i_setModified(IsModified_MachineData);
1948 mHWData.backup();
1949 mHWData->mTripleFaultReset = !!aValue;
1950 break;
1951
1952 case CPUPropertyType_APIC:
1953 if (mHWData->mX2APIC)
1954 aValue = TRUE;
1955 i_setModified(IsModified_MachineData);
1956 mHWData.backup();
1957 mHWData->mAPIC = !!aValue;
1958 break;
1959
1960 case CPUPropertyType_X2APIC:
1961 i_setModified(IsModified_MachineData);
1962 mHWData.backup();
1963 mHWData->mX2APIC = !!aValue;
1964 if (aValue)
1965 mHWData->mAPIC = !!aValue;
1966 break;
1967
1968 case CPUPropertyType_IBPBOnVMExit:
1969 i_setModified(IsModified_MachineData);
1970 mHWData.backup();
1971 mHWData->mIBPBOnVMExit = !!aValue;
1972 break;
1973
1974 case CPUPropertyType_IBPBOnVMEntry:
1975 i_setModified(IsModified_MachineData);
1976 mHWData.backup();
1977 mHWData->mIBPBOnVMEntry = !!aValue;
1978 break;
1979
1980 case CPUPropertyType_SpecCtrl:
1981 i_setModified(IsModified_MachineData);
1982 mHWData.backup();
1983 mHWData->mSpecCtrl = !!aValue;
1984 break;
1985
1986 case CPUPropertyType_SpecCtrlByHost:
1987 i_setModified(IsModified_MachineData);
1988 mHWData.backup();
1989 mHWData->mSpecCtrlByHost = !!aValue;
1990 break;
1991
1992 case CPUPropertyType_HWVirt:
1993 i_setModified(IsModified_MachineData);
1994 mHWData.backup();
1995 mHWData->mNestedHWVirt = !!aValue;
1996 break;
1997
1998 case CPUPropertyType_L1DFlushOnEMTScheduling:
1999 i_setModified(IsModified_MachineData);
2000 mHWData.backup();
2001 mHWData->mL1DFlushOnSched = !!aValue;
2002 break;
2003
2004 case CPUPropertyType_L1DFlushOnVMEntry:
2005 i_setModified(IsModified_MachineData);
2006 mHWData.backup();
2007 mHWData->mL1DFlushOnVMEntry = !!aValue;
2008 break;
2009
2010 case CPUPropertyType_MDSClearOnEMTScheduling:
2011 i_setModified(IsModified_MachineData);
2012 mHWData.backup();
2013 mHWData->mMDSClearOnSched = !!aValue;
2014 break;
2015
2016 case CPUPropertyType_MDSClearOnVMEntry:
2017 i_setModified(IsModified_MachineData);
2018 mHWData.backup();
2019 mHWData->mMDSClearOnVMEntry = !!aValue;
2020 break;
2021
2022 default:
2023 return E_INVALIDARG;
2024 }
2025 return S_OK;
2026}
2027
2028HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2029 ULONG *aValEcx, ULONG *aValEdx)
2030{
2031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2032 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2033 {
2034 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2035 it != mHWData->mCpuIdLeafList.end();
2036 ++it)
2037 {
2038 if (aOrdinal == 0)
2039 {
2040 const settings::CpuIdLeaf &rLeaf= *it;
2041 *aIdx = rLeaf.idx;
2042 *aSubIdx = rLeaf.idxSub;
2043 *aValEax = rLeaf.uEax;
2044 *aValEbx = rLeaf.uEbx;
2045 *aValEcx = rLeaf.uEcx;
2046 *aValEdx = rLeaf.uEdx;
2047 return S_OK;
2048 }
2049 aOrdinal--;
2050 }
2051 }
2052 return E_INVALIDARG;
2053}
2054
2055HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2056{
2057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2058
2059 /*
2060 * Search the list.
2061 */
2062 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2063 {
2064 const settings::CpuIdLeaf &rLeaf= *it;
2065 if ( rLeaf.idx == aIdx
2066 && ( aSubIdx == UINT32_MAX
2067 || rLeaf.idxSub == aSubIdx) )
2068 {
2069 *aValEax = rLeaf.uEax;
2070 *aValEbx = rLeaf.uEbx;
2071 *aValEcx = rLeaf.uEcx;
2072 *aValEdx = rLeaf.uEdx;
2073 return S_OK;
2074 }
2075 }
2076
2077 return E_INVALIDARG;
2078}
2079
2080
2081HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2082{
2083 /*
2084 * Validate input before taking locks and checking state.
2085 */
2086 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2087 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2088 if ( aIdx >= UINT32_C(0x20)
2089 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2090 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2091 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2092
2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2094 HRESULT rc = i_checkStateDependency(MutableStateDep);
2095 if (FAILED(rc)) return rc;
2096
2097 /*
2098 * Impose a maximum number of leaves.
2099 */
2100 if (mHWData->mCpuIdLeafList.size() > 256)
2101 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2102
2103 /*
2104 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2105 */
2106 i_setModified(IsModified_MachineData);
2107 mHWData.backup();
2108
2109 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2110 {
2111 settings::CpuIdLeaf &rLeaf= *it;
2112 if ( rLeaf.idx == aIdx
2113 && ( aSubIdx == UINT32_MAX
2114 || rLeaf.idxSub == aSubIdx) )
2115 it = mHWData->mCpuIdLeafList.erase(it);
2116 else
2117 ++it;
2118 }
2119
2120 settings::CpuIdLeaf NewLeaf;
2121 NewLeaf.idx = aIdx;
2122 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2123 NewLeaf.uEax = aValEax;
2124 NewLeaf.uEbx = aValEbx;
2125 NewLeaf.uEcx = aValEcx;
2126 NewLeaf.uEdx = aValEdx;
2127 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2128 return S_OK;
2129}
2130
2131HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2132{
2133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2134
2135 HRESULT rc = i_checkStateDependency(MutableStateDep);
2136 if (FAILED(rc)) return rc;
2137
2138 /*
2139 * Do the removal.
2140 */
2141 bool fModified = mHWData.isBackedUp();
2142 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2143 {
2144 settings::CpuIdLeaf &rLeaf= *it;
2145 if ( rLeaf.idx == aIdx
2146 && ( aSubIdx == UINT32_MAX
2147 || rLeaf.idxSub == aSubIdx) )
2148 {
2149 if (!fModified)
2150 {
2151 fModified = true;
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 // Start from the beginning, since mHWData.backup() creates
2155 // a new list, causing iterator mixup. This makes sure that
2156 // the settings are not unnecessarily marked as modified,
2157 // at the price of extra list walking.
2158 it = mHWData->mCpuIdLeafList.begin();
2159 }
2160 else
2161 it = mHWData->mCpuIdLeafList.erase(it);
2162 }
2163 else
2164 ++it;
2165 }
2166
2167 return S_OK;
2168}
2169
2170HRESULT Machine::removeAllCPUIDLeaves()
2171{
2172 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2173
2174 HRESULT rc = i_checkStateDependency(MutableStateDep);
2175 if (FAILED(rc)) return rc;
2176
2177 if (mHWData->mCpuIdLeafList.size() > 0)
2178 {
2179 i_setModified(IsModified_MachineData);
2180 mHWData.backup();
2181
2182 mHWData->mCpuIdLeafList.clear();
2183 }
2184
2185 return S_OK;
2186}
2187HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2188{
2189 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2190
2191 switch(aProperty)
2192 {
2193 case HWVirtExPropertyType_Enabled:
2194 *aValue = mHWData->mHWVirtExEnabled;
2195 break;
2196
2197 case HWVirtExPropertyType_VPID:
2198 *aValue = mHWData->mHWVirtExVPIDEnabled;
2199 break;
2200
2201 case HWVirtExPropertyType_NestedPaging:
2202 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2203 break;
2204
2205 case HWVirtExPropertyType_UnrestrictedExecution:
2206 *aValue = mHWData->mHWVirtExUXEnabled;
2207 break;
2208
2209 case HWVirtExPropertyType_LargePages:
2210 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2211#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2212 *aValue = FALSE;
2213#endif
2214 break;
2215
2216 case HWVirtExPropertyType_Force:
2217 *aValue = mHWData->mHWVirtExForceEnabled;
2218 break;
2219
2220 case HWVirtExPropertyType_UseNativeApi:
2221 *aValue = mHWData->mHWVirtExUseNativeApi;
2222 break;
2223
2224 default:
2225 return E_INVALIDARG;
2226 }
2227 return S_OK;
2228}
2229
2230HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2231{
2232 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2233
2234 HRESULT rc = i_checkStateDependency(MutableStateDep);
2235 if (FAILED(rc)) return rc;
2236
2237 switch (aProperty)
2238 {
2239 case HWVirtExPropertyType_Enabled:
2240 i_setModified(IsModified_MachineData);
2241 mHWData.backup();
2242 mHWData->mHWVirtExEnabled = !!aValue;
2243 break;
2244
2245 case HWVirtExPropertyType_VPID:
2246 i_setModified(IsModified_MachineData);
2247 mHWData.backup();
2248 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2249 break;
2250
2251 case HWVirtExPropertyType_NestedPaging:
2252 i_setModified(IsModified_MachineData);
2253 mHWData.backup();
2254 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2255 break;
2256
2257 case HWVirtExPropertyType_UnrestrictedExecution:
2258 i_setModified(IsModified_MachineData);
2259 mHWData.backup();
2260 mHWData->mHWVirtExUXEnabled = !!aValue;
2261 break;
2262
2263 case HWVirtExPropertyType_LargePages:
2264 i_setModified(IsModified_MachineData);
2265 mHWData.backup();
2266 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2267 break;
2268
2269 case HWVirtExPropertyType_Force:
2270 i_setModified(IsModified_MachineData);
2271 mHWData.backup();
2272 mHWData->mHWVirtExForceEnabled = !!aValue;
2273 break;
2274
2275 case HWVirtExPropertyType_UseNativeApi:
2276 i_setModified(IsModified_MachineData);
2277 mHWData.backup();
2278 mHWData->mHWVirtExUseNativeApi = !!aValue;
2279 break;
2280
2281 default:
2282 return E_INVALIDARG;
2283 }
2284
2285 return S_OK;
2286}
2287
2288HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2289{
2290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2291
2292 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2293
2294 return S_OK;
2295}
2296
2297HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2298{
2299 /** @todo (r=dmik):
2300 * 1. Allow to change the name of the snapshot folder containing snapshots
2301 * 2. Rename the folder on disk instead of just changing the property
2302 * value (to be smart and not to leave garbage). Note that it cannot be
2303 * done here because the change may be rolled back. Thus, the right
2304 * place is #saveSettings().
2305 */
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 (!mData->mCurrentSnapshot.isNull())
2313 return setError(E_FAIL,
2314 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2315
2316 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2317
2318 if (strSnapshotFolder.isEmpty())
2319 strSnapshotFolder = "Snapshots";
2320 int vrc = i_calculateFullPath(strSnapshotFolder, strSnapshotFolder);
2321 if (RT_FAILURE(vrc))
2322 return setErrorBoth(E_FAIL, vrc,
2323 tr("Invalid snapshot folder '%s' (%Rrc)"),
2324 strSnapshotFolder.c_str(), vrc);
2325
2326 i_setModified(IsModified_MachineData);
2327 mUserData.backup();
2328
2329 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2330
2331 return S_OK;
2332}
2333
2334HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2335{
2336 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2337
2338 aMediumAttachments.resize(mMediumAttachments->size());
2339 size_t i = 0;
2340 for (MediumAttachmentList::const_iterator
2341 it = mMediumAttachments->begin();
2342 it != mMediumAttachments->end();
2343 ++it, ++i)
2344 aMediumAttachments[i] = *it;
2345
2346 return S_OK;
2347}
2348
2349HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2350{
2351 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 Assert(!!mVRDEServer);
2354
2355 aVRDEServer = mVRDEServer;
2356
2357 return S_OK;
2358}
2359
2360HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2361{
2362 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2363
2364 aAudioAdapter = mAudioAdapter;
2365
2366 return S_OK;
2367}
2368
2369HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2370{
2371#ifdef VBOX_WITH_VUSB
2372 clearError();
2373 MultiResult rc(S_OK);
2374
2375# ifdef VBOX_WITH_USB
2376 rc = mParent->i_host()->i_checkUSBProxyService();
2377 if (FAILED(rc)) return rc;
2378# endif
2379
2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2381
2382 aUSBControllers.resize(mUSBControllers->size());
2383 size_t i = 0;
2384 for (USBControllerList::const_iterator
2385 it = mUSBControllers->begin();
2386 it != mUSBControllers->end();
2387 ++it, ++i)
2388 aUSBControllers[i] = *it;
2389
2390 return S_OK;
2391#else
2392 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2393 * extended error info to indicate that USB is simply not available
2394 * (w/o treating it as a failure), for example, as in OSE */
2395 NOREF(aUSBControllers);
2396 ReturnComNotImplemented();
2397#endif /* VBOX_WITH_VUSB */
2398}
2399
2400HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2401{
2402#ifdef VBOX_WITH_VUSB
2403 clearError();
2404 MultiResult rc(S_OK);
2405
2406# ifdef VBOX_WITH_USB
2407 rc = mParent->i_host()->i_checkUSBProxyService();
2408 if (FAILED(rc)) return rc;
2409# endif
2410
2411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2412
2413 aUSBDeviceFilters = mUSBDeviceFilters;
2414 return rc;
2415#else
2416 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2417 * extended error info to indicate that USB is simply not available
2418 * (w/o treating it as a failure), for example, as in OSE */
2419 NOREF(aUSBDeviceFilters);
2420 ReturnComNotImplemented();
2421#endif /* VBOX_WITH_VUSB */
2422}
2423
2424HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2425{
2426 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2427
2428 aSettingsFilePath = mData->m_strConfigFileFull;
2429
2430 return S_OK;
2431}
2432
2433HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2434{
2435 RT_NOREF(aSettingsFilePath);
2436 ReturnComNotImplemented();
2437}
2438
2439HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2440{
2441 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2442
2443 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2444 if (FAILED(rc)) return rc;
2445
2446 if (!mData->pMachineConfigFile->fileExists())
2447 // this is a new machine, and no config file exists yet:
2448 *aSettingsModified = TRUE;
2449 else
2450 *aSettingsModified = (mData->flModifications != 0);
2451
2452 return S_OK;
2453}
2454
2455HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2456{
2457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2458
2459 *aSessionState = mData->mSession.mState;
2460
2461 return S_OK;
2462}
2463
2464HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2465{
2466 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2467
2468 aSessionName = mData->mSession.mName;
2469
2470 return S_OK;
2471}
2472
2473HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2474{
2475 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2476
2477 *aSessionPID = mData->mSession.mPID;
2478
2479 return S_OK;
2480}
2481
2482HRESULT Machine::getState(MachineState_T *aState)
2483{
2484 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2485
2486 *aState = mData->mMachineState;
2487 Assert(mData->mMachineState != MachineState_Null);
2488
2489 return S_OK;
2490}
2491
2492HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2493{
2494 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2495
2496 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2497
2498 return S_OK;
2499}
2500
2501HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2502{
2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2504
2505 aStateFilePath = mSSData->strStateFilePath;
2506
2507 return S_OK;
2508}
2509
2510HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2511{
2512 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2513
2514 i_getLogFolder(aLogFolder);
2515
2516 return S_OK;
2517}
2518
2519HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2520{
2521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2522
2523 aCurrentSnapshot = mData->mCurrentSnapshot;
2524
2525 return S_OK;
2526}
2527
2528HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2529{
2530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2531
2532 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2533 ? 0
2534 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2535
2536 return S_OK;
2537}
2538
2539HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2540{
2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2542
2543 /* Note: for machines with no snapshots, we always return FALSE
2544 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2545 * reasons :) */
2546
2547 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2548 ? FALSE
2549 : mData->mCurrentStateModified;
2550
2551 return S_OK;
2552}
2553
2554HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2555{
2556 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2557
2558 aSharedFolders.resize(mHWData->mSharedFolders.size());
2559 size_t i = 0;
2560 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2561 it = mHWData->mSharedFolders.begin();
2562 it != mHWData->mSharedFolders.end();
2563 ++it, ++i)
2564 aSharedFolders[i] = *it;
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2570{
2571 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2572
2573 *aClipboardMode = mHWData->mClipboardMode;
2574
2575 return S_OK;
2576}
2577
2578HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2579{
2580 HRESULT rc = S_OK;
2581
2582 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2583
2584 alock.release();
2585 rc = i_onClipboardModeChange(aClipboardMode);
2586 alock.acquire();
2587 if (FAILED(rc)) return rc;
2588
2589 i_setModified(IsModified_MachineData);
2590 mHWData.backup();
2591 mHWData->mClipboardMode = aClipboardMode;
2592
2593 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2594 if (Global::IsOnline(mData->mMachineState))
2595 i_saveSettings(NULL);
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getClipboardFileTransfersEnabled(BOOL *aEnabled)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 *aEnabled = mHWData->mClipboardFileTransfersEnabled;
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::setClipboardFileTransfersEnabled(BOOL aEnabled)
2610{
2611 HRESULT rc = S_OK;
2612
2613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2614
2615 alock.release();
2616 rc = i_onClipboardFileTransferModeChange(aEnabled);
2617 alock.acquire();
2618 if (FAILED(rc)) return rc;
2619
2620 i_setModified(IsModified_MachineData);
2621 mHWData.backup();
2622 mHWData->mClipboardFileTransfersEnabled = aEnabled;
2623
2624 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2625 if (Global::IsOnline(mData->mMachineState))
2626 i_saveSettings(NULL);
2627
2628 return S_OK;
2629}
2630
2631HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2632{
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 *aDnDMode = mHWData->mDnDMode;
2636
2637 return S_OK;
2638}
2639
2640HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2641{
2642 HRESULT rc = S_OK;
2643
2644 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2645
2646 alock.release();
2647 rc = i_onDnDModeChange(aDnDMode);
2648
2649 alock.acquire();
2650 if (FAILED(rc)) return rc;
2651
2652 i_setModified(IsModified_MachineData);
2653 mHWData.backup();
2654 mHWData->mDnDMode = aDnDMode;
2655
2656 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2657 if (Global::IsOnline(mData->mMachineState))
2658 i_saveSettings(NULL);
2659
2660 return S_OK;
2661}
2662
2663HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2664{
2665 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2666
2667 aStorageControllers.resize(mStorageControllers->size());
2668 size_t i = 0;
2669 for (StorageControllerList::const_iterator
2670 it = mStorageControllers->begin();
2671 it != mStorageControllers->end();
2672 ++it, ++i)
2673 aStorageControllers[i] = *it;
2674
2675 return S_OK;
2676}
2677
2678HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2679{
2680 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2681
2682 *aEnabled = mUserData->s.fTeleporterEnabled;
2683
2684 return S_OK;
2685}
2686
2687HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2688{
2689 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2690
2691 /* Only allow it to be set to true when PoweredOff or Aborted.
2692 (Clearing it is always permitted.) */
2693 if ( aTeleporterEnabled
2694 && mData->mRegistered
2695 && ( !i_isSessionMachine()
2696 || ( mData->mMachineState != MachineState_PoweredOff
2697 && mData->mMachineState != MachineState_Teleported
2698 && mData->mMachineState != MachineState_Aborted
2699 )
2700 )
2701 )
2702 return setError(VBOX_E_INVALID_VM_STATE,
2703 tr("The machine is not powered off (state is %s)"),
2704 Global::stringifyMachineState(mData->mMachineState));
2705
2706 i_setModified(IsModified_MachineData);
2707 mUserData.backup();
2708 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2709
2710 return S_OK;
2711}
2712
2713HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2714{
2715 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2716
2717 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2718
2719 return S_OK;
2720}
2721
2722HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2723{
2724 if (aTeleporterPort >= _64K)
2725 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2726
2727 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2728
2729 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2730 if (FAILED(rc)) return rc;
2731
2732 i_setModified(IsModified_MachineData);
2733 mUserData.backup();
2734 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2735
2736 return S_OK;
2737}
2738
2739HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2740{
2741 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2742
2743 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2744
2745 return S_OK;
2746}
2747
2748HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2749{
2750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2751
2752 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2753 if (FAILED(rc)) return rc;
2754
2755 i_setModified(IsModified_MachineData);
2756 mUserData.backup();
2757 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2758
2759 return S_OK;
2760}
2761
2762HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2763{
2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2765 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2771{
2772 /*
2773 * Hash the password first.
2774 */
2775 com::Utf8Str aT = aTeleporterPassword;
2776
2777 if (!aT.isEmpty())
2778 {
2779 if (VBoxIsPasswordHashed(&aT))
2780 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2781 VBoxHashPassword(&aT);
2782 }
2783
2784 /*
2785 * Do the update.
2786 */
2787 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2788 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2789 if (SUCCEEDED(hrc))
2790 {
2791 i_setModified(IsModified_MachineData);
2792 mUserData.backup();
2793 mUserData->s.strTeleporterPassword = aT;
2794 }
2795
2796 return hrc;
2797}
2798
2799HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2800{
2801 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2802
2803 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2804
2805 return S_OK;
2806}
2807
2808HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2809{
2810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2811
2812 /* Only allow it to be set to true when PoweredOff or Aborted.
2813 (Clearing it is always permitted.) */
2814 if ( aRTCUseUTC
2815 && mData->mRegistered
2816 && ( !i_isSessionMachine()
2817 || ( mData->mMachineState != MachineState_PoweredOff
2818 && mData->mMachineState != MachineState_Teleported
2819 && mData->mMachineState != MachineState_Aborted
2820 )
2821 )
2822 )
2823 return setError(VBOX_E_INVALID_VM_STATE,
2824 tr("The machine is not powered off (state is %s)"),
2825 Global::stringifyMachineState(mData->mMachineState));
2826
2827 i_setModified(IsModified_MachineData);
2828 mUserData.backup();
2829 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2830
2831 return S_OK;
2832}
2833
2834HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2835{
2836 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2837
2838 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2839
2840 return S_OK;
2841}
2842
2843HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2844{
2845 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2846
2847 HRESULT rc = i_checkStateDependency(MutableStateDep);
2848 if (FAILED(rc)) return rc;
2849
2850 i_setModified(IsModified_MachineData);
2851 mHWData.backup();
2852 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2853
2854 return S_OK;
2855}
2856
2857HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2858{
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 *aIOCacheSize = mHWData->mIOCacheSize;
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2867{
2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 HRESULT rc = i_checkStateDependency(MutableStateDep);
2871 if (FAILED(rc)) return rc;
2872
2873 i_setModified(IsModified_MachineData);
2874 mHWData.backup();
2875 mHWData->mIOCacheSize = aIOCacheSize;
2876
2877 return S_OK;
2878}
2879
2880
2881/**
2882 * @note Locks objects!
2883 */
2884HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2885 LockType_T aLockType)
2886{
2887 /* check the session state */
2888 SessionState_T state;
2889 HRESULT rc = aSession->COMGETTER(State)(&state);
2890 if (FAILED(rc)) return rc;
2891
2892 if (state != SessionState_Unlocked)
2893 return setError(VBOX_E_INVALID_OBJECT_STATE,
2894 tr("The given session is busy"));
2895
2896 // get the client's IInternalSessionControl interface
2897 ComPtr<IInternalSessionControl> pSessionControl = aSession;
2898 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
2899 E_INVALIDARG);
2900
2901 // session name (only used in some code paths)
2902 Utf8Str strSessionName;
2903
2904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2905
2906 if (!mData->mRegistered)
2907 return setError(E_UNEXPECTED,
2908 tr("The machine '%s' is not registered"),
2909 mUserData->s.strName.c_str());
2910
2911 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2912
2913 SessionState_T oldState = mData->mSession.mState;
2914 /* Hack: in case the session is closing and there is a progress object
2915 * which allows waiting for the session to be closed, take the opportunity
2916 * and do a limited wait (max. 1 second). This helps a lot when the system
2917 * is busy and thus session closing can take a little while. */
2918 if ( mData->mSession.mState == SessionState_Unlocking
2919 && mData->mSession.mProgress)
2920 {
2921 alock.release();
2922 mData->mSession.mProgress->WaitForCompletion(1000);
2923 alock.acquire();
2924 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
2925 }
2926
2927 // try again now
2928 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
2929 // (i.e. session machine exists)
2930 && (aLockType == LockType_Shared) // caller wants a shared link to the
2931 // existing session that holds the write lock:
2932 )
2933 {
2934 // OK, share the session... we are now dealing with three processes:
2935 // 1) VBoxSVC (where this code runs);
2936 // 2) process C: the caller's client process (who wants a shared session);
2937 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
2938
2939 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
2940 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
2941 ComAssertRet(!pSessionW.isNull(), E_FAIL);
2942 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
2943 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
2944
2945 /*
2946 * Release the lock before calling the client process. It's safe here
2947 * since the only thing to do after we get the lock again is to add
2948 * the remote control to the list (which doesn't directly influence
2949 * anything).
2950 */
2951 alock.release();
2952
2953 // get the console of the session holding the write lock (this is a remote call)
2954 ComPtr<IConsole> pConsoleW;
2955 if (mData->mSession.mLockType == LockType_VM)
2956 {
2957 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
2958 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
2959 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
2960 if (FAILED(rc))
2961 // the failure may occur w/o any error info (from RPC), so provide one
2962 return setError(VBOX_E_VM_ERROR,
2963 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
2964 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
2965 }
2966
2967 // share the session machine and W's console with the caller's session
2968 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
2969 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
2970 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
2971
2972 if (FAILED(rc))
2973 // the failure may occur w/o any error info (from RPC), so provide one
2974 return setError(VBOX_E_VM_ERROR,
2975 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
2976 alock.acquire();
2977
2978 // need to revalidate the state after acquiring the lock again
2979 if (mData->mSession.mState != SessionState_Locked)
2980 {
2981 pSessionControl->Uninitialize();
2982 return setError(VBOX_E_INVALID_SESSION_STATE,
2983 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
2984 mUserData->s.strName.c_str());
2985 }
2986
2987 // add the caller's session to the list
2988 mData->mSession.mRemoteControls.push_back(pSessionControl);
2989 }
2990 else if ( mData->mSession.mState == SessionState_Locked
2991 || mData->mSession.mState == SessionState_Unlocking
2992 )
2993 {
2994 // sharing not permitted, or machine still unlocking:
2995 return setError(VBOX_E_INVALID_OBJECT_STATE,
2996 tr("The machine '%s' is already locked for a session (or being unlocked)"),
2997 mUserData->s.strName.c_str());
2998 }
2999 else
3000 {
3001 // machine is not locked: then write-lock the machine (create the session machine)
3002
3003 // must not be busy
3004 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3005
3006 // get the caller's session PID
3007 RTPROCESS pid = NIL_RTPROCESS;
3008 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3009 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3010 Assert(pid != NIL_RTPROCESS);
3011
3012 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3013
3014 if (fLaunchingVMProcess)
3015 {
3016 if (mData->mSession.mPID == NIL_RTPROCESS)
3017 {
3018 // two or more clients racing for a lock, the one which set the
3019 // session state to Spawning will win, the others will get an
3020 // error as we can't decide here if waiting a little would help
3021 // (only for shared locks this would avoid an error)
3022 return setError(VBOX_E_INVALID_OBJECT_STATE,
3023 tr("The machine '%s' already has a lock request pending"),
3024 mUserData->s.strName.c_str());
3025 }
3026
3027 // this machine is awaiting for a spawning session to be opened:
3028 // then the calling process must be the one that got started by
3029 // LaunchVMProcess()
3030
3031 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3032 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3033
3034#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3035 /* Hardened windows builds spawns three processes when a VM is
3036 launched, the 3rd one is the one that will end up here. */
3037 RTPROCESS pidParent;
3038 int vrc = RTProcQueryParent(pid, &pidParent);
3039 if (RT_SUCCESS(vrc))
3040 vrc = RTProcQueryParent(pidParent, &pidParent);
3041 if ( (RT_SUCCESS(vrc) && mData->mSession.mPID == pidParent)
3042 || vrc == VERR_ACCESS_DENIED)
3043 {
3044 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3045 mData->mSession.mPID = pid;
3046 }
3047#endif
3048
3049 if (mData->mSession.mPID != pid)
3050 return setError(E_ACCESSDENIED,
3051 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3052 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3053 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3054 }
3055
3056 // create the mutable SessionMachine from the current machine
3057 ComObjPtr<SessionMachine> sessionMachine;
3058 sessionMachine.createObject();
3059 rc = sessionMachine->init(this);
3060 AssertComRC(rc);
3061
3062 /* NOTE: doing return from this function after this point but
3063 * before the end is forbidden since it may call SessionMachine::uninit()
3064 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3065 * lock while still holding the Machine lock in alock so that a deadlock
3066 * is possible due to the wrong lock order. */
3067
3068 if (SUCCEEDED(rc))
3069 {
3070 /*
3071 * Set the session state to Spawning to protect against subsequent
3072 * attempts to open a session and to unregister the machine after
3073 * we release the lock.
3074 */
3075 SessionState_T origState = mData->mSession.mState;
3076 mData->mSession.mState = SessionState_Spawning;
3077
3078#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3079 /* Get the client token ID to be passed to the client process */
3080 Utf8Str strTokenId;
3081 sessionMachine->i_getTokenId(strTokenId);
3082 Assert(!strTokenId.isEmpty());
3083#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3084 /* Get the client token to be passed to the client process */
3085 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3086 /* The token is now "owned" by pToken, fix refcount */
3087 if (!pToken.isNull())
3088 pToken->Release();
3089#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3090
3091 /*
3092 * Release the lock before calling the client process -- it will call
3093 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3094 * because the state is Spawning, so that LaunchVMProcess() and
3095 * LockMachine() calls will fail. This method, called before we
3096 * acquire the lock again, will fail because of the wrong PID.
3097 *
3098 * Note that mData->mSession.mRemoteControls accessed outside
3099 * the lock may not be modified when state is Spawning, so it's safe.
3100 */
3101 alock.release();
3102
3103 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3104#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3105 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3106#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3107 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3108 /* Now the token is owned by the client process. */
3109 pToken.setNull();
3110#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3111 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3112
3113 /* The failure may occur w/o any error info (from RPC), so provide one */
3114 if (FAILED(rc))
3115 setError(VBOX_E_VM_ERROR,
3116 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3117
3118 // get session name, either to remember or to compare against
3119 // the already known session name.
3120 {
3121 Bstr bstrSessionName;
3122 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3123 if (SUCCEEDED(rc2))
3124 strSessionName = bstrSessionName;
3125 }
3126
3127 if ( SUCCEEDED(rc)
3128 && fLaunchingVMProcess
3129 )
3130 {
3131 /* complete the remote session initialization */
3132
3133 /* get the console from the direct session */
3134 ComPtr<IConsole> console;
3135 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3136 ComAssertComRC(rc);
3137
3138 if (SUCCEEDED(rc) && !console)
3139 {
3140 ComAssert(!!console);
3141 rc = E_FAIL;
3142 }
3143
3144 /* assign machine & console to the remote session */
3145 if (SUCCEEDED(rc))
3146 {
3147 /*
3148 * after LaunchVMProcess(), the first and the only
3149 * entry in remoteControls is that remote session
3150 */
3151 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3152 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3153 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3154
3155 /* The failure may occur w/o any error info (from RPC), so provide one */
3156 if (FAILED(rc))
3157 setError(VBOX_E_VM_ERROR,
3158 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3159 }
3160
3161 if (FAILED(rc))
3162 pSessionControl->Uninitialize();
3163 }
3164
3165 /* acquire the lock again */
3166 alock.acquire();
3167
3168 /* Restore the session state */
3169 mData->mSession.mState = origState;
3170 }
3171
3172 // finalize spawning anyway (this is why we don't return on errors above)
3173 if (fLaunchingVMProcess)
3174 {
3175 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3176 /* Note that the progress object is finalized later */
3177 /** @todo Consider checking mData->mSession.mProgress for cancellation
3178 * around here. */
3179
3180 /* We don't reset mSession.mPID here because it is necessary for
3181 * SessionMachine::uninit() to reap the child process later. */
3182
3183 if (FAILED(rc))
3184 {
3185 /* Close the remote session, remove the remote control from the list
3186 * and reset session state to Closed (@note keep the code in sync
3187 * with the relevant part in checkForSpawnFailure()). */
3188
3189 Assert(mData->mSession.mRemoteControls.size() == 1);
3190 if (mData->mSession.mRemoteControls.size() == 1)
3191 {
3192 ErrorInfoKeeper eik;
3193 mData->mSession.mRemoteControls.front()->Uninitialize();
3194 }
3195
3196 mData->mSession.mRemoteControls.clear();
3197 mData->mSession.mState = SessionState_Unlocked;
3198 }
3199 }
3200 else
3201 {
3202 /* memorize PID of the directly opened session */
3203 if (SUCCEEDED(rc))
3204 mData->mSession.mPID = pid;
3205 }
3206
3207 if (SUCCEEDED(rc))
3208 {
3209 mData->mSession.mLockType = aLockType;
3210 /* memorize the direct session control and cache IUnknown for it */
3211 mData->mSession.mDirectControl = pSessionControl;
3212 mData->mSession.mState = SessionState_Locked;
3213 if (!fLaunchingVMProcess)
3214 mData->mSession.mName = strSessionName;
3215 /* associate the SessionMachine with this Machine */
3216 mData->mSession.mMachine = sessionMachine;
3217
3218 /* request an IUnknown pointer early from the remote party for later
3219 * identity checks (it will be internally cached within mDirectControl
3220 * at least on XPCOM) */
3221 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3222 NOREF(unk);
3223 }
3224
3225 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3226 * would break the lock order */
3227 alock.release();
3228
3229 /* uninitialize the created session machine on failure */
3230 if (FAILED(rc))
3231 sessionMachine->uninit();
3232 }
3233
3234 if (SUCCEEDED(rc))
3235 {
3236 /*
3237 * tell the client watcher thread to update the set of
3238 * machines that have open sessions
3239 */
3240 mParent->i_updateClientWatcher();
3241
3242 if (oldState != SessionState_Locked)
3243 /* fire an event */
3244 mParent->i_onSessionStateChanged(i_getId(), SessionState_Locked);
3245 }
3246
3247 return rc;
3248}
3249
3250/**
3251 * @note Locks objects!
3252 */
3253HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3254 const com::Utf8Str &aName,
3255 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3256 ComPtr<IProgress> &aProgress)
3257{
3258 Utf8Str strFrontend(aName);
3259 /* "emergencystop" doesn't need the session, so skip the checks/interface
3260 * retrieval. This code doesn't quite fit in here, but introducing a
3261 * special API method would be even more effort, and would require explicit
3262 * support by every API client. It's better to hide the feature a bit. */
3263 if (strFrontend != "emergencystop")
3264 CheckComArgNotNull(aSession);
3265
3266 HRESULT rc = S_OK;
3267 if (strFrontend.isEmpty())
3268 {
3269 Bstr bstrFrontend;
3270 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3271 if (FAILED(rc))
3272 return rc;
3273 strFrontend = bstrFrontend;
3274 if (strFrontend.isEmpty())
3275 {
3276 ComPtr<ISystemProperties> systemProperties;
3277 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3278 if (FAILED(rc))
3279 return rc;
3280 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3281 if (FAILED(rc))
3282 return rc;
3283 strFrontend = bstrFrontend;
3284 }
3285 /* paranoia - emergencystop is not a valid default */
3286 if (strFrontend == "emergencystop")
3287 strFrontend = Utf8Str::Empty;
3288 }
3289 /* default frontend: Qt GUI */
3290 if (strFrontend.isEmpty())
3291 strFrontend = "GUI/Qt";
3292
3293 if (strFrontend != "emergencystop")
3294 {
3295 /* check the session state */
3296 SessionState_T state;
3297 rc = aSession->COMGETTER(State)(&state);
3298 if (FAILED(rc))
3299 return rc;
3300
3301 if (state != SessionState_Unlocked)
3302 return setError(VBOX_E_INVALID_OBJECT_STATE,
3303 tr("The given session is busy"));
3304
3305 /* get the IInternalSessionControl interface */
3306 ComPtr<IInternalSessionControl> control(aSession);
3307 ComAssertMsgRet(!control.isNull(),
3308 ("No IInternalSessionControl interface"),
3309 E_INVALIDARG);
3310
3311 /* get the teleporter enable state for the progress object init. */
3312 BOOL fTeleporterEnabled;
3313 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3314 if (FAILED(rc))
3315 return rc;
3316
3317 /* create a progress object */
3318 ComObjPtr<ProgressProxy> progress;
3319 progress.createObject();
3320 rc = progress->init(mParent,
3321 static_cast<IMachine*>(this),
3322 Bstr(tr("Starting VM")).raw(),
3323 TRUE /* aCancelable */,
3324 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3325 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3326 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3327 2 /* uFirstOperationWeight */,
3328 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3329
3330 if (SUCCEEDED(rc))
3331 {
3332 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3333 if (SUCCEEDED(rc))
3334 {
3335 aProgress = progress;
3336
3337 /* signal the client watcher thread */
3338 mParent->i_updateClientWatcher();
3339
3340 /* fire an event */
3341 mParent->i_onSessionStateChanged(i_getId(), SessionState_Spawning);
3342 }
3343 }
3344 }
3345 else
3346 {
3347 /* no progress object - either instant success or failure */
3348 aProgress = NULL;
3349
3350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3351
3352 if (mData->mSession.mState != SessionState_Locked)
3353 return setError(VBOX_E_INVALID_OBJECT_STATE,
3354 tr("The machine '%s' is not locked by a session"),
3355 mUserData->s.strName.c_str());
3356
3357 /* must have a VM process associated - do not kill normal API clients
3358 * with an open session */
3359 if (!Global::IsOnline(mData->mMachineState))
3360 return setError(VBOX_E_INVALID_OBJECT_STATE,
3361 tr("The machine '%s' does not have a VM process"),
3362 mUserData->s.strName.c_str());
3363
3364 /* forcibly terminate the VM process */
3365 if (mData->mSession.mPID != NIL_RTPROCESS)
3366 RTProcTerminate(mData->mSession.mPID);
3367
3368 /* signal the client watcher thread, as most likely the client has
3369 * been terminated */
3370 mParent->i_updateClientWatcher();
3371 }
3372
3373 return rc;
3374}
3375
3376HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3377{
3378 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3379 return setError(E_INVALIDARG,
3380 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3381 aPosition, SchemaDefs::MaxBootPosition);
3382
3383 if (aDevice == DeviceType_USB)
3384 return setError(E_NOTIMPL,
3385 tr("Booting from USB device is currently not supported"));
3386
3387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3388
3389 HRESULT rc = i_checkStateDependency(MutableStateDep);
3390 if (FAILED(rc)) return rc;
3391
3392 i_setModified(IsModified_MachineData);
3393 mHWData.backup();
3394 mHWData->mBootOrder[aPosition - 1] = aDevice;
3395
3396 return S_OK;
3397}
3398
3399HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3400{
3401 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3402 return setError(E_INVALIDARG,
3403 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3404 aPosition, SchemaDefs::MaxBootPosition);
3405
3406 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3407
3408 *aDevice = mHWData->mBootOrder[aPosition - 1];
3409
3410 return S_OK;
3411}
3412
3413HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3414 LONG aControllerPort,
3415 LONG aDevice,
3416 DeviceType_T aType,
3417 const ComPtr<IMedium> &aMedium)
3418{
3419 IMedium *aM = aMedium;
3420 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3421 aName.c_str(), aControllerPort, aDevice, aType, aM));
3422
3423 // request the host lock first, since might be calling Host methods for getting host drives;
3424 // next, protect the media tree all the while we're in here, as well as our member variables
3425 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3426 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3427
3428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3429 if (FAILED(rc)) return rc;
3430
3431 /// @todo NEWMEDIA implicit machine registration
3432 if (!mData->mRegistered)
3433 return setError(VBOX_E_INVALID_OBJECT_STATE,
3434 tr("Cannot attach storage devices to an unregistered machine"));
3435
3436 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3437
3438 /* Check for an existing controller. */
3439 ComObjPtr<StorageController> ctl;
3440 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3441 if (FAILED(rc)) return rc;
3442
3443 StorageControllerType_T ctrlType;
3444 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3445 if (FAILED(rc))
3446 return setError(E_FAIL,
3447 tr("Could not get type of controller '%s'"),
3448 aName.c_str());
3449
3450 bool fSilent = false;
3451 Utf8Str strReconfig;
3452
3453 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3454 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3455 if ( mData->mMachineState == MachineState_Paused
3456 && strReconfig == "1")
3457 fSilent = true;
3458
3459 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3460 bool fHotplug = false;
3461 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3462 fHotplug = true;
3463
3464 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3465 return setError(VBOX_E_INVALID_VM_STATE,
3466 tr("Controller '%s' does not support hotplugging"),
3467 aName.c_str());
3468
3469 // check that the port and device are not out of range
3470 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3471 if (FAILED(rc)) return rc;
3472
3473 /* check if the device slot is already busy */
3474 MediumAttachment *pAttachTemp;
3475 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3476 aName,
3477 aControllerPort,
3478 aDevice)))
3479 {
3480 Medium *pMedium = pAttachTemp->i_getMedium();
3481 if (pMedium)
3482 {
3483 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3484 return setError(VBOX_E_OBJECT_IN_USE,
3485 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3486 pMedium->i_getLocationFull().c_str(),
3487 aControllerPort,
3488 aDevice,
3489 aName.c_str());
3490 }
3491 else
3492 return setError(VBOX_E_OBJECT_IN_USE,
3493 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3494 aControllerPort, aDevice, aName.c_str());
3495 }
3496
3497 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3498 if (aMedium && medium.isNull())
3499 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3500
3501 AutoCaller mediumCaller(medium);
3502 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3503
3504 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3505
3506 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3507 && !medium.isNull()
3508 )
3509 return setError(VBOX_E_OBJECT_IN_USE,
3510 tr("Medium '%s' is already attached to this virtual machine"),
3511 medium->i_getLocationFull().c_str());
3512
3513 if (!medium.isNull())
3514 {
3515 MediumType_T mtype = medium->i_getType();
3516 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3517 // For DVDs it's not written to the config file, so needs no global config
3518 // version bump. For floppies it's a new attribute "type", which is ignored
3519 // by older VirtualBox version, so needs no global config version bump either.
3520 // For hard disks this type is not accepted.
3521 if (mtype == MediumType_MultiAttach)
3522 {
3523 // This type is new with VirtualBox 4.0 and therefore requires settings
3524 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3525 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3526 // two reasons: The medium type is a property of the media registry tree, which
3527 // can reside in the global config file (for pre-4.0 media); we would therefore
3528 // possibly need to bump the global config version. We don't want to do that though
3529 // because that might make downgrading to pre-4.0 impossible.
3530 // As a result, we can only use these two new types if the medium is NOT in the
3531 // global registry:
3532 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3533 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3534 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3535 )
3536 return setError(VBOX_E_INVALID_OBJECT_STATE,
3537 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3538 "to machines that were created with VirtualBox 4.0 or later"),
3539 medium->i_getLocationFull().c_str());
3540 }
3541 }
3542
3543 bool fIndirect = false;
3544 if (!medium.isNull())
3545 fIndirect = medium->i_isReadOnly();
3546 bool associate = true;
3547
3548 do
3549 {
3550 if ( aType == DeviceType_HardDisk
3551 && mMediumAttachments.isBackedUp())
3552 {
3553 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3554
3555 /* check if the medium was attached to the VM before we started
3556 * changing attachments in which case the attachment just needs to
3557 * be restored */
3558 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3559 {
3560 AssertReturn(!fIndirect, E_FAIL);
3561
3562 /* see if it's the same bus/channel/device */
3563 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3564 {
3565 /* the simplest case: restore the whole attachment
3566 * and return, nothing else to do */
3567 mMediumAttachments->push_back(pAttachTemp);
3568
3569 /* Reattach the medium to the VM. */
3570 if (fHotplug || fSilent)
3571 {
3572 mediumLock.release();
3573 treeLock.release();
3574 alock.release();
3575
3576 MediumLockList *pMediumLockList(new MediumLockList());
3577
3578 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3579 medium /* pToLockWrite */,
3580 false /* fMediumLockWriteAll */,
3581 NULL,
3582 *pMediumLockList);
3583 alock.acquire();
3584 if (FAILED(rc))
3585 delete pMediumLockList;
3586 else
3587 {
3588 mData->mSession.mLockedMedia.Unlock();
3589 alock.release();
3590 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3591 mData->mSession.mLockedMedia.Lock();
3592 alock.acquire();
3593 }
3594 alock.release();
3595
3596 if (SUCCEEDED(rc))
3597 {
3598 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3599 /* Remove lock list in case of error. */
3600 if (FAILED(rc))
3601 {
3602 mData->mSession.mLockedMedia.Unlock();
3603 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3604 mData->mSession.mLockedMedia.Lock();
3605 }
3606 }
3607 }
3608
3609 return S_OK;
3610 }
3611
3612 /* bus/channel/device differ; we need a new attachment object,
3613 * but don't try to associate it again */
3614 associate = false;
3615 break;
3616 }
3617 }
3618
3619 /* go further only if the attachment is to be indirect */
3620 if (!fIndirect)
3621 break;
3622
3623 /* perform the so called smart attachment logic for indirect
3624 * attachments. Note that smart attachment is only applicable to base
3625 * hard disks. */
3626
3627 if (medium->i_getParent().isNull())
3628 {
3629 /* first, investigate the backup copy of the current hard disk
3630 * attachments to make it possible to re-attach existing diffs to
3631 * another device slot w/o losing their contents */
3632 if (mMediumAttachments.isBackedUp())
3633 {
3634 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3635
3636 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3637 uint32_t foundLevel = 0;
3638
3639 for (MediumAttachmentList::const_iterator
3640 it = oldAtts.begin();
3641 it != oldAtts.end();
3642 ++it)
3643 {
3644 uint32_t level = 0;
3645 MediumAttachment *pAttach = *it;
3646 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3647 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3648 if (pMedium.isNull())
3649 continue;
3650
3651 if (pMedium->i_getBase(&level) == medium)
3652 {
3653 /* skip the hard disk if its currently attached (we
3654 * cannot attach the same hard disk twice) */
3655 if (i_findAttachment(*mMediumAttachments.data(),
3656 pMedium))
3657 continue;
3658
3659 /* matched device, channel and bus (i.e. attached to the
3660 * same place) will win and immediately stop the search;
3661 * otherwise the attachment that has the youngest
3662 * descendant of medium will be used
3663 */
3664 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3665 {
3666 /* the simplest case: restore the whole attachment
3667 * and return, nothing else to do */
3668 mMediumAttachments->push_back(*it);
3669
3670 /* Reattach the medium to the VM. */
3671 if (fHotplug || fSilent)
3672 {
3673 mediumLock.release();
3674 treeLock.release();
3675 alock.release();
3676
3677 MediumLockList *pMediumLockList(new MediumLockList());
3678
3679 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3680 medium /* pToLockWrite */,
3681 false /* fMediumLockWriteAll */,
3682 NULL,
3683 *pMediumLockList);
3684 alock.acquire();
3685 if (FAILED(rc))
3686 delete pMediumLockList;
3687 else
3688 {
3689 mData->mSession.mLockedMedia.Unlock();
3690 alock.release();
3691 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3692 mData->mSession.mLockedMedia.Lock();
3693 alock.acquire();
3694 }
3695 alock.release();
3696
3697 if (SUCCEEDED(rc))
3698 {
3699 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3700 /* Remove lock list in case of error. */
3701 if (FAILED(rc))
3702 {
3703 mData->mSession.mLockedMedia.Unlock();
3704 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3705 mData->mSession.mLockedMedia.Lock();
3706 }
3707 }
3708 }
3709
3710 return S_OK;
3711 }
3712 else if ( foundIt == oldAtts.end()
3713 || level > foundLevel /* prefer younger */
3714 )
3715 {
3716 foundIt = it;
3717 foundLevel = level;
3718 }
3719 }
3720 }
3721
3722 if (foundIt != oldAtts.end())
3723 {
3724 /* use the previously attached hard disk */
3725 medium = (*foundIt)->i_getMedium();
3726 mediumCaller.attach(medium);
3727 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3728 mediumLock.attach(medium);
3729 /* not implicit, doesn't require association with this VM */
3730 fIndirect = false;
3731 associate = false;
3732 /* go right to the MediumAttachment creation */
3733 break;
3734 }
3735 }
3736
3737 /* must give up the medium lock and medium tree lock as below we
3738 * go over snapshots, which needs a lock with higher lock order. */
3739 mediumLock.release();
3740 treeLock.release();
3741
3742 /* then, search through snapshots for the best diff in the given
3743 * hard disk's chain to base the new diff on */
3744
3745 ComObjPtr<Medium> base;
3746 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3747 while (snap)
3748 {
3749 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3750
3751 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3752
3753 MediumAttachment *pAttachFound = NULL;
3754 uint32_t foundLevel = 0;
3755
3756 for (MediumAttachmentList::const_iterator
3757 it = snapAtts.begin();
3758 it != snapAtts.end();
3759 ++it)
3760 {
3761 MediumAttachment *pAttach = *it;
3762 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3763 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3764 if (pMedium.isNull())
3765 continue;
3766
3767 uint32_t level = 0;
3768 if (pMedium->i_getBase(&level) == medium)
3769 {
3770 /* matched device, channel and bus (i.e. attached to the
3771 * same place) will win and immediately stop the search;
3772 * otherwise the attachment that has the youngest
3773 * descendant of medium will be used
3774 */
3775 if ( pAttach->i_getDevice() == aDevice
3776 && pAttach->i_getPort() == aControllerPort
3777 && pAttach->i_getControllerName() == aName
3778 )
3779 {
3780 pAttachFound = pAttach;
3781 break;
3782 }
3783 else if ( !pAttachFound
3784 || level > foundLevel /* prefer younger */
3785 )
3786 {
3787 pAttachFound = pAttach;
3788 foundLevel = level;
3789 }
3790 }
3791 }
3792
3793 if (pAttachFound)
3794 {
3795 base = pAttachFound->i_getMedium();
3796 break;
3797 }
3798
3799 snap = snap->i_getParent();
3800 }
3801
3802 /* re-lock medium tree and the medium, as we need it below */
3803 treeLock.acquire();
3804 mediumLock.acquire();
3805
3806 /* found a suitable diff, use it as a base */
3807 if (!base.isNull())
3808 {
3809 medium = base;
3810 mediumCaller.attach(medium);
3811 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3812 mediumLock.attach(medium);
3813 }
3814 }
3815
3816 Utf8Str strFullSnapshotFolder;
3817 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3818
3819 ComObjPtr<Medium> diff;
3820 diff.createObject();
3821 // store this diff in the same registry as the parent
3822 Guid uuidRegistryParent;
3823 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3824 {
3825 // parent image has no registry: this can happen if we're attaching a new immutable
3826 // image that has not yet been attached (medium then points to the base and we're
3827 // creating the diff image for the immutable, and the parent is not yet registered);
3828 // put the parent in the machine registry then
3829 mediumLock.release();
3830 treeLock.release();
3831 alock.release();
3832 i_addMediumToRegistry(medium);
3833 alock.acquire();
3834 treeLock.acquire();
3835 mediumLock.acquire();
3836 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3837 }
3838 rc = diff->init(mParent,
3839 medium->i_getPreferredDiffFormat(),
3840 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3841 uuidRegistryParent,
3842 DeviceType_HardDisk);
3843 if (FAILED(rc)) return rc;
3844
3845 /* Apply the normal locking logic to the entire chain. */
3846 MediumLockList *pMediumLockList(new MediumLockList());
3847 mediumLock.release();
3848 treeLock.release();
3849 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3850 diff /* pToLockWrite */,
3851 false /* fMediumLockWriteAll */,
3852 medium,
3853 *pMediumLockList);
3854 treeLock.acquire();
3855 mediumLock.acquire();
3856 if (SUCCEEDED(rc))
3857 {
3858 mediumLock.release();
3859 treeLock.release();
3860 rc = pMediumLockList->Lock();
3861 treeLock.acquire();
3862 mediumLock.acquire();
3863 if (FAILED(rc))
3864 setError(rc,
3865 tr("Could not lock medium when creating diff '%s'"),
3866 diff->i_getLocationFull().c_str());
3867 else
3868 {
3869 /* will release the lock before the potentially lengthy
3870 * operation, so protect with the special state */
3871 MachineState_T oldState = mData->mMachineState;
3872 i_setMachineState(MachineState_SettingUp);
3873
3874 mediumLock.release();
3875 treeLock.release();
3876 alock.release();
3877
3878 rc = medium->i_createDiffStorage(diff,
3879 medium->i_getPreferredDiffVariant(),
3880 pMediumLockList,
3881 NULL /* aProgress */,
3882 true /* aWait */,
3883 false /* aNotify */);
3884
3885 alock.acquire();
3886 treeLock.acquire();
3887 mediumLock.acquire();
3888
3889 i_setMachineState(oldState);
3890 }
3891 }
3892
3893 /* Unlock the media and free the associated memory. */
3894 delete pMediumLockList;
3895
3896 if (FAILED(rc)) return rc;
3897
3898 /* use the created diff for the actual attachment */
3899 medium = diff;
3900 mediumCaller.attach(medium);
3901 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3902 mediumLock.attach(medium);
3903 }
3904 while (0);
3905
3906 ComObjPtr<MediumAttachment> attachment;
3907 attachment.createObject();
3908 rc = attachment->init(this,
3909 medium,
3910 aName,
3911 aControllerPort,
3912 aDevice,
3913 aType,
3914 fIndirect,
3915 false /* fPassthrough */,
3916 false /* fTempEject */,
3917 false /* fNonRotational */,
3918 false /* fDiscard */,
3919 fHotplug /* fHotPluggable */,
3920 Utf8Str::Empty);
3921 if (FAILED(rc)) return rc;
3922
3923 if (associate && !medium.isNull())
3924 {
3925 // as the last step, associate the medium to the VM
3926 rc = medium->i_addBackReference(mData->mUuid);
3927 // here we can fail because of Deleting, or being in process of creating a Diff
3928 if (FAILED(rc)) return rc;
3929
3930 mediumLock.release();
3931 treeLock.release();
3932 alock.release();
3933 i_addMediumToRegistry(medium);
3934 alock.acquire();
3935 treeLock.acquire();
3936 mediumLock.acquire();
3937 }
3938
3939 /* success: finally remember the attachment */
3940 i_setModified(IsModified_Storage);
3941 mMediumAttachments.backup();
3942 mMediumAttachments->push_back(attachment);
3943
3944 mediumLock.release();
3945 treeLock.release();
3946 alock.release();
3947
3948 if (fHotplug || fSilent)
3949 {
3950 if (!medium.isNull())
3951 {
3952 MediumLockList *pMediumLockList(new MediumLockList());
3953
3954 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3955 medium /* pToLockWrite */,
3956 false /* fMediumLockWriteAll */,
3957 NULL,
3958 *pMediumLockList);
3959 alock.acquire();
3960 if (FAILED(rc))
3961 delete pMediumLockList;
3962 else
3963 {
3964 mData->mSession.mLockedMedia.Unlock();
3965 alock.release();
3966 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
3967 mData->mSession.mLockedMedia.Lock();
3968 alock.acquire();
3969 }
3970 alock.release();
3971 }
3972
3973 if (SUCCEEDED(rc))
3974 {
3975 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
3976 /* Remove lock list in case of error. */
3977 if (FAILED(rc))
3978 {
3979 mData->mSession.mLockedMedia.Unlock();
3980 mData->mSession.mLockedMedia.Remove(attachment);
3981 mData->mSession.mLockedMedia.Lock();
3982 }
3983 }
3984 }
3985
3986 /* Save modified registries, but skip this machine as it's the caller's
3987 * job to save its settings like all other settings changes. */
3988 mParent->i_unmarkRegistryModified(i_getId());
3989 mParent->i_saveModifiedRegistries();
3990
3991 if (SUCCEEDED(rc))
3992 {
3993 if (fIndirect && medium != aM)
3994 mParent->i_onMediumConfigChanged(medium);
3995 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
3996 }
3997
3998 return rc;
3999}
4000
4001HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4002 LONG aDevice)
4003{
4004 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4005 aName.c_str(), aControllerPort, aDevice));
4006
4007 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4008
4009 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4010 if (FAILED(rc)) return rc;
4011
4012 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4013
4014 /* Check for an existing controller. */
4015 ComObjPtr<StorageController> ctl;
4016 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4017 if (FAILED(rc)) return rc;
4018
4019 StorageControllerType_T ctrlType;
4020 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4021 if (FAILED(rc))
4022 return setError(E_FAIL,
4023 tr("Could not get type of controller '%s'"),
4024 aName.c_str());
4025
4026 bool fSilent = false;
4027 Utf8Str strReconfig;
4028
4029 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4030 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4031 if ( mData->mMachineState == MachineState_Paused
4032 && strReconfig == "1")
4033 fSilent = true;
4034
4035 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4036 bool fHotplug = false;
4037 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4038 fHotplug = true;
4039
4040 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4041 return setError(VBOX_E_INVALID_VM_STATE,
4042 tr("Controller '%s' does not support hotplugging"),
4043 aName.c_str());
4044
4045 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4046 aName,
4047 aControllerPort,
4048 aDevice);
4049 if (!pAttach)
4050 return setError(VBOX_E_OBJECT_NOT_FOUND,
4051 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4052 aDevice, aControllerPort, aName.c_str());
4053
4054 if (fHotplug && !pAttach->i_getHotPluggable())
4055 return setError(VBOX_E_NOT_SUPPORTED,
4056 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4057 aDevice, aControllerPort, aName.c_str());
4058
4059 /*
4060 * The VM has to detach the device before we delete any implicit diffs.
4061 * If this fails we can roll back without loosing data.
4062 */
4063 if (fHotplug || fSilent)
4064 {
4065 alock.release();
4066 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4067 alock.acquire();
4068 }
4069 if (FAILED(rc)) return rc;
4070
4071 /* If we are here everything went well and we can delete the implicit now. */
4072 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4073
4074 alock.release();
4075
4076 /* Save modified registries, but skip this machine as it's the caller's
4077 * job to save its settings like all other settings changes. */
4078 mParent->i_unmarkRegistryModified(i_getId());
4079 mParent->i_saveModifiedRegistries();
4080
4081 if (SUCCEEDED(rc))
4082 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4083
4084 return rc;
4085}
4086
4087HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4088 LONG aDevice, BOOL aPassthrough)
4089{
4090 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4091 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4092
4093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4094
4095 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4096 if (FAILED(rc)) return rc;
4097
4098 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4099
4100 /* Check for an existing controller. */
4101 ComObjPtr<StorageController> ctl;
4102 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4103 if (FAILED(rc)) return rc;
4104
4105 StorageControllerType_T ctrlType;
4106 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4107 if (FAILED(rc))
4108 return setError(E_FAIL,
4109 tr("Could not get type of controller '%s'"),
4110 aName.c_str());
4111
4112 bool fSilent = false;
4113 Utf8Str strReconfig;
4114
4115 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4116 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4117 if ( mData->mMachineState == MachineState_Paused
4118 && strReconfig == "1")
4119 fSilent = true;
4120
4121 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4122 bool fHotplug = false;
4123 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4124 fHotplug = true;
4125
4126 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4127 return setError(VBOX_E_INVALID_VM_STATE,
4128 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4129 aName.c_str());
4130
4131 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4132 aName,
4133 aControllerPort,
4134 aDevice);
4135 if (!pAttach)
4136 return setError(VBOX_E_OBJECT_NOT_FOUND,
4137 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4138 aDevice, aControllerPort, aName.c_str());
4139
4140
4141 i_setModified(IsModified_Storage);
4142 mMediumAttachments.backup();
4143
4144 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4145
4146 if (pAttach->i_getType() != DeviceType_DVD)
4147 return setError(E_INVALIDARG,
4148 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4149 aDevice, aControllerPort, aName.c_str());
4150
4151 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4152
4153 pAttach->i_updatePassthrough(!!aPassthrough);
4154
4155 attLock.release();
4156 alock.release();
4157 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4158 if (SUCCEEDED(rc) && fValueChanged)
4159 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4160
4161 return rc;
4162}
4163
4164HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4165 LONG aDevice, BOOL aTemporaryEject)
4166{
4167
4168 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4169 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4170
4171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4172
4173 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4174 if (FAILED(rc)) return rc;
4175
4176 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4177 aName,
4178 aControllerPort,
4179 aDevice);
4180 if (!pAttach)
4181 return setError(VBOX_E_OBJECT_NOT_FOUND,
4182 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4183 aDevice, aControllerPort, aName.c_str());
4184
4185
4186 i_setModified(IsModified_Storage);
4187 mMediumAttachments.backup();
4188
4189 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4190
4191 if (pAttach->i_getType() != DeviceType_DVD)
4192 return setError(E_INVALIDARG,
4193 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4194 aDevice, aControllerPort, aName.c_str());
4195 pAttach->i_updateTempEject(!!aTemporaryEject);
4196
4197 return S_OK;
4198}
4199
4200HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4201 LONG aDevice, BOOL aNonRotational)
4202{
4203
4204 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4205 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4206
4207 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4208
4209 HRESULT rc = i_checkStateDependency(MutableStateDep);
4210 if (FAILED(rc)) return rc;
4211
4212 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4213
4214 if (Global::IsOnlineOrTransient(mData->mMachineState))
4215 return setError(VBOX_E_INVALID_VM_STATE,
4216 tr("Invalid machine state: %s"),
4217 Global::stringifyMachineState(mData->mMachineState));
4218
4219 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4220 aName,
4221 aControllerPort,
4222 aDevice);
4223 if (!pAttach)
4224 return setError(VBOX_E_OBJECT_NOT_FOUND,
4225 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4226 aDevice, aControllerPort, aName.c_str());
4227
4228
4229 i_setModified(IsModified_Storage);
4230 mMediumAttachments.backup();
4231
4232 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4233
4234 if (pAttach->i_getType() != DeviceType_HardDisk)
4235 return setError(E_INVALIDARG,
4236 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"),
4237 aDevice, aControllerPort, aName.c_str());
4238 pAttach->i_updateNonRotational(!!aNonRotational);
4239
4240 return S_OK;
4241}
4242
4243HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4244 LONG aDevice, BOOL aDiscard)
4245{
4246
4247 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4248 aName.c_str(), aControllerPort, aDevice, aDiscard));
4249
4250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4251
4252 HRESULT rc = i_checkStateDependency(MutableStateDep);
4253 if (FAILED(rc)) return rc;
4254
4255 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4256
4257 if (Global::IsOnlineOrTransient(mData->mMachineState))
4258 return setError(VBOX_E_INVALID_VM_STATE,
4259 tr("Invalid machine state: %s"),
4260 Global::stringifyMachineState(mData->mMachineState));
4261
4262 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4263 aName,
4264 aControllerPort,
4265 aDevice);
4266 if (!pAttach)
4267 return setError(VBOX_E_OBJECT_NOT_FOUND,
4268 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4269 aDevice, aControllerPort, aName.c_str());
4270
4271
4272 i_setModified(IsModified_Storage);
4273 mMediumAttachments.backup();
4274
4275 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4276
4277 if (pAttach->i_getType() != DeviceType_HardDisk)
4278 return setError(E_INVALIDARG,
4279 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"),
4280 aDevice, aControllerPort, aName.c_str());
4281 pAttach->i_updateDiscard(!!aDiscard);
4282
4283 return S_OK;
4284}
4285
4286HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4287 LONG aDevice, BOOL aHotPluggable)
4288{
4289 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4290 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4291
4292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4293
4294 HRESULT rc = i_checkStateDependency(MutableStateDep);
4295 if (FAILED(rc)) return rc;
4296
4297 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4298
4299 if (Global::IsOnlineOrTransient(mData->mMachineState))
4300 return setError(VBOX_E_INVALID_VM_STATE,
4301 tr("Invalid machine state: %s"),
4302 Global::stringifyMachineState(mData->mMachineState));
4303
4304 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4305 aName,
4306 aControllerPort,
4307 aDevice);
4308 if (!pAttach)
4309 return setError(VBOX_E_OBJECT_NOT_FOUND,
4310 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4311 aDevice, aControllerPort, aName.c_str());
4312
4313 /* Check for an existing controller. */
4314 ComObjPtr<StorageController> ctl;
4315 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4316 if (FAILED(rc)) return rc;
4317
4318 StorageControllerType_T ctrlType;
4319 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4320 if (FAILED(rc))
4321 return setError(E_FAIL,
4322 tr("Could not get type of controller '%s'"),
4323 aName.c_str());
4324
4325 if (!i_isControllerHotplugCapable(ctrlType))
4326 return setError(VBOX_E_NOT_SUPPORTED,
4327 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4328 aName.c_str());
4329
4330 i_setModified(IsModified_Storage);
4331 mMediumAttachments.backup();
4332
4333 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4334
4335 if (pAttach->i_getType() == DeviceType_Floppy)
4336 return setError(E_INVALIDARG,
4337 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"),
4338 aDevice, aControllerPort, aName.c_str());
4339 pAttach->i_updateHotPluggable(!!aHotPluggable);
4340
4341 return S_OK;
4342}
4343
4344HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4345 LONG aDevice)
4346{
4347 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4348 aName.c_str(), aControllerPort, aDevice));
4349
4350 return setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4351}
4352
4353HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4354 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4355{
4356 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4357 aName.c_str(), aControllerPort, aDevice));
4358
4359 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4360
4361 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4362 if (FAILED(rc)) return rc;
4363
4364 if (Global::IsOnlineOrTransient(mData->mMachineState))
4365 return setError(VBOX_E_INVALID_VM_STATE,
4366 tr("Invalid machine state: %s"),
4367 Global::stringifyMachineState(mData->mMachineState));
4368
4369 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4370 aName,
4371 aControllerPort,
4372 aDevice);
4373 if (!pAttach)
4374 return setError(VBOX_E_OBJECT_NOT_FOUND,
4375 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4376 aDevice, aControllerPort, aName.c_str());
4377
4378
4379 i_setModified(IsModified_Storage);
4380 mMediumAttachments.backup();
4381
4382 IBandwidthGroup *iB = aBandwidthGroup;
4383 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4384 if (aBandwidthGroup && group.isNull())
4385 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4386
4387 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4388
4389 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4390 if (strBandwidthGroupOld.isNotEmpty())
4391 {
4392 /* Get the bandwidth group object and release it - this must not fail. */
4393 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4394 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4395 Assert(SUCCEEDED(rc));
4396
4397 pBandwidthGroupOld->i_release();
4398 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4399 }
4400
4401 if (!group.isNull())
4402 {
4403 group->i_reference();
4404 pAttach->i_updateBandwidthGroup(group->i_getName());
4405 }
4406
4407 return S_OK;
4408}
4409
4410HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4411 LONG aControllerPort,
4412 LONG aDevice,
4413 DeviceType_T aType)
4414{
4415 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4416 aName.c_str(), aControllerPort, aDevice, aType));
4417
4418 return attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4419}
4420
4421
4422HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4423 LONG aControllerPort,
4424 LONG aDevice,
4425 BOOL aForce)
4426{
4427 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4428 aName.c_str(), aControllerPort, aForce));
4429
4430 return mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4431}
4432
4433HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4434 LONG aControllerPort,
4435 LONG aDevice,
4436 const ComPtr<IMedium> &aMedium,
4437 BOOL aForce)
4438{
4439 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4440 aName.c_str(), aControllerPort, aDevice, aForce));
4441
4442 // request the host lock first, since might be calling Host methods for getting host drives;
4443 // next, protect the media tree all the while we're in here, as well as our member variables
4444 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4445 this->lockHandle(),
4446 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4447
4448 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4449 aName,
4450 aControllerPort,
4451 aDevice);
4452 if (pAttach.isNull())
4453 return setError(VBOX_E_OBJECT_NOT_FOUND,
4454 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4455 aDevice, aControllerPort, aName.c_str());
4456
4457 /* Remember previously mounted medium. The medium before taking the
4458 * backup is not necessarily the same thing. */
4459 ComObjPtr<Medium> oldmedium;
4460 oldmedium = pAttach->i_getMedium();
4461
4462 IMedium *iM = aMedium;
4463 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4464 if (aMedium && pMedium.isNull())
4465 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4466
4467 AutoCaller mediumCaller(pMedium);
4468 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4469
4470 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4471 if (pMedium)
4472 {
4473 DeviceType_T mediumType = pAttach->i_getType();
4474 switch (mediumType)
4475 {
4476 case DeviceType_DVD:
4477 case DeviceType_Floppy:
4478 break;
4479
4480 default:
4481 return setError(VBOX_E_INVALID_OBJECT_STATE,
4482 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4483 aControllerPort,
4484 aDevice,
4485 aName.c_str());
4486 }
4487 }
4488
4489 i_setModified(IsModified_Storage);
4490 mMediumAttachments.backup();
4491
4492 {
4493 // The backup operation makes the pAttach reference point to the
4494 // old settings. Re-get the correct reference.
4495 pAttach = i_findAttachment(*mMediumAttachments.data(),
4496 aName,
4497 aControllerPort,
4498 aDevice);
4499 if (!oldmedium.isNull())
4500 oldmedium->i_removeBackReference(mData->mUuid);
4501 if (!pMedium.isNull())
4502 {
4503 pMedium->i_addBackReference(mData->mUuid);
4504
4505 mediumLock.release();
4506 multiLock.release();
4507 i_addMediumToRegistry(pMedium);
4508 multiLock.acquire();
4509 mediumLock.acquire();
4510 }
4511
4512 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4513 pAttach->i_updateMedium(pMedium);
4514 }
4515
4516 i_setModified(IsModified_Storage);
4517
4518 mediumLock.release();
4519 multiLock.release();
4520 HRESULT rc = i_onMediumChange(pAttach, aForce);
4521 multiLock.acquire();
4522 mediumLock.acquire();
4523
4524 /* On error roll back this change only. */
4525 if (FAILED(rc))
4526 {
4527 if (!pMedium.isNull())
4528 pMedium->i_removeBackReference(mData->mUuid);
4529 pAttach = i_findAttachment(*mMediumAttachments.data(),
4530 aName,
4531 aControllerPort,
4532 aDevice);
4533 /* If the attachment is gone in the meantime, bail out. */
4534 if (pAttach.isNull())
4535 return rc;
4536 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4537 if (!oldmedium.isNull())
4538 oldmedium->i_addBackReference(mData->mUuid);
4539 pAttach->i_updateMedium(oldmedium);
4540 }
4541
4542 mediumLock.release();
4543 multiLock.release();
4544
4545 /* Save modified registries, but skip this machine as it's the caller's
4546 * job to save its settings like all other settings changes. */
4547 mParent->i_unmarkRegistryModified(i_getId());
4548 mParent->i_saveModifiedRegistries();
4549
4550 return rc;
4551}
4552HRESULT Machine::getMedium(const com::Utf8Str &aName,
4553 LONG aControllerPort,
4554 LONG aDevice,
4555 ComPtr<IMedium> &aMedium)
4556{
4557 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4558 aName.c_str(), aControllerPort, aDevice));
4559
4560 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4561
4562 aMedium = NULL;
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 storage device attached to device slot %d on port %d of controller '%s'"),
4571 aDevice, aControllerPort, aName.c_str());
4572
4573 aMedium = pAttach->i_getMedium();
4574
4575 return S_OK;
4576}
4577
4578HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4579{
4580
4581 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4582
4583 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4584
4585 return S_OK;
4586}
4587
4588HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4589{
4590 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4591
4592 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4593
4594 return S_OK;
4595}
4596
4597HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4598{
4599 /* Do not assert if slot is out of range, just return the advertised
4600 status. testdriver/vbox.py triggers this in logVmInfo. */
4601 if (aSlot >= mNetworkAdapters.size())
4602 return setError(E_INVALIDARG,
4603 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4604 aSlot, mNetworkAdapters.size());
4605
4606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4607
4608 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4609
4610 return S_OK;
4611}
4612
4613HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4614{
4615 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4616
4617 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4618 size_t i = 0;
4619 for (settings::StringsMap::const_iterator
4620 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4621 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4622 ++it, ++i)
4623 aKeys[i] = it->first;
4624
4625 return S_OK;
4626}
4627
4628 /**
4629 * @note Locks this object for reading.
4630 */
4631HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4632 com::Utf8Str &aValue)
4633{
4634 /* start with nothing found */
4635 aValue = "";
4636
4637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4638
4639 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4640 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4641 // found:
4642 aValue = it->second; // source is a Utf8Str
4643
4644 /* return the result to caller (may be empty) */
4645 return S_OK;
4646}
4647
4648 /**
4649 * @note Locks mParent for writing + this object for writing.
4650 */
4651HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4652{
4653 /* Because control characters in aKey have caused problems in the settings
4654 * they are rejected unless the key should be deleted. */
4655 if (!aValue.isEmpty())
4656 {
4657 for (size_t i = 0; i < aKey.length(); ++i)
4658 {
4659 char ch = aKey[i];
4660 if (RTLocCIsCntrl(ch))
4661 return E_INVALIDARG;
4662 }
4663 }
4664
4665 Utf8Str strOldValue; // empty
4666
4667 // locking note: we only hold the read lock briefly to look up the old value,
4668 // then release it and call the onExtraCanChange callbacks. There is a small
4669 // chance of a race insofar as the callback might be called twice if two callers
4670 // change the same key at the same time, but that's a much better solution
4671 // than the deadlock we had here before. The actual changing of the extradata
4672 // is then performed under the write lock and race-free.
4673
4674 // look up the old value first; if nothing has changed then we need not do anything
4675 {
4676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4677
4678 // For snapshots don't even think about allowing changes, extradata
4679 // is global for a machine, so there is nothing snapshot specific.
4680 if (i_isSnapshotMachine())
4681 return setError(VBOX_E_INVALID_VM_STATE,
4682 tr("Cannot set extradata for a snapshot"));
4683
4684 // check if the right IMachine instance is used
4685 if (mData->mRegistered && !i_isSessionMachine())
4686 return setError(VBOX_E_INVALID_VM_STATE,
4687 tr("Cannot set extradata for an immutable machine"));
4688
4689 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4690 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4691 strOldValue = it->second;
4692 }
4693
4694 bool fChanged;
4695 if ((fChanged = (strOldValue != aValue)))
4696 {
4697 // ask for permission from all listeners outside the locks;
4698 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4699 // lock to copy the list of callbacks to invoke
4700 Bstr bstrError;
4701 if (!mParent->i_onExtraDataCanChange(mData->mUuid, aKey, aValue, bstrError))
4702 {
4703 const char *sep = bstrError.isEmpty() ? "" : ": ";
4704 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, bstrError.raw()));
4705 return setError(E_ACCESSDENIED,
4706 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4707 aKey.c_str(),
4708 aValue.c_str(),
4709 sep,
4710 bstrError.raw());
4711 }
4712
4713 // data is changing and change not vetoed: then write it out under the lock
4714 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4715
4716 if (aValue.isEmpty())
4717 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4718 else
4719 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4720 // creates a new key if needed
4721
4722 bool fNeedsGlobalSaveSettings = false;
4723 // This saving of settings is tricky: there is no "old state" for the
4724 // extradata items at all (unlike all other settings), so the old/new
4725 // settings comparison would give a wrong result!
4726 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4727
4728 if (fNeedsGlobalSaveSettings)
4729 {
4730 // save the global settings; for that we should hold only the VirtualBox lock
4731 alock.release();
4732 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4733 mParent->i_saveSettings();
4734 }
4735 }
4736
4737 // fire notification outside the lock
4738 if (fChanged)
4739 mParent->i_onExtraDataChanged(mData->mUuid, aKey, aValue);
4740
4741 return S_OK;
4742}
4743
4744HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4745{
4746 aProgress = NULL;
4747 NOREF(aSettingsFilePath);
4748 ReturnComNotImplemented();
4749}
4750
4751HRESULT Machine::saveSettings()
4752{
4753 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4754
4755 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4756 if (FAILED(rc)) return rc;
4757
4758 /* the settings file path may never be null */
4759 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4760
4761 /* save all VM data excluding snapshots */
4762 bool fNeedsGlobalSaveSettings = false;
4763 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4764 mlock.release();
4765
4766 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4767 {
4768 // save the global settings; for that we should hold only the VirtualBox lock
4769 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4770 rc = mParent->i_saveSettings();
4771 }
4772
4773 return rc;
4774}
4775
4776
4777HRESULT Machine::discardSettings()
4778{
4779 /*
4780 * We need to take the machine list lock here as well as the machine one
4781 * or we'll get into trouble should any media stuff require rolling back.
4782 *
4783 * Details:
4784 *
4785 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4786 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4787 * 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]
4788 * 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
4789 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4790 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4791 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4792 * 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
4793 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4794 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4795 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4796 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4797 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4798 * 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]
4799 * 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] (*)
4800 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4801 * 0:005> k
4802 * # Child-SP RetAddr Call Site
4803 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4804 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4805 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4806 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4807 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4808 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4809 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4810 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4811 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4812 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4813 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4814 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4815 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4816 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4817 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4818 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4819 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4820 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4821 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4822 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4823 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4824 *
4825 */
4826 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4828
4829 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4830 if (FAILED(rc)) return rc;
4831
4832 /*
4833 * during this rollback, the session will be notified if data has
4834 * been actually changed
4835 */
4836 i_rollback(true /* aNotify */);
4837
4838 return S_OK;
4839}
4840
4841/** @note Locks objects! */
4842HRESULT Machine::unregister(AutoCaller &autoCaller,
4843 CleanupMode_T aCleanupMode,
4844 std::vector<ComPtr<IMedium> > &aMedia)
4845{
4846 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4847
4848 Guid id(i_getId());
4849
4850 if (mData->mSession.mState != SessionState_Unlocked)
4851 return setError(VBOX_E_INVALID_OBJECT_STATE,
4852 tr("Cannot unregister the machine '%s' while it is locked"),
4853 mUserData->s.strName.c_str());
4854
4855 // wait for state dependents to drop to zero
4856 i_ensureNoStateDependencies();
4857
4858 if (!mData->mAccessible)
4859 {
4860 // inaccessible machines can only be unregistered; uninitialize ourselves
4861 // here because currently there may be no unregistered that are inaccessible
4862 // (this state combination is not supported). Note releasing the caller and
4863 // leaving the lock before calling uninit()
4864 alock.release();
4865 autoCaller.release();
4866
4867 uninit();
4868
4869 mParent->i_unregisterMachine(this, id);
4870 // calls VirtualBox::i_saveSettings()
4871
4872 return S_OK;
4873 }
4874
4875 HRESULT rc = S_OK;
4876 mData->llFilesToDelete.clear();
4877
4878 if (!mSSData->strStateFilePath.isEmpty())
4879 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
4880
4881 Utf8Str strNVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
4882 if (!strNVRAMFile.isEmpty() && RTFileExists(strNVRAMFile.c_str()))
4883 mData->llFilesToDelete.push_back(strNVRAMFile);
4884
4885 // This list collects the medium objects from all medium attachments
4886 // which we will detach from the machine and its snapshots, in a specific
4887 // order which allows for closing all media without getting "media in use"
4888 // errors, simply by going through the list from the front to the back:
4889 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
4890 // and must be closed before the parent media from the snapshots, or closing the parents
4891 // will fail because they still have children);
4892 // 2) media from the youngest snapshots followed by those from the parent snapshots until
4893 // the root ("first") snapshot of the machine.
4894 MediaList llMedia;
4895
4896 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
4897 && mMediumAttachments->size()
4898 )
4899 {
4900 // we have media attachments: detach them all and add the Medium objects to our list
4901 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
4902 }
4903
4904 if (mData->mFirstSnapshot)
4905 {
4906 // add the media from the medium attachments of the snapshots to llMedia
4907 // as well, after the "main" machine media; Snapshot::uninitRecursively()
4908 // calls Machine::detachAllMedia() for the snapshot machine, recursing
4909 // into the children first
4910
4911 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
4912 MachineState_T oldState = mData->mMachineState;
4913 mData->mMachineState = MachineState_DeletingSnapshot;
4914
4915 // make a copy of the first snapshot reference so the refcount does not
4916 // drop to 0 in beginDeletingSnapshot, which sets pFirstSnapshot to 0
4917 // (would hang due to the AutoCaller voodoo)
4918 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
4919
4920 // GO!
4921 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
4922
4923 mData->mMachineState = oldState;
4924 }
4925
4926 if (FAILED(rc))
4927 {
4928 i_rollbackMedia();
4929 return rc;
4930 }
4931
4932 // commit all the media changes made above
4933 i_commitMedia();
4934
4935 mData->mRegistered = false;
4936
4937 // machine lock no longer needed
4938 alock.release();
4939
4940 /* Make sure that the settings of the current VM are not saved, because
4941 * they are rather crippled at this point to meet the cleanup expectations
4942 * and there's no point destroying the VM config on disk just because. */
4943 mParent->i_unmarkRegistryModified(id);
4944
4945 // return media to caller
4946 aMedia.resize(llMedia.size());
4947 size_t i = 0;
4948 for (MediaList::const_iterator
4949 it = llMedia.begin();
4950 it != llMedia.end();
4951 ++it, ++i)
4952 (*it).queryInterfaceTo(aMedia[i].asOutParam());
4953
4954 mParent->i_unregisterMachine(this, id);
4955 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
4956
4957 return S_OK;
4958}
4959
4960/**
4961 * Task record for deleting a machine config.
4962 */
4963class Machine::DeleteConfigTask
4964 : public Machine::Task
4965{
4966public:
4967 DeleteConfigTask(Machine *m,
4968 Progress *p,
4969 const Utf8Str &t,
4970 const RTCList<ComPtr<IMedium> > &llMediums,
4971 const StringsList &llFilesToDelete)
4972 : Task(m, p, t),
4973 m_llMediums(llMediums),
4974 m_llFilesToDelete(llFilesToDelete)
4975 {}
4976
4977private:
4978 void handler()
4979 {
4980 try
4981 {
4982 m_pMachine->i_deleteConfigHandler(*this);
4983 }
4984 catch (...)
4985 {
4986 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
4987 }
4988 }
4989
4990 RTCList<ComPtr<IMedium> > m_llMediums;
4991 StringsList m_llFilesToDelete;
4992
4993 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
4994};
4995
4996/**
4997 * Task thread implementation for SessionMachine::DeleteConfig(), called from
4998 * SessionMachine::taskHandler().
4999 *
5000 * @note Locks this object for writing.
5001 *
5002 * @param task
5003 * @return
5004 */
5005void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5006{
5007 LogFlowThisFuncEnter();
5008
5009 AutoCaller autoCaller(this);
5010 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5011 if (FAILED(autoCaller.rc()))
5012 {
5013 /* we might have been uninitialized because the session was accidentally
5014 * closed by the client, so don't assert */
5015 HRESULT rc = setError(E_FAIL,
5016 tr("The session has been accidentally closed"));
5017 task.m_pProgress->i_notifyComplete(rc);
5018 LogFlowThisFuncLeave();
5019 return;
5020 }
5021
5022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5023
5024 HRESULT rc = S_OK;
5025
5026 try
5027 {
5028 ULONG uLogHistoryCount = 3;
5029 ComPtr<ISystemProperties> systemProperties;
5030 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5031 if (FAILED(rc)) throw rc;
5032
5033 if (!systemProperties.isNull())
5034 {
5035 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5036 if (FAILED(rc)) throw rc;
5037 }
5038
5039 MachineState_T oldState = mData->mMachineState;
5040 i_setMachineState(MachineState_SettingUp);
5041 alock.release();
5042 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5043 {
5044 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5045 {
5046 AutoCaller mac(pMedium);
5047 if (FAILED(mac.rc())) throw mac.rc();
5048 Utf8Str strLocation = pMedium->i_getLocationFull();
5049 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5050 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5051 if (FAILED(rc)) throw rc;
5052 }
5053 if (pMedium->i_isMediumFormatFile())
5054 {
5055 ComPtr<IProgress> pProgress2;
5056 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5057 if (FAILED(rc)) throw rc;
5058 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5059 if (FAILED(rc)) throw rc;
5060 }
5061
5062 /* Close the medium, deliberately without checking the return
5063 * code, and without leaving any trace in the error info, as
5064 * a failure here is a very minor issue, which shouldn't happen
5065 * as above we even managed to delete the medium. */
5066 {
5067 ErrorInfoKeeper eik;
5068 pMedium->Close();
5069 }
5070 }
5071 i_setMachineState(oldState);
5072 alock.acquire();
5073
5074 // delete the files pushed on the task list by Machine::Delete()
5075 // (this includes saved states of the machine and snapshots and
5076 // medium storage files from the IMedium list passed in, and the
5077 // machine XML file)
5078 for (StringsList::const_iterator
5079 it = task.m_llFilesToDelete.begin();
5080 it != task.m_llFilesToDelete.end();
5081 ++it)
5082 {
5083 const Utf8Str &strFile = *it;
5084 LogFunc(("Deleting file %s\n", strFile.c_str()));
5085 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5086 if (FAILED(rc)) throw rc;
5087
5088 int vrc = RTFileDelete(strFile.c_str());
5089 if (RT_FAILURE(vrc))
5090 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5091 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5092 }
5093
5094 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5095 if (FAILED(rc)) throw rc;
5096
5097 /* delete the settings only when the file actually exists */
5098 if (mData->pMachineConfigFile->fileExists())
5099 {
5100 /* Delete any backup or uncommitted XML files. Ignore failures.
5101 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5102 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5103 Utf8StrFmt otherXml("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5104 RTFileDelete(otherXml.c_str());
5105 otherXml.printf("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5106 RTFileDelete(otherXml.c_str());
5107
5108 /* delete the Logs folder, nothing important should be left
5109 * there (we don't check for errors because the user might have
5110 * some private files there that we don't want to delete) */
5111 Utf8Str logFolder;
5112 getLogFolder(logFolder);
5113 Assert(logFolder.length());
5114 if (RTDirExists(logFolder.c_str()))
5115 {
5116 /* Delete all VBox.log[.N] files from the Logs folder
5117 * (this must be in sync with the rotation logic in
5118 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5119 * files that may have been created by the GUI. */
5120 Utf8StrFmt log("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
5121 RTFileDelete(log.c_str());
5122 log.printf("%s%cVBox.png", logFolder.c_str(), RTPATH_DELIMITER);
5123 RTFileDelete(log.c_str());
5124 for (ULONG i = uLogHistoryCount; i > 0; i--)
5125 {
5126 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5127 RTFileDelete(log.c_str());
5128 log.printf("%s%cVBox.png.%u", logFolder.c_str(), RTPATH_DELIMITER, i);
5129 RTFileDelete(log.c_str());
5130 }
5131#if defined(RT_OS_WINDOWS)
5132 log.printf("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5133 RTFileDelete(log.c_str());
5134 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5135 RTFileDelete(log.c_str());
5136#endif
5137
5138 RTDirRemove(logFolder.c_str());
5139 }
5140
5141 /* delete the Snapshots folder, nothing important should be left
5142 * there (we don't check for errors because the user might have
5143 * some private files there that we don't want to delete) */
5144 Utf8Str strFullSnapshotFolder;
5145 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5146 Assert(!strFullSnapshotFolder.isEmpty());
5147 if (RTDirExists(strFullSnapshotFolder.c_str()))
5148 RTDirRemove(strFullSnapshotFolder.c_str());
5149
5150 // delete the directory that contains the settings file, but only
5151 // if it matches the VM name
5152 Utf8Str settingsDir;
5153 if (i_isInOwnDir(&settingsDir))
5154 RTDirRemove(settingsDir.c_str());
5155 }
5156
5157 alock.release();
5158
5159 mParent->i_saveModifiedRegistries();
5160 }
5161 catch (HRESULT aRC) { rc = aRC; }
5162
5163 task.m_pProgress->i_notifyComplete(rc);
5164
5165 LogFlowThisFuncLeave();
5166}
5167
5168HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5169{
5170 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5171
5172 HRESULT rc = i_checkStateDependency(MutableStateDep);
5173 if (FAILED(rc)) return rc;
5174
5175 if (mData->mRegistered)
5176 return setError(VBOX_E_INVALID_VM_STATE,
5177 tr("Cannot delete settings of a registered machine"));
5178
5179 // collect files to delete
5180 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states and NVRAM files pushed here by Unregister()
5181 // machine config file
5182 if (mData->pMachineConfigFile->fileExists())
5183 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5184 // backup of machine config file
5185 Utf8Str strTmp(mData->m_strConfigFileFull);
5186 strTmp.append("-prev");
5187 if (RTFileExists(strTmp.c_str()))
5188 llFilesToDelete.push_back(strTmp);
5189
5190 RTCList<ComPtr<IMedium> > llMediums;
5191 for (size_t i = 0; i < aMedia.size(); ++i)
5192 {
5193 IMedium *pIMedium(aMedia[i]);
5194 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5195 if (pMedium.isNull())
5196 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5197 SafeArray<BSTR> ids;
5198 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5199 if (FAILED(rc)) return rc;
5200 /* At this point the medium should not have any back references
5201 * anymore. If it has it is attached to another VM and *must* not
5202 * deleted. */
5203 if (ids.size() < 1)
5204 llMediums.append(pMedium);
5205 }
5206
5207 ComObjPtr<Progress> pProgress;
5208 pProgress.createObject();
5209 rc = pProgress->init(i_getVirtualBox(),
5210 static_cast<IMachine*>(this) /* aInitiator */,
5211 tr("Deleting files"),
5212 true /* fCancellable */,
5213 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5214 tr("Collecting file inventory"));
5215 if (FAILED(rc))
5216 return rc;
5217
5218 /* create and start the task on a separate thread (note that it will not
5219 * start working until we release alock) */
5220 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5221 rc = pTask->createThread();
5222 pTask = NULL;
5223 if (FAILED(rc))
5224 return rc;
5225
5226 pProgress.queryInterfaceTo(aProgress.asOutParam());
5227
5228 LogFlowFuncLeave();
5229
5230 return S_OK;
5231}
5232
5233HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5234{
5235 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5236
5237 ComObjPtr<Snapshot> pSnapshot;
5238 HRESULT rc;
5239
5240 if (aNameOrId.isEmpty())
5241 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5242 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5243 else
5244 {
5245 Guid uuid(aNameOrId);
5246 if (uuid.isValid())
5247 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5248 else
5249 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5250 }
5251 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5252
5253 return rc;
5254}
5255
5256HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5257 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5258{
5259 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5260
5261 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5262 if (FAILED(rc)) return rc;
5263
5264 ComObjPtr<SharedFolder> sharedFolder;
5265 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5266 if (SUCCEEDED(rc))
5267 return setError(VBOX_E_OBJECT_IN_USE,
5268 tr("Shared folder named '%s' already exists"),
5269 aName.c_str());
5270
5271 sharedFolder.createObject();
5272 rc = sharedFolder->init(i_getMachine(),
5273 aName,
5274 aHostPath,
5275 !!aWritable,
5276 !!aAutomount,
5277 aAutoMountPoint,
5278 true /* fFailOnError */);
5279 if (FAILED(rc)) return rc;
5280
5281 i_setModified(IsModified_SharedFolders);
5282 mHWData.backup();
5283 mHWData->mSharedFolders.push_back(sharedFolder);
5284
5285 /* inform the direct session if any */
5286 alock.release();
5287 i_onSharedFolderChange();
5288
5289 return S_OK;
5290}
5291
5292HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5293{
5294 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5295
5296 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5297 if (FAILED(rc)) return rc;
5298
5299 ComObjPtr<SharedFolder> sharedFolder;
5300 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5301 if (FAILED(rc)) return rc;
5302
5303 i_setModified(IsModified_SharedFolders);
5304 mHWData.backup();
5305 mHWData->mSharedFolders.remove(sharedFolder);
5306
5307 /* inform the direct session if any */
5308 alock.release();
5309 i_onSharedFolderChange();
5310
5311 return S_OK;
5312}
5313
5314HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5315{
5316 /* start with No */
5317 *aCanShow = FALSE;
5318
5319 ComPtr<IInternalSessionControl> directControl;
5320 {
5321 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5322
5323 if (mData->mSession.mState != SessionState_Locked)
5324 return setError(VBOX_E_INVALID_VM_STATE,
5325 tr("Machine is not locked for session (session state: %s)"),
5326 Global::stringifySessionState(mData->mSession.mState));
5327
5328 if (mData->mSession.mLockType == LockType_VM)
5329 directControl = mData->mSession.mDirectControl;
5330 }
5331
5332 /* ignore calls made after #OnSessionEnd() is called */
5333 if (!directControl)
5334 return S_OK;
5335
5336 LONG64 dummy;
5337 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5338}
5339
5340HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5341{
5342 ComPtr<IInternalSessionControl> directControl;
5343 {
5344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5345
5346 if (mData->mSession.mState != SessionState_Locked)
5347 return setError(E_FAIL,
5348 tr("Machine is not locked for session (session state: %s)"),
5349 Global::stringifySessionState(mData->mSession.mState));
5350
5351 if (mData->mSession.mLockType == LockType_VM)
5352 directControl = mData->mSession.mDirectControl;
5353 }
5354
5355 /* ignore calls made after #OnSessionEnd() is called */
5356 if (!directControl)
5357 return S_OK;
5358
5359 BOOL dummy;
5360 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5361}
5362
5363#ifdef VBOX_WITH_GUEST_PROPS
5364/**
5365 * Look up a guest property in VBoxSVC's internal structures.
5366 */
5367HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5368 com::Utf8Str &aValue,
5369 LONG64 *aTimestamp,
5370 com::Utf8Str &aFlags) const
5371{
5372 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5373
5374 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5375 if (it != mHWData->mGuestProperties.end())
5376 {
5377 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5378 aValue = it->second.strValue;
5379 *aTimestamp = it->second.mTimestamp;
5380 GuestPropWriteFlags(it->second.mFlags, szFlags);
5381 aFlags = Utf8Str(szFlags);
5382 }
5383
5384 return S_OK;
5385}
5386
5387/**
5388 * Query the VM that a guest property belongs to for the property.
5389 * @returns E_ACCESSDENIED if the VM process is not available or not
5390 * currently handling queries and the lookup should then be done in
5391 * VBoxSVC.
5392 */
5393HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5394 com::Utf8Str &aValue,
5395 LONG64 *aTimestamp,
5396 com::Utf8Str &aFlags) const
5397{
5398 HRESULT rc = S_OK;
5399 Bstr bstrValue;
5400 Bstr bstrFlags;
5401
5402 ComPtr<IInternalSessionControl> directControl;
5403 {
5404 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5405 if (mData->mSession.mLockType == LockType_VM)
5406 directControl = mData->mSession.mDirectControl;
5407 }
5408
5409 /* ignore calls made after #OnSessionEnd() is called */
5410 if (!directControl)
5411 rc = E_ACCESSDENIED;
5412 else
5413 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5414 0 /* accessMode */,
5415 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5416
5417 aValue = bstrValue;
5418 aFlags = bstrFlags;
5419
5420 return rc;
5421}
5422#endif // VBOX_WITH_GUEST_PROPS
5423
5424HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5425 com::Utf8Str &aValue,
5426 LONG64 *aTimestamp,
5427 com::Utf8Str &aFlags)
5428{
5429#ifndef VBOX_WITH_GUEST_PROPS
5430 ReturnComNotImplemented();
5431#else // VBOX_WITH_GUEST_PROPS
5432
5433 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5434
5435 if (rc == E_ACCESSDENIED)
5436 /* The VM is not running or the service is not (yet) accessible */
5437 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5438 return rc;
5439#endif // VBOX_WITH_GUEST_PROPS
5440}
5441
5442HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5443{
5444 LONG64 dummyTimestamp;
5445 com::Utf8Str dummyFlags;
5446 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5447 return rc;
5448
5449}
5450HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5451{
5452 com::Utf8Str dummyFlags;
5453 com::Utf8Str dummyValue;
5454 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5455 return rc;
5456}
5457
5458#ifdef VBOX_WITH_GUEST_PROPS
5459/**
5460 * Set a guest property in VBoxSVC's internal structures.
5461 */
5462HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5463 const com::Utf8Str &aFlags, bool fDelete)
5464{
5465 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5466 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5467 if (FAILED(rc)) return rc;
5468
5469 try
5470 {
5471 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5472 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5473 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5474
5475 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5476 if (it == mHWData->mGuestProperties.end())
5477 {
5478 if (!fDelete)
5479 {
5480 i_setModified(IsModified_MachineData);
5481 mHWData.backupEx();
5482
5483 RTTIMESPEC time;
5484 HWData::GuestProperty prop;
5485 prop.strValue = Bstr(aValue).raw();
5486 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5487 prop.mFlags = fFlags;
5488 mHWData->mGuestProperties[aName] = prop;
5489 }
5490 }
5491 else
5492 {
5493 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5494 {
5495 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5496 }
5497 else
5498 {
5499 i_setModified(IsModified_MachineData);
5500 mHWData.backupEx();
5501
5502 /* The backupEx() operation invalidates our iterator,
5503 * so get a new one. */
5504 it = mHWData->mGuestProperties.find(aName);
5505 Assert(it != mHWData->mGuestProperties.end());
5506
5507 if (!fDelete)
5508 {
5509 RTTIMESPEC time;
5510 it->second.strValue = aValue;
5511 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5512 it->second.mFlags = fFlags;
5513 }
5514 else
5515 mHWData->mGuestProperties.erase(it);
5516 }
5517 }
5518
5519 if (SUCCEEDED(rc))
5520 {
5521 alock.release();
5522
5523 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
5524 }
5525 }
5526 catch (std::bad_alloc &)
5527 {
5528 rc = E_OUTOFMEMORY;
5529 }
5530
5531 return rc;
5532}
5533
5534/**
5535 * Set a property on the VM that that property belongs to.
5536 * @returns E_ACCESSDENIED if the VM process is not available or not
5537 * currently handling queries and the setting should then be done in
5538 * VBoxSVC.
5539 */
5540HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5541 const com::Utf8Str &aFlags, bool fDelete)
5542{
5543 HRESULT rc;
5544
5545 try
5546 {
5547 ComPtr<IInternalSessionControl> directControl;
5548 {
5549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5550 if (mData->mSession.mLockType == LockType_VM)
5551 directControl = mData->mSession.mDirectControl;
5552 }
5553
5554 Bstr dummy1; /* will not be changed (setter) */
5555 Bstr dummy2; /* will not be changed (setter) */
5556 LONG64 dummy64;
5557 if (!directControl)
5558 rc = E_ACCESSDENIED;
5559 else
5560 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5561 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5562 fDelete ? 2 : 1 /* accessMode */,
5563 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5564 }
5565 catch (std::bad_alloc &)
5566 {
5567 rc = E_OUTOFMEMORY;
5568 }
5569
5570 return rc;
5571}
5572#endif // VBOX_WITH_GUEST_PROPS
5573
5574HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5575 const com::Utf8Str &aFlags)
5576{
5577#ifndef VBOX_WITH_GUEST_PROPS
5578 ReturnComNotImplemented();
5579#else // VBOX_WITH_GUEST_PROPS
5580 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5581 if (rc == E_ACCESSDENIED)
5582 /* The VM is not running or the service is not (yet) accessible */
5583 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5584 return rc;
5585#endif // VBOX_WITH_GUEST_PROPS
5586}
5587
5588HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5589{
5590 return setGuestProperty(aProperty, aValue, "");
5591}
5592
5593HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5594{
5595#ifndef VBOX_WITH_GUEST_PROPS
5596 ReturnComNotImplemented();
5597#else // VBOX_WITH_GUEST_PROPS
5598 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5599 if (rc == E_ACCESSDENIED)
5600 /* The VM is not running or the service is not (yet) accessible */
5601 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5602 return rc;
5603#endif // VBOX_WITH_GUEST_PROPS
5604}
5605
5606#ifdef VBOX_WITH_GUEST_PROPS
5607/**
5608 * Enumerate the guest properties in VBoxSVC's internal structures.
5609 */
5610HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5611 std::vector<com::Utf8Str> &aNames,
5612 std::vector<com::Utf8Str> &aValues,
5613 std::vector<LONG64> &aTimestamps,
5614 std::vector<com::Utf8Str> &aFlags)
5615{
5616 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5617 Utf8Str strPatterns(aPatterns);
5618
5619 /*
5620 * Look for matching patterns and build up a list.
5621 */
5622 HWData::GuestPropertyMap propMap;
5623 for (HWData::GuestPropertyMap::const_iterator
5624 it = mHWData->mGuestProperties.begin();
5625 it != mHWData->mGuestProperties.end();
5626 ++it)
5627 {
5628 if ( strPatterns.isEmpty()
5629 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5630 RTSTR_MAX,
5631 it->first.c_str(),
5632 RTSTR_MAX,
5633 NULL)
5634 )
5635 propMap.insert(*it);
5636 }
5637
5638 alock.release();
5639
5640 /*
5641 * And build up the arrays for returning the property information.
5642 */
5643 size_t cEntries = propMap.size();
5644
5645 aNames.resize(cEntries);
5646 aValues.resize(cEntries);
5647 aTimestamps.resize(cEntries);
5648 aFlags.resize(cEntries);
5649
5650 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5651 size_t i = 0;
5652 for (HWData::GuestPropertyMap::const_iterator
5653 it = propMap.begin();
5654 it != propMap.end();
5655 ++it, ++i)
5656 {
5657 aNames[i] = it->first;
5658 aValues[i] = it->second.strValue;
5659 aTimestamps[i] = it->second.mTimestamp;
5660 GuestPropWriteFlags(it->second.mFlags, szFlags);
5661 aFlags[i] = Utf8Str(szFlags);
5662 }
5663
5664 return S_OK;
5665}
5666
5667/**
5668 * Enumerate the properties managed by a VM.
5669 * @returns E_ACCESSDENIED if the VM process is not available or not
5670 * currently handling queries and the setting should then be done in
5671 * VBoxSVC.
5672 */
5673HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5674 std::vector<com::Utf8Str> &aNames,
5675 std::vector<com::Utf8Str> &aValues,
5676 std::vector<LONG64> &aTimestamps,
5677 std::vector<com::Utf8Str> &aFlags)
5678{
5679 HRESULT rc;
5680 ComPtr<IInternalSessionControl> directControl;
5681 {
5682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5683 if (mData->mSession.mLockType == LockType_VM)
5684 directControl = mData->mSession.mDirectControl;
5685 }
5686
5687 com::SafeArray<BSTR> bNames;
5688 com::SafeArray<BSTR> bValues;
5689 com::SafeArray<LONG64> bTimestamps;
5690 com::SafeArray<BSTR> bFlags;
5691
5692 if (!directControl)
5693 rc = E_ACCESSDENIED;
5694 else
5695 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5696 ComSafeArrayAsOutParam(bNames),
5697 ComSafeArrayAsOutParam(bValues),
5698 ComSafeArrayAsOutParam(bTimestamps),
5699 ComSafeArrayAsOutParam(bFlags));
5700 size_t i;
5701 aNames.resize(bNames.size());
5702 for (i = 0; i < bNames.size(); ++i)
5703 aNames[i] = Utf8Str(bNames[i]);
5704 aValues.resize(bValues.size());
5705 for (i = 0; i < bValues.size(); ++i)
5706 aValues[i] = Utf8Str(bValues[i]);
5707 aTimestamps.resize(bTimestamps.size());
5708 for (i = 0; i < bTimestamps.size(); ++i)
5709 aTimestamps[i] = bTimestamps[i];
5710 aFlags.resize(bFlags.size());
5711 for (i = 0; i < bFlags.size(); ++i)
5712 aFlags[i] = Utf8Str(bFlags[i]);
5713
5714 return rc;
5715}
5716#endif // VBOX_WITH_GUEST_PROPS
5717HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5718 std::vector<com::Utf8Str> &aNames,
5719 std::vector<com::Utf8Str> &aValues,
5720 std::vector<LONG64> &aTimestamps,
5721 std::vector<com::Utf8Str> &aFlags)
5722{
5723#ifndef VBOX_WITH_GUEST_PROPS
5724 ReturnComNotImplemented();
5725#else // VBOX_WITH_GUEST_PROPS
5726
5727 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5728
5729 if (rc == E_ACCESSDENIED)
5730 /* The VM is not running or the service is not (yet) accessible */
5731 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5732 return rc;
5733#endif // VBOX_WITH_GUEST_PROPS
5734}
5735
5736HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5737 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5738{
5739 MediumAttachmentList atts;
5740
5741 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5742 if (FAILED(rc)) return rc;
5743
5744 aMediumAttachments.resize(atts.size());
5745 size_t i = 0;
5746 for (MediumAttachmentList::const_iterator
5747 it = atts.begin();
5748 it != atts.end();
5749 ++it, ++i)
5750 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5751
5752 return S_OK;
5753}
5754
5755HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5756 LONG aControllerPort,
5757 LONG aDevice,
5758 ComPtr<IMediumAttachment> &aAttachment)
5759{
5760 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5761 aName.c_str(), aControllerPort, aDevice));
5762
5763 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5764
5765 aAttachment = NULL;
5766
5767 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5768 aName,
5769 aControllerPort,
5770 aDevice);
5771 if (pAttach.isNull())
5772 return setError(VBOX_E_OBJECT_NOT_FOUND,
5773 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5774 aDevice, aControllerPort, aName.c_str());
5775
5776 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5777
5778 return S_OK;
5779}
5780
5781
5782HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5783 StorageBus_T aConnectionType,
5784 ComPtr<IStorageController> &aController)
5785{
5786 if ( (aConnectionType <= StorageBus_Null)
5787 || (aConnectionType > StorageBus_VirtioSCSI))
5788 return setError(E_INVALIDARG,
5789 tr("Invalid connection type: %d"),
5790 aConnectionType);
5791
5792 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5793
5794 HRESULT rc = i_checkStateDependency(MutableStateDep);
5795 if (FAILED(rc)) return rc;
5796
5797 /* try to find one with the name first. */
5798 ComObjPtr<StorageController> ctrl;
5799
5800 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5801 if (SUCCEEDED(rc))
5802 return setError(VBOX_E_OBJECT_IN_USE,
5803 tr("Storage controller named '%s' already exists"),
5804 aName.c_str());
5805
5806 ctrl.createObject();
5807
5808 /* get a new instance number for the storage controller */
5809 ULONG ulInstance = 0;
5810 bool fBootable = true;
5811 for (StorageControllerList::const_iterator
5812 it = mStorageControllers->begin();
5813 it != mStorageControllers->end();
5814 ++it)
5815 {
5816 if ((*it)->i_getStorageBus() == aConnectionType)
5817 {
5818 ULONG ulCurInst = (*it)->i_getInstance();
5819
5820 if (ulCurInst >= ulInstance)
5821 ulInstance = ulCurInst + 1;
5822
5823 /* Only one controller of each type can be marked as bootable. */
5824 if ((*it)->i_getBootable())
5825 fBootable = false;
5826 }
5827 }
5828
5829 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5830 if (FAILED(rc)) return rc;
5831
5832 i_setModified(IsModified_Storage);
5833 mStorageControllers.backup();
5834 mStorageControllers->push_back(ctrl);
5835
5836 ctrl.queryInterfaceTo(aController.asOutParam());
5837
5838 /* inform the direct session if any */
5839 alock.release();
5840 i_onStorageControllerChange(i_getId(), aName);
5841
5842 return S_OK;
5843}
5844
5845HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5846 ComPtr<IStorageController> &aStorageController)
5847{
5848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5849
5850 ComObjPtr<StorageController> ctrl;
5851
5852 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5853 if (SUCCEEDED(rc))
5854 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5855
5856 return rc;
5857}
5858
5859HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5860 ULONG aInstance,
5861 ComPtr<IStorageController> &aStorageController)
5862{
5863 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5864
5865 for (StorageControllerList::const_iterator
5866 it = mStorageControllers->begin();
5867 it != mStorageControllers->end();
5868 ++it)
5869 {
5870 if ( (*it)->i_getStorageBus() == aConnectionType
5871 && (*it)->i_getInstance() == aInstance)
5872 {
5873 (*it).queryInterfaceTo(aStorageController.asOutParam());
5874 return S_OK;
5875 }
5876 }
5877
5878 return setError(VBOX_E_OBJECT_NOT_FOUND,
5879 tr("Could not find a storage controller with instance number '%lu'"),
5880 aInstance);
5881}
5882
5883HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
5884{
5885 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5886
5887 HRESULT rc = i_checkStateDependency(MutableStateDep);
5888 if (FAILED(rc)) return rc;
5889
5890 ComObjPtr<StorageController> ctrl;
5891
5892 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5893 if (SUCCEEDED(rc))
5894 {
5895 /* Ensure that only one controller of each type is marked as bootable. */
5896 if (aBootable == TRUE)
5897 {
5898 for (StorageControllerList::const_iterator
5899 it = mStorageControllers->begin();
5900 it != mStorageControllers->end();
5901 ++it)
5902 {
5903 ComObjPtr<StorageController> aCtrl = (*it);
5904
5905 if ( (aCtrl->i_getName() != aName)
5906 && aCtrl->i_getBootable() == TRUE
5907 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
5908 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
5909 {
5910 aCtrl->i_setBootable(FALSE);
5911 break;
5912 }
5913 }
5914 }
5915
5916 if (SUCCEEDED(rc))
5917 {
5918 ctrl->i_setBootable(aBootable);
5919 i_setModified(IsModified_Storage);
5920 }
5921 }
5922
5923 if (SUCCEEDED(rc))
5924 {
5925 /* inform the direct session if any */
5926 alock.release();
5927 i_onStorageControllerChange(i_getId(), aName);
5928 }
5929
5930 return rc;
5931}
5932
5933HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
5934{
5935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5936
5937 HRESULT rc = i_checkStateDependency(MutableStateDep);
5938 if (FAILED(rc)) return rc;
5939
5940 ComObjPtr<StorageController> ctrl;
5941 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5942 if (FAILED(rc)) return rc;
5943
5944 MediumAttachmentList llDetachedAttachments;
5945 {
5946 /* find all attached devices to the appropriate storage controller and detach them all */
5947 // make a temporary list because detachDevice invalidates iterators into
5948 // mMediumAttachments
5949 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
5950
5951 for (MediumAttachmentList::const_iterator
5952 it = llAttachments2.begin();
5953 it != llAttachments2.end();
5954 ++it)
5955 {
5956 MediumAttachment *pAttachTemp = *it;
5957
5958 AutoCaller localAutoCaller(pAttachTemp);
5959 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
5960
5961 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
5962
5963 if (pAttachTemp->i_getControllerName() == aName)
5964 {
5965 llDetachedAttachments.push_back(pAttachTemp);
5966 rc = i_detachDevice(pAttachTemp, alock, NULL);
5967 if (FAILED(rc)) return rc;
5968 }
5969 }
5970 }
5971
5972 /* send event about detached devices before removing parent controller */
5973 for (MediumAttachmentList::const_iterator
5974 it = llDetachedAttachments.begin();
5975 it != llDetachedAttachments.end();
5976 ++it)
5977 {
5978 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
5979 }
5980
5981 /* We can remove it now. */
5982 i_setModified(IsModified_Storage);
5983 mStorageControllers.backup();
5984
5985 ctrl->i_unshare();
5986
5987 mStorageControllers->remove(ctrl);
5988
5989 /* inform the direct session if any */
5990 alock.release();
5991 i_onStorageControllerChange(i_getId(), aName);
5992
5993 return S_OK;
5994}
5995
5996HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
5997 ComPtr<IUSBController> &aController)
5998{
5999 if ( (aType <= USBControllerType_Null)
6000 || (aType >= USBControllerType_Last))
6001 return setError(E_INVALIDARG,
6002 tr("Invalid USB controller type: %d"),
6003 aType);
6004
6005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6006
6007 HRESULT rc = i_checkStateDependency(MutableStateDep);
6008 if (FAILED(rc)) return rc;
6009
6010 /* try to find one with the same type first. */
6011 ComObjPtr<USBController> ctrl;
6012
6013 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6014 if (SUCCEEDED(rc))
6015 return setError(VBOX_E_OBJECT_IN_USE,
6016 tr("USB controller named '%s' already exists"),
6017 aName.c_str());
6018
6019 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6020 ULONG maxInstances;
6021 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6022 if (FAILED(rc))
6023 return rc;
6024
6025 ULONG cInstances = i_getUSBControllerCountByType(aType);
6026 if (cInstances >= maxInstances)
6027 return setError(E_INVALIDARG,
6028 tr("Too many USB controllers of this type"));
6029
6030 ctrl.createObject();
6031
6032 rc = ctrl->init(this, aName, aType);
6033 if (FAILED(rc)) return rc;
6034
6035 i_setModified(IsModified_USB);
6036 mUSBControllers.backup();
6037 mUSBControllers->push_back(ctrl);
6038
6039 ctrl.queryInterfaceTo(aController.asOutParam());
6040
6041 /* inform the direct session if any */
6042 alock.release();
6043 i_onUSBControllerChange();
6044
6045 return S_OK;
6046}
6047
6048HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6049{
6050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6051
6052 ComObjPtr<USBController> ctrl;
6053
6054 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6055 if (SUCCEEDED(rc))
6056 ctrl.queryInterfaceTo(aController.asOutParam());
6057
6058 return rc;
6059}
6060
6061HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6062 ULONG *aControllers)
6063{
6064 if ( (aType <= USBControllerType_Null)
6065 || (aType >= USBControllerType_Last))
6066 return setError(E_INVALIDARG,
6067 tr("Invalid USB controller type: %d"),
6068 aType);
6069
6070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 ComObjPtr<USBController> ctrl;
6073
6074 *aControllers = i_getUSBControllerCountByType(aType);
6075
6076 return S_OK;
6077}
6078
6079HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6080{
6081
6082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6083
6084 HRESULT rc = i_checkStateDependency(MutableStateDep);
6085 if (FAILED(rc)) return rc;
6086
6087 ComObjPtr<USBController> ctrl;
6088 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6089 if (FAILED(rc)) return rc;
6090
6091 i_setModified(IsModified_USB);
6092 mUSBControllers.backup();
6093
6094 ctrl->i_unshare();
6095
6096 mUSBControllers->remove(ctrl);
6097
6098 /* inform the direct session if any */
6099 alock.release();
6100 i_onUSBControllerChange();
6101
6102 return S_OK;
6103}
6104
6105HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6106 ULONG *aOriginX,
6107 ULONG *aOriginY,
6108 ULONG *aWidth,
6109 ULONG *aHeight,
6110 BOOL *aEnabled)
6111{
6112 uint32_t u32OriginX= 0;
6113 uint32_t u32OriginY= 0;
6114 uint32_t u32Width = 0;
6115 uint32_t u32Height = 0;
6116 uint16_t u16Flags = 0;
6117
6118 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6119 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6120 if (RT_FAILURE(vrc))
6121 {
6122#ifdef RT_OS_WINDOWS
6123 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6124 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6125 * So just assign fEnable to TRUE again.
6126 * The right fix would be to change GUI API wrappers to make sure that parameters
6127 * are changed only if API succeeds.
6128 */
6129 *aEnabled = TRUE;
6130#endif
6131 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6132 tr("Saved guest size is not available (%Rrc)"),
6133 vrc);
6134 }
6135
6136 *aOriginX = u32OriginX;
6137 *aOriginY = u32OriginY;
6138 *aWidth = u32Width;
6139 *aHeight = u32Height;
6140 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6141
6142 return S_OK;
6143}
6144
6145HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6146 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6147{
6148 if (aScreenId != 0)
6149 return E_NOTIMPL;
6150
6151 if ( aBitmapFormat != BitmapFormat_BGR0
6152 && aBitmapFormat != BitmapFormat_BGRA
6153 && aBitmapFormat != BitmapFormat_RGBA
6154 && aBitmapFormat != BitmapFormat_PNG)
6155 return setError(E_NOTIMPL,
6156 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6157
6158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6159
6160 uint8_t *pu8Data = NULL;
6161 uint32_t cbData = 0;
6162 uint32_t u32Width = 0;
6163 uint32_t u32Height = 0;
6164
6165 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6166
6167 if (RT_FAILURE(vrc))
6168 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6169 tr("Saved thumbnail data is not available (%Rrc)"),
6170 vrc);
6171
6172 HRESULT hr = S_OK;
6173
6174 *aWidth = u32Width;
6175 *aHeight = u32Height;
6176
6177 if (cbData > 0)
6178 {
6179 /* Convert pixels to the format expected by the API caller. */
6180 if (aBitmapFormat == BitmapFormat_BGR0)
6181 {
6182 /* [0] B, [1] G, [2] R, [3] 0. */
6183 aData.resize(cbData);
6184 memcpy(&aData.front(), pu8Data, cbData);
6185 }
6186 else if (aBitmapFormat == BitmapFormat_BGRA)
6187 {
6188 /* [0] B, [1] G, [2] R, [3] A. */
6189 aData.resize(cbData);
6190 for (uint32_t i = 0; i < cbData; i += 4)
6191 {
6192 aData[i] = pu8Data[i];
6193 aData[i + 1] = pu8Data[i + 1];
6194 aData[i + 2] = pu8Data[i + 2];
6195 aData[i + 3] = 0xff;
6196 }
6197 }
6198 else if (aBitmapFormat == BitmapFormat_RGBA)
6199 {
6200 /* [0] R, [1] G, [2] B, [3] A. */
6201 aData.resize(cbData);
6202 for (uint32_t i = 0; i < cbData; i += 4)
6203 {
6204 aData[i] = pu8Data[i + 2];
6205 aData[i + 1] = pu8Data[i + 1];
6206 aData[i + 2] = pu8Data[i];
6207 aData[i + 3] = 0xff;
6208 }
6209 }
6210 else if (aBitmapFormat == BitmapFormat_PNG)
6211 {
6212 uint8_t *pu8PNG = NULL;
6213 uint32_t cbPNG = 0;
6214 uint32_t cxPNG = 0;
6215 uint32_t cyPNG = 0;
6216
6217 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6218
6219 if (RT_SUCCESS(vrc))
6220 {
6221 aData.resize(cbPNG);
6222 if (cbPNG)
6223 memcpy(&aData.front(), pu8PNG, cbPNG);
6224 }
6225 else
6226 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6227 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6228 vrc);
6229
6230 RTMemFree(pu8PNG);
6231 }
6232 }
6233
6234 freeSavedDisplayScreenshot(pu8Data);
6235
6236 return hr;
6237}
6238
6239HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6240 ULONG *aWidth,
6241 ULONG *aHeight,
6242 std::vector<BitmapFormat_T> &aBitmapFormats)
6243{
6244 if (aScreenId != 0)
6245 return E_NOTIMPL;
6246
6247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6248
6249 uint8_t *pu8Data = NULL;
6250 uint32_t cbData = 0;
6251 uint32_t u32Width = 0;
6252 uint32_t u32Height = 0;
6253
6254 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6255
6256 if (RT_FAILURE(vrc))
6257 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6258 tr("Saved screenshot data is not available (%Rrc)"),
6259 vrc);
6260
6261 *aWidth = u32Width;
6262 *aHeight = u32Height;
6263 aBitmapFormats.resize(1);
6264 aBitmapFormats[0] = BitmapFormat_PNG;
6265
6266 freeSavedDisplayScreenshot(pu8Data);
6267
6268 return S_OK;
6269}
6270
6271HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6272 BitmapFormat_T aBitmapFormat,
6273 ULONG *aWidth,
6274 ULONG *aHeight,
6275 std::vector<BYTE> &aData)
6276{
6277 if (aScreenId != 0)
6278 return E_NOTIMPL;
6279
6280 if (aBitmapFormat != BitmapFormat_PNG)
6281 return E_NOTIMPL;
6282
6283 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6284
6285 uint8_t *pu8Data = NULL;
6286 uint32_t cbData = 0;
6287 uint32_t u32Width = 0;
6288 uint32_t u32Height = 0;
6289
6290 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6291
6292 if (RT_FAILURE(vrc))
6293 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6294 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6295 vrc);
6296
6297 *aWidth = u32Width;
6298 *aHeight = u32Height;
6299
6300 aData.resize(cbData);
6301 if (cbData)
6302 memcpy(&aData.front(), pu8Data, cbData);
6303
6304 freeSavedDisplayScreenshot(pu8Data);
6305
6306 return S_OK;
6307}
6308
6309HRESULT Machine::hotPlugCPU(ULONG aCpu)
6310{
6311 HRESULT rc = S_OK;
6312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6313
6314 if (!mHWData->mCPUHotPlugEnabled)
6315 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6316
6317 if (aCpu >= mHWData->mCPUCount)
6318 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6319
6320 if (mHWData->mCPUAttached[aCpu])
6321 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6322
6323 alock.release();
6324 rc = i_onCPUChange(aCpu, false);
6325 alock.acquire();
6326 if (FAILED(rc)) return rc;
6327
6328 i_setModified(IsModified_MachineData);
6329 mHWData.backup();
6330 mHWData->mCPUAttached[aCpu] = true;
6331
6332 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6333 if (Global::IsOnline(mData->mMachineState))
6334 i_saveSettings(NULL);
6335
6336 return S_OK;
6337}
6338
6339HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6340{
6341 HRESULT rc = S_OK;
6342
6343 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6344
6345 if (!mHWData->mCPUHotPlugEnabled)
6346 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6347
6348 if (aCpu >= SchemaDefs::MaxCPUCount)
6349 return setError(E_INVALIDARG,
6350 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6351 SchemaDefs::MaxCPUCount);
6352
6353 if (!mHWData->mCPUAttached[aCpu])
6354 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6355
6356 /* CPU 0 can't be detached */
6357 if (aCpu == 0)
6358 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6359
6360 alock.release();
6361 rc = i_onCPUChange(aCpu, true);
6362 alock.acquire();
6363 if (FAILED(rc)) return rc;
6364
6365 i_setModified(IsModified_MachineData);
6366 mHWData.backup();
6367 mHWData->mCPUAttached[aCpu] = false;
6368
6369 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6370 if (Global::IsOnline(mData->mMachineState))
6371 i_saveSettings(NULL);
6372
6373 return S_OK;
6374}
6375
6376HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6377{
6378 *aAttached = false;
6379
6380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6381
6382 /* If hotplug is enabled the CPU is always enabled. */
6383 if (!mHWData->mCPUHotPlugEnabled)
6384 {
6385 if (aCpu < mHWData->mCPUCount)
6386 *aAttached = true;
6387 }
6388 else
6389 {
6390 if (aCpu < SchemaDefs::MaxCPUCount)
6391 *aAttached = mHWData->mCPUAttached[aCpu];
6392 }
6393
6394 return S_OK;
6395}
6396
6397HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6398{
6399 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6400
6401 Utf8Str log = i_getLogFilename(aIdx);
6402 if (!RTFileExists(log.c_str()))
6403 log.setNull();
6404 aFilename = log;
6405
6406 return S_OK;
6407}
6408
6409HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6410{
6411 if (aSize < 0)
6412 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6413
6414 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6415
6416 HRESULT rc = S_OK;
6417 Utf8Str log = i_getLogFilename(aIdx);
6418
6419 /* do not unnecessarily hold the lock while doing something which does
6420 * not need the lock and potentially takes a long time. */
6421 alock.release();
6422
6423 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6424 * keeps the SOAP reply size under 1M for the webservice (we're using
6425 * base64 encoded strings for binary data for years now, avoiding the
6426 * expansion of each byte array element to approx. 25 bytes of XML. */
6427 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6428 aData.resize(cbData);
6429
6430 RTFILE LogFile;
6431 int vrc = RTFileOpen(&LogFile, log.c_str(),
6432 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6433 if (RT_SUCCESS(vrc))
6434 {
6435 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6436 if (RT_SUCCESS(vrc))
6437 aData.resize(cbData);
6438 else
6439 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6440 tr("Could not read log file '%s' (%Rrc)"),
6441 log.c_str(), vrc);
6442 RTFileClose(LogFile);
6443 }
6444 else
6445 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6446 tr("Could not open log file '%s' (%Rrc)"),
6447 log.c_str(), vrc);
6448
6449 if (FAILED(rc))
6450 aData.resize(0);
6451
6452 return rc;
6453}
6454
6455
6456/**
6457 * Currently this method doesn't attach device to the running VM,
6458 * just makes sure it's plugged on next VM start.
6459 */
6460HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6461{
6462 // lock scope
6463 {
6464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6465
6466 HRESULT rc = i_checkStateDependency(MutableStateDep);
6467 if (FAILED(rc)) return rc;
6468
6469 ChipsetType_T aChipset = ChipsetType_PIIX3;
6470 COMGETTER(ChipsetType)(&aChipset);
6471
6472 if (aChipset != ChipsetType_ICH9)
6473 {
6474 return setError(E_INVALIDARG,
6475 tr("Host PCI attachment only supported with ICH9 chipset"));
6476 }
6477
6478 // check if device with this host PCI address already attached
6479 for (HWData::PCIDeviceAssignmentList::const_iterator
6480 it = mHWData->mPCIDeviceAssignments.begin();
6481 it != mHWData->mPCIDeviceAssignments.end();
6482 ++it)
6483 {
6484 LONG iHostAddress = -1;
6485 ComPtr<PCIDeviceAttachment> pAttach;
6486 pAttach = *it;
6487 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6488 if (iHostAddress == aHostAddress)
6489 return setError(E_INVALIDARG,
6490 tr("Device with host PCI address already attached to this VM"));
6491 }
6492
6493 ComObjPtr<PCIDeviceAttachment> pda;
6494 char name[32];
6495
6496 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6497 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6498 pda.createObject();
6499 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6500 i_setModified(IsModified_MachineData);
6501 mHWData.backup();
6502 mHWData->mPCIDeviceAssignments.push_back(pda);
6503 }
6504
6505 return S_OK;
6506}
6507
6508/**
6509 * Currently this method doesn't detach device from the running VM,
6510 * just makes sure it's not plugged on next VM start.
6511 */
6512HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6513{
6514 ComObjPtr<PCIDeviceAttachment> pAttach;
6515 bool fRemoved = false;
6516 HRESULT rc;
6517
6518 // lock scope
6519 {
6520 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6521
6522 rc = i_checkStateDependency(MutableStateDep);
6523 if (FAILED(rc)) return rc;
6524
6525 for (HWData::PCIDeviceAssignmentList::const_iterator
6526 it = mHWData->mPCIDeviceAssignments.begin();
6527 it != mHWData->mPCIDeviceAssignments.end();
6528 ++it)
6529 {
6530 LONG iHostAddress = -1;
6531 pAttach = *it;
6532 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6533 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6534 {
6535 i_setModified(IsModified_MachineData);
6536 mHWData.backup();
6537 mHWData->mPCIDeviceAssignments.remove(pAttach);
6538 fRemoved = true;
6539 break;
6540 }
6541 }
6542 }
6543
6544
6545 /* Fire event outside of the lock */
6546 if (fRemoved)
6547 {
6548 Assert(!pAttach.isNull());
6549 ComPtr<IEventSource> es;
6550 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6551 Assert(SUCCEEDED(rc));
6552 Bstr mid;
6553 rc = this->COMGETTER(Id)(mid.asOutParam());
6554 Assert(SUCCEEDED(rc));
6555 ::FireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6556 }
6557
6558 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6559 tr("No host PCI device %08x attached"),
6560 aHostAddress
6561 );
6562}
6563
6564HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6565{
6566 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6567
6568 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6569 size_t i = 0;
6570 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6571 it = mHWData->mPCIDeviceAssignments.begin();
6572 it != mHWData->mPCIDeviceAssignments.end();
6573 ++it, ++i)
6574 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6575
6576 return S_OK;
6577}
6578
6579HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6580{
6581 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6582
6583 return S_OK;
6584}
6585
6586HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6587{
6588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6591
6592 return S_OK;
6593}
6594
6595HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6596{
6597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6598 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6599 if (SUCCEEDED(hrc))
6600 {
6601 hrc = mHWData.backupEx();
6602 if (SUCCEEDED(hrc))
6603 {
6604 i_setModified(IsModified_MachineData);
6605 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6606 }
6607 }
6608 return hrc;
6609}
6610
6611HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6612{
6613 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6614 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6615 return S_OK;
6616}
6617
6618HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6619{
6620 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6621 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6622 if (SUCCEEDED(hrc))
6623 {
6624 hrc = mHWData.backupEx();
6625 if (SUCCEEDED(hrc))
6626 {
6627 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6628 if (SUCCEEDED(hrc))
6629 i_setModified(IsModified_MachineData);
6630 }
6631 }
6632 return hrc;
6633}
6634
6635HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6636{
6637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6638
6639 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6640
6641 return S_OK;
6642}
6643
6644HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6645{
6646 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6647 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6648 if (SUCCEEDED(hrc))
6649 {
6650 hrc = mHWData.backupEx();
6651 if (SUCCEEDED(hrc))
6652 {
6653 i_setModified(IsModified_MachineData);
6654 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6655 }
6656 }
6657 return hrc;
6658}
6659
6660HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6661{
6662 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6663
6664 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6665
6666 return S_OK;
6667}
6668
6669HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6670{
6671 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6672
6673 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6674 if ( SUCCEEDED(hrc)
6675 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6676 {
6677 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6678 int vrc;
6679
6680 if (aAutostartEnabled)
6681 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6682 else
6683 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6684
6685 if (RT_SUCCESS(vrc))
6686 {
6687 hrc = mHWData.backupEx();
6688 if (SUCCEEDED(hrc))
6689 {
6690 i_setModified(IsModified_MachineData);
6691 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6692 }
6693 }
6694 else if (vrc == VERR_NOT_SUPPORTED)
6695 hrc = setError(VBOX_E_NOT_SUPPORTED,
6696 tr("The VM autostart feature is not supported on this platform"));
6697 else if (vrc == VERR_PATH_NOT_FOUND)
6698 hrc = setError(E_FAIL,
6699 tr("The path to the autostart database is not set"));
6700 else
6701 hrc = setError(E_UNEXPECTED,
6702 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6703 aAutostartEnabled ? "Adding" : "Removing",
6704 mUserData->s.strName.c_str(), vrc);
6705 }
6706 return hrc;
6707}
6708
6709HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6710{
6711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6712
6713 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6714
6715 return S_OK;
6716}
6717
6718HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6719{
6720 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6721 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6722 if (SUCCEEDED(hrc))
6723 {
6724 hrc = mHWData.backupEx();
6725 if (SUCCEEDED(hrc))
6726 {
6727 i_setModified(IsModified_MachineData);
6728 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6729 }
6730 }
6731 return hrc;
6732}
6733
6734HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6735{
6736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6737
6738 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6739
6740 return S_OK;
6741}
6742
6743HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6744{
6745 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6746 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6747 if ( SUCCEEDED(hrc)
6748 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6749 {
6750 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6751 int vrc;
6752
6753 if (aAutostopType != AutostopType_Disabled)
6754 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6755 else
6756 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6757
6758 if (RT_SUCCESS(vrc))
6759 {
6760 hrc = mHWData.backupEx();
6761 if (SUCCEEDED(hrc))
6762 {
6763 i_setModified(IsModified_MachineData);
6764 mHWData->mAutostart.enmAutostopType = aAutostopType;
6765 }
6766 }
6767 else if (vrc == VERR_NOT_SUPPORTED)
6768 hrc = setError(VBOX_E_NOT_SUPPORTED,
6769 tr("The VM autostop feature is not supported on this platform"));
6770 else if (vrc == VERR_PATH_NOT_FOUND)
6771 hrc = setError(E_FAIL,
6772 tr("The path to the autostart database is not set"));
6773 else
6774 hrc = setError(E_UNEXPECTED,
6775 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6776 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6777 mUserData->s.strName.c_str(), vrc);
6778 }
6779 return hrc;
6780}
6781
6782HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6783{
6784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6785
6786 aDefaultFrontend = mHWData->mDefaultFrontend;
6787
6788 return S_OK;
6789}
6790
6791HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6792{
6793 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6794 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6795 if (SUCCEEDED(hrc))
6796 {
6797 hrc = mHWData.backupEx();
6798 if (SUCCEEDED(hrc))
6799 {
6800 i_setModified(IsModified_MachineData);
6801 mHWData->mDefaultFrontend = aDefaultFrontend;
6802 }
6803 }
6804 return hrc;
6805}
6806
6807HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6808{
6809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6810 size_t cbIcon = mUserData->s.ovIcon.size();
6811 aIcon.resize(cbIcon);
6812 if (cbIcon)
6813 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6814 return S_OK;
6815}
6816
6817HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6818{
6819 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6820 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6821 if (SUCCEEDED(hrc))
6822 {
6823 i_setModified(IsModified_MachineData);
6824 mUserData.backup();
6825 size_t cbIcon = aIcon.size();
6826 mUserData->s.ovIcon.resize(cbIcon);
6827 if (cbIcon)
6828 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6829 }
6830 return hrc;
6831}
6832
6833HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6834{
6835#ifdef VBOX_WITH_USB
6836 *aUSBProxyAvailable = true;
6837#else
6838 *aUSBProxyAvailable = false;
6839#endif
6840 return S_OK;
6841}
6842
6843HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6844{
6845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6846
6847 *aVMProcessPriority = mUserData->s.enmVMPriority;
6848
6849 return S_OK;
6850}
6851
6852HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6853{
6854 RT_NOREF(aVMProcessPriority);
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mUserData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mUserData->s.enmVMPriority = aVMProcessPriority;
6864 }
6865 }
6866 alock.release();
6867 if (SUCCEEDED(hrc))
6868 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
6869 return hrc;
6870}
6871
6872HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
6873 ComPtr<IProgress> &aProgress)
6874{
6875 ComObjPtr<Progress> pP;
6876 Progress *ppP = pP;
6877 IProgress *iP = static_cast<IProgress *>(ppP);
6878 IProgress **pProgress = &iP;
6879
6880 IMachine *pTarget = aTarget;
6881
6882 /* Convert the options. */
6883 RTCList<CloneOptions_T> optList;
6884 if (aOptions.size())
6885 for (size_t i = 0; i < aOptions.size(); ++i)
6886 optList.append(aOptions[i]);
6887
6888 if (optList.contains(CloneOptions_Link))
6889 {
6890 if (!i_isSnapshotMachine())
6891 return setError(E_INVALIDARG,
6892 tr("Linked clone can only be created from a snapshot"));
6893 if (aMode != CloneMode_MachineState)
6894 return setError(E_INVALIDARG,
6895 tr("Linked clone can only be created for a single machine state"));
6896 }
6897 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
6898
6899 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
6900
6901 HRESULT rc = pWorker->start(pProgress);
6902
6903 pP = static_cast<Progress *>(*pProgress);
6904 pP.queryInterfaceTo(aProgress.asOutParam());
6905
6906 return rc;
6907
6908}
6909
6910HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
6911 const com::Utf8Str &aType,
6912 ComPtr<IProgress> &aProgress)
6913{
6914 LogFlowThisFuncEnter();
6915
6916 ComObjPtr<Progress> ptrProgress;
6917 HRESULT hrc = ptrProgress.createObject();
6918 if (SUCCEEDED(hrc))
6919 {
6920 /* Initialize our worker task */
6921 MachineMoveVM *pTask = NULL;
6922 try
6923 {
6924 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
6925 }
6926 catch (std::bad_alloc &)
6927 {
6928 return E_OUTOFMEMORY;
6929 }
6930
6931 hrc = pTask->init();//no exceptions are thrown
6932
6933 if (SUCCEEDED(hrc))
6934 {
6935 hrc = pTask->createThread();
6936 pTask = NULL; /* Consumed by createThread(). */
6937 if (SUCCEEDED(hrc))
6938 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
6939 else
6940 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
6941 }
6942 else
6943 delete pTask;
6944 }
6945
6946 LogFlowThisFuncLeave();
6947 return hrc;
6948
6949}
6950
6951HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
6952{
6953 NOREF(aProgress);
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955
6956 // This check should always fail.
6957 HRESULT rc = i_checkStateDependency(MutableStateDep);
6958 if (FAILED(rc)) return rc;
6959
6960 AssertFailedReturn(E_NOTIMPL);
6961}
6962
6963HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
6964{
6965 NOREF(aSavedStateFile);
6966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6967
6968 // This check should always fail.
6969 HRESULT rc = i_checkStateDependency(MutableStateDep);
6970 if (FAILED(rc)) return rc;
6971
6972 AssertFailedReturn(E_NOTIMPL);
6973}
6974
6975HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
6976{
6977 NOREF(aFRemoveFile);
6978 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6979
6980 // This check should always fail.
6981 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
6982 if (FAILED(rc)) return rc;
6983
6984 AssertFailedReturn(E_NOTIMPL);
6985}
6986
6987// public methods for internal purposes
6988/////////////////////////////////////////////////////////////////////////////
6989
6990/**
6991 * Adds the given IsModified_* flag to the dirty flags of the machine.
6992 * This must be called either during i_loadSettings or under the machine write lock.
6993 * @param fl Flag
6994 * @param fAllowStateModification If state modifications are allowed.
6995 */
6996void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
6997{
6998 mData->flModifications |= fl;
6999 if (fAllowStateModification && i_isStateModificationAllowed())
7000 mData->mCurrentStateModified = true;
7001}
7002
7003/**
7004 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7005 * care of the write locking.
7006 *
7007 * @param fModification The flag to add.
7008 * @param fAllowStateModification If state modifications are allowed.
7009 */
7010void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7011{
7012 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7013 i_setModified(fModification, fAllowStateModification);
7014}
7015
7016/**
7017 * Saves the registry entry of this machine to the given configuration node.
7018 *
7019 * @param data Machine registry data.
7020 *
7021 * @note locks this object for reading.
7022 */
7023HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7024{
7025 AutoLimitedCaller autoCaller(this);
7026 AssertComRCReturnRC(autoCaller.rc());
7027
7028 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7029
7030 data.uuid = mData->mUuid;
7031 data.strSettingsFile = mData->m_strConfigFile;
7032
7033 return S_OK;
7034}
7035
7036/**
7037 * Calculates the absolute path of the given path taking the directory of the
7038 * machine settings file as the current directory.
7039 *
7040 * @param strPath Path to calculate the absolute path for.
7041 * @param aResult Where to put the result (used only on success, can be the
7042 * same Utf8Str instance as passed in @a aPath).
7043 * @return IPRT result.
7044 *
7045 * @note Locks this object for reading.
7046 */
7047int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7048{
7049 AutoCaller autoCaller(this);
7050 AssertComRCReturn(autoCaller.rc(), Global::vboxStatusCodeFromCOM(autoCaller.rc()));
7051
7052 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7053
7054 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7055
7056 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7057
7058 strSettingsDir.stripFilename();
7059 char szFolder[RTPATH_MAX];
7060 size_t cbFolder = sizeof(szFolder);
7061 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7062 if (RT_SUCCESS(vrc))
7063 aResult = szFolder;
7064
7065 return vrc;
7066}
7067
7068/**
7069 * Copies strSource to strTarget, making it relative to the machine folder
7070 * if it is a subdirectory thereof, or simply copying it otherwise.
7071 *
7072 * @param strSource Path to evaluate and copy.
7073 * @param strTarget Buffer to receive target path.
7074 *
7075 * @note Locks this object for reading.
7076 */
7077void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7078 Utf8Str &strTarget)
7079{
7080 AutoCaller autoCaller(this);
7081 AssertComRCReturn(autoCaller.rc(), (void)0);
7082
7083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7084
7085 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7086 // use strTarget as a temporary buffer to hold the machine settings dir
7087 strTarget = mData->m_strConfigFileFull;
7088 strTarget.stripFilename();
7089 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7090 {
7091 // is relative: then append what's left
7092 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7093 // for empty paths (only possible for subdirs) use "." to avoid
7094 // triggering default settings for not present config attributes.
7095 if (strTarget.isEmpty())
7096 strTarget = ".";
7097 }
7098 else
7099 // is not relative: then overwrite
7100 strTarget = strSource;
7101}
7102
7103/**
7104 * Returns the full path to the machine's log folder in the
7105 * \a aLogFolder argument.
7106 */
7107void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7108{
7109 AutoCaller autoCaller(this);
7110 AssertComRCReturnVoid(autoCaller.rc());
7111
7112 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7113
7114 char szTmp[RTPATH_MAX];
7115 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7116 if (RT_SUCCESS(vrc))
7117 {
7118 if (szTmp[0] && !mUserData.isNull())
7119 {
7120 char szTmp2[RTPATH_MAX];
7121 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7122 if (RT_SUCCESS(vrc))
7123 aLogFolder.printf("%s%c%s",
7124 szTmp2,
7125 RTPATH_DELIMITER,
7126 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7127 }
7128 else
7129 vrc = VERR_PATH_IS_RELATIVE;
7130 }
7131
7132 if (RT_FAILURE(vrc))
7133 {
7134 // fallback if VBOX_USER_LOGHOME is not set or invalid
7135 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7136 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7137 aLogFolder.append(RTPATH_DELIMITER);
7138 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7139 }
7140}
7141
7142/**
7143 * Returns the full path to the machine's log file for an given index.
7144 */
7145Utf8Str Machine::i_getLogFilename(ULONG idx)
7146{
7147 Utf8Str logFolder;
7148 getLogFolder(logFolder);
7149 Assert(logFolder.length());
7150
7151 Utf8Str log;
7152 if (idx == 0)
7153 log.printf("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7154#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7155 else if (idx == 1)
7156 log.printf("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7157 else
7158 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7159#else
7160 else
7161 log.printf("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7162#endif
7163 return log;
7164}
7165
7166/**
7167 * Returns the full path to the machine's hardened log file.
7168 */
7169Utf8Str Machine::i_getHardeningLogFilename(void)
7170{
7171 Utf8Str strFilename;
7172 getLogFolder(strFilename);
7173 Assert(strFilename.length());
7174 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7175 return strFilename;
7176}
7177
7178/**
7179 * Returns the default NVRAM filename based on the location of the VM config.
7180 * Note that this is a relative path.
7181 */
7182Utf8Str Machine::i_getDefaultNVRAMFilename()
7183{
7184 AutoCaller autoCaller(this);
7185 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7186
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188
7189 if ( mHWData->mFirmwareType == FirmwareType_BIOS
7190 || i_isSnapshotMachine())
7191 return Utf8Str::Empty;
7192
7193 Utf8Str strNVRAMFilePath = mData->m_strConfigFileFull;
7194 strNVRAMFilePath.stripPath();
7195 strNVRAMFilePath.stripSuffix();
7196 strNVRAMFilePath += ".nvram";
7197
7198 return strNVRAMFilePath;
7199}
7200
7201/**
7202 * Returns the NVRAM filename for a new snapshot. This intentionally works
7203 * similarly to the saved state file naming. Note that this is usually
7204 * a relative path, unless the snapshot folder is absolute.
7205 */
7206Utf8Str Machine::i_getSnapshotNVRAMFilename()
7207{
7208 AutoCaller autoCaller(this);
7209 AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty);
7210
7211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7212
7213 if (mHWData->mFirmwareType == FirmwareType_BIOS)
7214 return Utf8Str::Empty;
7215
7216 RTTIMESPEC ts;
7217 RTTimeNow(&ts);
7218 RTTIME time;
7219 RTTimeExplode(&time, &ts);
7220
7221 Utf8Str strNVRAMFilePath = mUserData->s.strSnapshotFolder;
7222 strNVRAMFilePath += RTPATH_DELIMITER;
7223 strNVRAMFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.nvram",
7224 time.i32Year, time.u8Month, time.u8MonthDay,
7225 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7226
7227 return strNVRAMFilePath;
7228}
7229
7230/**
7231 * Composes a unique saved state filename based on the current system time. The filename is
7232 * granular to the second so this will work so long as no more than one snapshot is taken on
7233 * a machine per second.
7234 *
7235 * Before version 4.1, we used this formula for saved state files:
7236 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7237 * which no longer works because saved state files can now be shared between the saved state of the
7238 * "saved" machine and an online snapshot, and the following would cause problems:
7239 * 1) save machine
7240 * 2) create online snapshot from that machine state --> reusing saved state file
7241 * 3) save machine again --> filename would be reused, breaking the online snapshot
7242 *
7243 * So instead we now use a timestamp.
7244 *
7245 * @param strStateFilePath
7246 */
7247
7248void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7249{
7250 AutoCaller autoCaller(this);
7251 AssertComRCReturnVoid(autoCaller.rc());
7252
7253 {
7254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7255 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7256 }
7257
7258 RTTIMESPEC ts;
7259 RTTimeNow(&ts);
7260 RTTIME time;
7261 RTTimeExplode(&time, &ts);
7262
7263 strStateFilePath += RTPATH_DELIMITER;
7264 strStateFilePath.appendPrintf("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7265 time.i32Year, time.u8Month, time.u8MonthDay,
7266 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7267}
7268
7269/**
7270 * Returns whether at least one USB controller is present for the VM.
7271 */
7272bool Machine::i_isUSBControllerPresent()
7273{
7274 AutoCaller autoCaller(this);
7275 AssertComRCReturn(autoCaller.rc(), false);
7276
7277 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7278
7279 return (mUSBControllers->size() > 0);
7280}
7281
7282
7283/**
7284 * @note Locks this object for writing, calls the client process
7285 * (inside the lock).
7286 */
7287HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7288 const Utf8Str &strFrontend,
7289 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7290 ProgressProxy *aProgress)
7291{
7292 LogFlowThisFuncEnter();
7293
7294 AssertReturn(aControl, E_FAIL);
7295 AssertReturn(aProgress, E_FAIL);
7296 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7297
7298 AutoCaller autoCaller(this);
7299 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7300
7301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7302
7303 if (!mData->mRegistered)
7304 return setError(E_UNEXPECTED,
7305 tr("The machine '%s' is not registered"),
7306 mUserData->s.strName.c_str());
7307
7308 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7309
7310 /* The process started when launching a VM with separate UI/VM processes is always
7311 * the UI process, i.e. needs special handling as it won't claim the session. */
7312 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7313
7314 if (fSeparate)
7315 {
7316 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7317 return setError(VBOX_E_INVALID_OBJECT_STATE,
7318 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7319 mUserData->s.strName.c_str());
7320 }
7321 else
7322 {
7323 if ( mData->mSession.mState == SessionState_Locked
7324 || mData->mSession.mState == SessionState_Spawning
7325 || mData->mSession.mState == SessionState_Unlocking)
7326 return setError(VBOX_E_INVALID_OBJECT_STATE,
7327 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7328 mUserData->s.strName.c_str());
7329
7330 /* may not be busy */
7331 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7332 }
7333
7334 /* Hardening logging */
7335#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7336 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7337 {
7338 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7339 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7340 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7341 {
7342 Utf8Str strStartupLogDir = strHardeningLogFile;
7343 strStartupLogDir.stripFilename();
7344 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7345 file without stripping the file. */
7346 }
7347 strSupHardeningLogArg.append(strHardeningLogFile);
7348
7349 /* Remove legacy log filename to avoid confusion. */
7350 Utf8Str strOldStartupLogFile;
7351 getLogFolder(strOldStartupLogFile);
7352 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7353 RTFileDelete(strOldStartupLogFile.c_str());
7354 }
7355#else
7356 Utf8Str strSupHardeningLogArg;
7357#endif
7358
7359 Utf8Str strAppOverride;
7360#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7361 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7362#endif
7363
7364 bool fUseVBoxSDS = false;
7365 Utf8Str strCanonicalName;
7366 if (false)
7367 { }
7368#ifdef VBOX_WITH_QTGUI
7369 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7370 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7371 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7372 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7373 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7374 {
7375 strCanonicalName = "GUI/Qt";
7376 fUseVBoxSDS = true;
7377 }
7378#endif
7379#ifdef VBOX_WITH_VBOXSDL
7380 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7381 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7382 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7383 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7384 {
7385 strCanonicalName = "GUI/SDL";
7386 fUseVBoxSDS = true;
7387 }
7388#endif
7389#ifdef VBOX_WITH_HEADLESS
7390 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7391 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7392 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7393 {
7394 strCanonicalName = "headless";
7395 }
7396#endif
7397 else
7398 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7399
7400 Utf8Str idStr = mData->mUuid.toString();
7401 Utf8Str const &strMachineName = mUserData->s.strName;
7402 RTPROCESS pid = NIL_RTPROCESS;
7403
7404#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7405 RT_NOREF(fUseVBoxSDS);
7406#else
7407 DWORD idCallerSession = ~(DWORD)0;
7408 if (fUseVBoxSDS)
7409 {
7410 /*
7411 * The VBoxSDS should be used for process launching the VM with
7412 * GUI only if the caller and the VBoxSDS are in different Windows
7413 * sessions and the caller in the interactive one.
7414 */
7415 fUseVBoxSDS = false;
7416
7417 /* Get windows session of the current process. The process token used
7418 due to several reasons:
7419 1. The token is absent for the current thread except someone set it
7420 for us.
7421 2. Needs to get the id of the session where the process is started.
7422 We only need to do this once, though. */
7423 static DWORD s_idCurrentSession = ~(DWORD)0;
7424 DWORD idCurrentSession = s_idCurrentSession;
7425 if (idCurrentSession == ~(DWORD)0)
7426 {
7427 HANDLE hCurrentProcessToken = NULL;
7428 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7429 {
7430 DWORD cbIgn = 0;
7431 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7432 s_idCurrentSession = idCurrentSession;
7433 else
7434 {
7435 idCurrentSession = ~(DWORD)0;
7436 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7437 }
7438 CloseHandle(hCurrentProcessToken);
7439 }
7440 else
7441 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7442 }
7443
7444 /* get the caller's session */
7445 HRESULT hrc = CoImpersonateClient();
7446 if (SUCCEEDED(hrc))
7447 {
7448 HANDLE hCallerThreadToken;
7449 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7450 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7451 &hCallerThreadToken))
7452 {
7453 SetLastError(NO_ERROR);
7454 DWORD cbIgn = 0;
7455 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7456 {
7457 /* Only need to use SDS if the session ID differs: */
7458 if (idCurrentSession != idCallerSession)
7459 {
7460 fUseVBoxSDS = false;
7461
7462 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7463 DWORD cbTokenGroups = 0;
7464 PTOKEN_GROUPS pTokenGroups = NULL;
7465 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7466 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7467 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7468 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7469 {
7470 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7471 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7472 PSID pInteractiveSid = NULL;
7473 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7474 {
7475 /* Iterate over the groups looking for the interactive SID: */
7476 fUseVBoxSDS = false;
7477 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7478 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7479 {
7480 fUseVBoxSDS = true;
7481 break;
7482 }
7483 FreeSid(pInteractiveSid);
7484 }
7485 }
7486 else
7487 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7488 RTMemTmpFree(pTokenGroups);
7489 }
7490 }
7491 else
7492 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7493 CloseHandle(hCallerThreadToken);
7494 }
7495 else
7496 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7497 CoRevertToSelf();
7498 }
7499 else
7500 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7501 }
7502 if (fUseVBoxSDS)
7503 {
7504 /* connect to VBoxSDS */
7505 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7506 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7507 if (FAILED(rc))
7508 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7509 strMachineName.c_str());
7510
7511 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7512 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7513 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7514 service to access the files. */
7515 rc = CoSetProxyBlanket(pVBoxSDS,
7516 RPC_C_AUTHN_DEFAULT,
7517 RPC_C_AUTHZ_DEFAULT,
7518 COLE_DEFAULT_PRINCIPAL,
7519 RPC_C_AUTHN_LEVEL_DEFAULT,
7520 RPC_C_IMP_LEVEL_IMPERSONATE,
7521 NULL,
7522 EOAC_DEFAULT);
7523 if (FAILED(rc))
7524 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7525
7526 size_t const cEnvVars = aEnvironmentChanges.size();
7527 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7528 for (size_t i = 0; i < cEnvVars; i++)
7529 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7530
7531 ULONG uPid = 0;
7532 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7533 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7534 idCallerSession, &uPid);
7535 if (FAILED(rc))
7536 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7537 pid = (RTPROCESS)uPid;
7538 }
7539 else
7540#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7541 {
7542 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7543 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7544 if (RT_FAILURE(vrc))
7545 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7546 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7547 }
7548
7549 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7550 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7551
7552 if (!fSeparate)
7553 {
7554 /*
7555 * Note that we don't release the lock here before calling the client,
7556 * because it doesn't need to call us back if called with a NULL argument.
7557 * Releasing the lock here is dangerous because we didn't prepare the
7558 * launch data yet, but the client we've just started may happen to be
7559 * too fast and call LockMachine() that will fail (because of PID, etc.),
7560 * so that the Machine will never get out of the Spawning session state.
7561 */
7562
7563 /* inform the session that it will be a remote one */
7564 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7565#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7566 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7567#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7568 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7569#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7570 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7571
7572 if (FAILED(rc))
7573 {
7574 /* restore the session state */
7575 mData->mSession.mState = SessionState_Unlocked;
7576 alock.release();
7577 mParent->i_addProcessToReap(pid);
7578 /* The failure may occur w/o any error info (from RPC), so provide one */
7579 return setError(VBOX_E_VM_ERROR,
7580 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7581 }
7582
7583 /* attach launch data to the machine */
7584 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7585 mData->mSession.mRemoteControls.push_back(aControl);
7586 mData->mSession.mProgress = aProgress;
7587 mData->mSession.mPID = pid;
7588 mData->mSession.mState = SessionState_Spawning;
7589 Assert(strCanonicalName.isNotEmpty());
7590 mData->mSession.mName = strCanonicalName;
7591 }
7592 else
7593 {
7594 /* For separate UI process we declare the launch as completed instantly, as the
7595 * actual headless VM start may or may not come. No point in remembering anything
7596 * yet, as what matters for us is when the headless VM gets started. */
7597 aProgress->i_notifyComplete(S_OK);
7598 }
7599
7600 alock.release();
7601 mParent->i_addProcessToReap(pid);
7602
7603 LogFlowThisFuncLeave();
7604 return S_OK;
7605}
7606
7607/**
7608 * Returns @c true if the given session machine instance has an open direct
7609 * session (and optionally also for direct sessions which are closing) and
7610 * returns the session control machine instance if so.
7611 *
7612 * Note that when the method returns @c false, the arguments remain unchanged.
7613 *
7614 * @param aMachine Session machine object.
7615 * @param aControl Direct session control object (optional).
7616 * @param aRequireVM If true then only allow VM sessions.
7617 * @param aAllowClosing If true then additionally a session which is currently
7618 * being closed will also be allowed.
7619 *
7620 * @note locks this object for reading.
7621 */
7622bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7623 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7624 bool aRequireVM /*= false*/,
7625 bool aAllowClosing /*= false*/)
7626{
7627 AutoLimitedCaller autoCaller(this);
7628 AssertComRCReturn(autoCaller.rc(), false);
7629
7630 /* just return false for inaccessible machines */
7631 if (getObjectState().getState() != ObjectState::Ready)
7632 return false;
7633
7634 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7635
7636 if ( ( mData->mSession.mState == SessionState_Locked
7637 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7638 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7639 )
7640 {
7641 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7642
7643 aMachine = mData->mSession.mMachine;
7644
7645 if (aControl != NULL)
7646 *aControl = mData->mSession.mDirectControl;
7647
7648 return true;
7649 }
7650
7651 return false;
7652}
7653
7654/**
7655 * Returns @c true if the given machine has an spawning direct session.
7656 *
7657 * @note locks this object for reading.
7658 */
7659bool Machine::i_isSessionSpawning()
7660{
7661 AutoLimitedCaller autoCaller(this);
7662 AssertComRCReturn(autoCaller.rc(), false);
7663
7664 /* just return false for inaccessible machines */
7665 if (getObjectState().getState() != ObjectState::Ready)
7666 return false;
7667
7668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7669
7670 if (mData->mSession.mState == SessionState_Spawning)
7671 return true;
7672
7673 return false;
7674}
7675
7676/**
7677 * Called from the client watcher thread to check for unexpected client process
7678 * death during Session_Spawning state (e.g. before it successfully opened a
7679 * direct session).
7680 *
7681 * On Win32 and on OS/2, this method is called only when we've got the
7682 * direct client's process termination notification, so it always returns @c
7683 * true.
7684 *
7685 * On other platforms, this method returns @c true if the client process is
7686 * terminated and @c false if it's still alive.
7687 *
7688 * @note Locks this object for writing.
7689 */
7690bool Machine::i_checkForSpawnFailure()
7691{
7692 AutoCaller autoCaller(this);
7693 if (!autoCaller.isOk())
7694 {
7695 /* nothing to do */
7696 LogFlowThisFunc(("Already uninitialized!\n"));
7697 return true;
7698 }
7699
7700 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7701
7702 if (mData->mSession.mState != SessionState_Spawning)
7703 {
7704 /* nothing to do */
7705 LogFlowThisFunc(("Not spawning any more!\n"));
7706 return true;
7707 }
7708
7709 HRESULT rc = S_OK;
7710
7711 /* PID not yet initialized, skip check. */
7712 if (mData->mSession.mPID == NIL_RTPROCESS)
7713 return false;
7714
7715 RTPROCSTATUS status;
7716 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7717
7718 if (vrc != VERR_PROCESS_RUNNING)
7719 {
7720 Utf8Str strExtraInfo;
7721
7722#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7723 /* If the startup logfile exists and is of non-zero length, tell the
7724 user to look there for more details to encourage them to attach it
7725 when reporting startup issues. */
7726 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7727 uint64_t cbStartupLogFile = 0;
7728 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7729 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7730 strExtraInfo.appendPrintf(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str());
7731#endif
7732
7733 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7734 rc = setError(E_FAIL,
7735 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7736 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7737 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7738 rc = setError(E_FAIL,
7739 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7740 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7741 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7742 rc = setError(E_FAIL,
7743 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7744 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7745 else
7746 rc = setErrorBoth(E_FAIL, vrc,
7747 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7748 i_getName().c_str(), vrc, strExtraInfo.c_str());
7749 }
7750
7751 if (FAILED(rc))
7752 {
7753 /* Close the remote session, remove the remote control from the list
7754 * and reset session state to Closed (@note keep the code in sync with
7755 * the relevant part in LockMachine()). */
7756
7757 Assert(mData->mSession.mRemoteControls.size() == 1);
7758 if (mData->mSession.mRemoteControls.size() == 1)
7759 {
7760 ErrorInfoKeeper eik;
7761 mData->mSession.mRemoteControls.front()->Uninitialize();
7762 }
7763
7764 mData->mSession.mRemoteControls.clear();
7765 mData->mSession.mState = SessionState_Unlocked;
7766
7767 /* finalize the progress after setting the state */
7768 if (!mData->mSession.mProgress.isNull())
7769 {
7770 mData->mSession.mProgress->notifyComplete(rc);
7771 mData->mSession.mProgress.setNull();
7772 }
7773
7774 mData->mSession.mPID = NIL_RTPROCESS;
7775
7776 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
7777 return true;
7778 }
7779
7780 return false;
7781}
7782
7783/**
7784 * Checks whether the machine can be registered. If so, commits and saves
7785 * all settings.
7786 *
7787 * @note Must be called from mParent's write lock. Locks this object and
7788 * children for writing.
7789 */
7790HRESULT Machine::i_prepareRegister()
7791{
7792 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7793
7794 AutoLimitedCaller autoCaller(this);
7795 AssertComRCReturnRC(autoCaller.rc());
7796
7797 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7798
7799 /* wait for state dependents to drop to zero */
7800 i_ensureNoStateDependencies();
7801
7802 if (!mData->mAccessible)
7803 return setError(VBOX_E_INVALID_OBJECT_STATE,
7804 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7805 mUserData->s.strName.c_str(),
7806 mData->mUuid.toString().c_str());
7807
7808 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7809
7810 if (mData->mRegistered)
7811 return setError(VBOX_E_INVALID_OBJECT_STATE,
7812 tr("The machine '%s' with UUID {%s} is already registered"),
7813 mUserData->s.strName.c_str(),
7814 mData->mUuid.toString().c_str());
7815
7816 HRESULT rc = S_OK;
7817
7818 // Ensure the settings are saved. If we are going to be registered and
7819 // no config file exists yet, create it by calling i_saveSettings() too.
7820 if ( (mData->flModifications)
7821 || (!mData->pMachineConfigFile->fileExists())
7822 )
7823 {
7824 rc = i_saveSettings(NULL);
7825 // no need to check whether VirtualBox.xml needs saving too since
7826 // we can't have a machine XML file rename pending
7827 if (FAILED(rc)) return rc;
7828 }
7829
7830 /* more config checking goes here */
7831
7832 if (SUCCEEDED(rc))
7833 {
7834 /* we may have had implicit modifications we want to fix on success */
7835 i_commit();
7836
7837 mData->mRegistered = true;
7838 }
7839 else
7840 {
7841 /* we may have had implicit modifications we want to cancel on failure*/
7842 i_rollback(false /* aNotify */);
7843 }
7844
7845 return rc;
7846}
7847
7848/**
7849 * Increases the number of objects dependent on the machine state or on the
7850 * registered state. Guarantees that these two states will not change at least
7851 * until #i_releaseStateDependency() is called.
7852 *
7853 * Depending on the @a aDepType value, additional state checks may be made.
7854 * These checks will set extended error info on failure. See
7855 * #i_checkStateDependency() for more info.
7856 *
7857 * If this method returns a failure, the dependency is not added and the caller
7858 * is not allowed to rely on any particular machine state or registration state
7859 * value and may return the failed result code to the upper level.
7860 *
7861 * @param aDepType Dependency type to add.
7862 * @param aState Current machine state (NULL if not interested).
7863 * @param aRegistered Current registered state (NULL if not interested).
7864 *
7865 * @note Locks this object for writing.
7866 */
7867HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7868 MachineState_T *aState /* = NULL */,
7869 BOOL *aRegistered /* = NULL */)
7870{
7871 AutoCaller autoCaller(this);
7872 AssertComRCReturnRC(autoCaller.rc());
7873
7874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7875
7876 HRESULT rc = i_checkStateDependency(aDepType);
7877 if (FAILED(rc)) return rc;
7878
7879 {
7880 if (mData->mMachineStateChangePending != 0)
7881 {
7882 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7883 * drop to zero so don't add more. It may make sense to wait a bit
7884 * and retry before reporting an error (since the pending state
7885 * transition should be really quick) but let's just assert for
7886 * now to see if it ever happens on practice. */
7887
7888 AssertFailed();
7889
7890 return setError(E_ACCESSDENIED,
7891 tr("Machine state change is in progress. Please retry the operation later."));
7892 }
7893
7894 ++mData->mMachineStateDeps;
7895 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7896 }
7897
7898 if (aState)
7899 *aState = mData->mMachineState;
7900 if (aRegistered)
7901 *aRegistered = mData->mRegistered;
7902
7903 return S_OK;
7904}
7905
7906/**
7907 * Decreases the number of objects dependent on the machine state.
7908 * Must always complete the #i_addStateDependency() call after the state
7909 * dependency is no more necessary.
7910 */
7911void Machine::i_releaseStateDependency()
7912{
7913 AutoCaller autoCaller(this);
7914 AssertComRCReturnVoid(autoCaller.rc());
7915
7916 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7917
7918 /* releaseStateDependency() w/o addStateDependency()? */
7919 AssertReturnVoid(mData->mMachineStateDeps != 0);
7920 -- mData->mMachineStateDeps;
7921
7922 if (mData->mMachineStateDeps == 0)
7923 {
7924 /* inform i_ensureNoStateDependencies() that there are no more deps */
7925 if (mData->mMachineStateChangePending != 0)
7926 {
7927 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
7928 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
7929 }
7930 }
7931}
7932
7933Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
7934{
7935 /* start with nothing found */
7936 Utf8Str strResult("");
7937
7938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7939
7940 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
7941 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
7942 // found:
7943 strResult = it->second; // source is a Utf8Str
7944
7945 return strResult;
7946}
7947
7948// protected methods
7949/////////////////////////////////////////////////////////////////////////////
7950
7951/**
7952 * Performs machine state checks based on the @a aDepType value. If a check
7953 * fails, this method will set extended error info, otherwise it will return
7954 * S_OK. It is supposed, that on failure, the caller will immediately return
7955 * the return value of this method to the upper level.
7956 *
7957 * When @a aDepType is AnyStateDep, this method always returns S_OK.
7958 *
7959 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
7960 * current state of this machine object allows to change settings of the
7961 * machine (i.e. the machine is not registered, or registered but not running
7962 * and not saved). It is useful to call this method from Machine setters
7963 * before performing any change.
7964 *
7965 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
7966 * as for MutableStateDep except that if the machine is saved, S_OK is also
7967 * returned. This is useful in setters which allow changing machine
7968 * properties when it is in the saved state.
7969 *
7970 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
7971 * if the current state of this machine object allows to change runtime
7972 * changeable settings of the machine (i.e. the machine is not registered, or
7973 * registered but either running or not running and not saved). It is useful
7974 * to call this method from Machine setters before performing any changes to
7975 * runtime changeable settings.
7976 *
7977 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
7978 * the same as for MutableOrRunningStateDep except that if the machine is
7979 * saved, S_OK is also returned. This is useful in setters which allow
7980 * changing runtime and saved state changeable machine properties.
7981 *
7982 * @param aDepType Dependency type to check.
7983 *
7984 * @note Non Machine based classes should use #i_addStateDependency() and
7985 * #i_releaseStateDependency() methods or the smart AutoStateDependency
7986 * template.
7987 *
7988 * @note This method must be called from under this object's read or write
7989 * lock.
7990 */
7991HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
7992{
7993 switch (aDepType)
7994 {
7995 case AnyStateDep:
7996 {
7997 break;
7998 }
7999 case MutableStateDep:
8000 {
8001 if ( mData->mRegistered
8002 && ( !i_isSessionMachine()
8003 || ( mData->mMachineState != MachineState_Aborted
8004 && mData->mMachineState != MachineState_Teleported
8005 && mData->mMachineState != MachineState_PoweredOff
8006 )
8007 )
8008 )
8009 return setError(VBOX_E_INVALID_VM_STATE,
8010 tr("The machine is not mutable (state is %s)"),
8011 Global::stringifyMachineState(mData->mMachineState));
8012 break;
8013 }
8014 case MutableOrSavedStateDep:
8015 {
8016 if ( mData->mRegistered
8017 && ( !i_isSessionMachine()
8018 || ( mData->mMachineState != MachineState_Aborted
8019 && mData->mMachineState != MachineState_Teleported
8020 && mData->mMachineState != MachineState_Saved
8021 && mData->mMachineState != MachineState_PoweredOff
8022 )
8023 )
8024 )
8025 return setError(VBOX_E_INVALID_VM_STATE,
8026 tr("The machine is not mutable or saved (state is %s)"),
8027 Global::stringifyMachineState(mData->mMachineState));
8028 break;
8029 }
8030 case MutableOrRunningStateDep:
8031 {
8032 if ( mData->mRegistered
8033 && ( !i_isSessionMachine()
8034 || ( mData->mMachineState != MachineState_Aborted
8035 && mData->mMachineState != MachineState_Teleported
8036 && mData->mMachineState != MachineState_PoweredOff
8037 && !Global::IsOnline(mData->mMachineState)
8038 )
8039 )
8040 )
8041 return setError(VBOX_E_INVALID_VM_STATE,
8042 tr("The machine is not mutable or running (state is %s)"),
8043 Global::stringifyMachineState(mData->mMachineState));
8044 break;
8045 }
8046 case MutableOrSavedOrRunningStateDep:
8047 {
8048 if ( mData->mRegistered
8049 && ( !i_isSessionMachine()
8050 || ( mData->mMachineState != MachineState_Aborted
8051 && mData->mMachineState != MachineState_Teleported
8052 && mData->mMachineState != MachineState_Saved
8053 && mData->mMachineState != MachineState_PoweredOff
8054 && !Global::IsOnline(mData->mMachineState)
8055 )
8056 )
8057 )
8058 return setError(VBOX_E_INVALID_VM_STATE,
8059 tr("The machine is not mutable, saved or running (state is %s)"),
8060 Global::stringifyMachineState(mData->mMachineState));
8061 break;
8062 }
8063 }
8064
8065 return S_OK;
8066}
8067
8068/**
8069 * Helper to initialize all associated child objects and allocate data
8070 * structures.
8071 *
8072 * This method must be called as a part of the object's initialization procedure
8073 * (usually done in the #init() method).
8074 *
8075 * @note Must be called only from #init() or from #i_registeredInit().
8076 */
8077HRESULT Machine::initDataAndChildObjects()
8078{
8079 AutoCaller autoCaller(this);
8080 AssertComRCReturnRC(autoCaller.rc());
8081 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8082 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8083
8084 AssertReturn(!mData->mAccessible, E_FAIL);
8085
8086 /* allocate data structures */
8087 mSSData.allocate();
8088 mUserData.allocate();
8089 mHWData.allocate();
8090 mMediumAttachments.allocate();
8091 mStorageControllers.allocate();
8092 mUSBControllers.allocate();
8093
8094 /* initialize mOSTypeId */
8095 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8096
8097/** @todo r=bird: init() methods never fails, right? Why don't we make them
8098 * return void then! */
8099
8100 /* create associated BIOS settings object */
8101 unconst(mBIOSSettings).createObject();
8102 mBIOSSettings->init(this);
8103
8104 /* create associated record settings object */
8105 unconst(mRecordingSettings).createObject();
8106 mRecordingSettings->init(this);
8107
8108 /* create the graphics adapter object (always present) */
8109 unconst(mGraphicsAdapter).createObject();
8110 mGraphicsAdapter->init(this);
8111
8112 /* create an associated VRDE object (default is disabled) */
8113 unconst(mVRDEServer).createObject();
8114 mVRDEServer->init(this);
8115
8116 /* create associated serial port objects */
8117 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8118 {
8119 unconst(mSerialPorts[slot]).createObject();
8120 mSerialPorts[slot]->init(this, slot);
8121 }
8122
8123 /* create associated parallel port objects */
8124 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8125 {
8126 unconst(mParallelPorts[slot]).createObject();
8127 mParallelPorts[slot]->init(this, slot);
8128 }
8129
8130 /* create the audio adapter object (always present, default is disabled) */
8131 unconst(mAudioAdapter).createObject();
8132 mAudioAdapter->init(this);
8133
8134 /* create the USB device filters object (always present) */
8135 unconst(mUSBDeviceFilters).createObject();
8136 mUSBDeviceFilters->init(this);
8137
8138 /* create associated network adapter objects */
8139 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8140 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8141 {
8142 unconst(mNetworkAdapters[slot]).createObject();
8143 mNetworkAdapters[slot]->init(this, slot);
8144 }
8145
8146 /* create the bandwidth control */
8147 unconst(mBandwidthControl).createObject();
8148 mBandwidthControl->init(this);
8149
8150 return S_OK;
8151}
8152
8153/**
8154 * Helper to uninitialize all associated child objects and to free all data
8155 * structures.
8156 *
8157 * This method must be called as a part of the object's uninitialization
8158 * procedure (usually done in the #uninit() method).
8159 *
8160 * @note Must be called only from #uninit() or from #i_registeredInit().
8161 */
8162void Machine::uninitDataAndChildObjects()
8163{
8164 AutoCaller autoCaller(this);
8165 AssertComRCReturnVoid(autoCaller.rc());
8166 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8167 || getObjectState().getState() == ObjectState::Limited);
8168
8169 /* tell all our other child objects we've been uninitialized */
8170 if (mBandwidthControl)
8171 {
8172 mBandwidthControl->uninit();
8173 unconst(mBandwidthControl).setNull();
8174 }
8175
8176 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8177 {
8178 if (mNetworkAdapters[slot])
8179 {
8180 mNetworkAdapters[slot]->uninit();
8181 unconst(mNetworkAdapters[slot]).setNull();
8182 }
8183 }
8184
8185 if (mUSBDeviceFilters)
8186 {
8187 mUSBDeviceFilters->uninit();
8188 unconst(mUSBDeviceFilters).setNull();
8189 }
8190
8191 if (mAudioAdapter)
8192 {
8193 mAudioAdapter->uninit();
8194 unconst(mAudioAdapter).setNull();
8195 }
8196
8197 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8198 {
8199 if (mParallelPorts[slot])
8200 {
8201 mParallelPorts[slot]->uninit();
8202 unconst(mParallelPorts[slot]).setNull();
8203 }
8204 }
8205
8206 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8207 {
8208 if (mSerialPorts[slot])
8209 {
8210 mSerialPorts[slot]->uninit();
8211 unconst(mSerialPorts[slot]).setNull();
8212 }
8213 }
8214
8215 if (mVRDEServer)
8216 {
8217 mVRDEServer->uninit();
8218 unconst(mVRDEServer).setNull();
8219 }
8220
8221 if (mGraphicsAdapter)
8222 {
8223 mGraphicsAdapter->uninit();
8224 unconst(mGraphicsAdapter).setNull();
8225 }
8226
8227 if (mBIOSSettings)
8228 {
8229 mBIOSSettings->uninit();
8230 unconst(mBIOSSettings).setNull();
8231 }
8232
8233 if (mRecordingSettings)
8234 {
8235 mRecordingSettings->uninit();
8236 unconst(mRecordingSettings).setNull();
8237 }
8238
8239 /* Deassociate media (only when a real Machine or a SnapshotMachine
8240 * instance is uninitialized; SessionMachine instances refer to real
8241 * Machine media). This is necessary for a clean re-initialization of
8242 * the VM after successfully re-checking the accessibility state. Note
8243 * that in case of normal Machine or SnapshotMachine uninitialization (as
8244 * a result of unregistering or deleting the snapshot), outdated media
8245 * attachments will already be uninitialized and deleted, so this
8246 * code will not affect them. */
8247 if ( !mMediumAttachments.isNull()
8248 && !i_isSessionMachine()
8249 )
8250 {
8251 for (MediumAttachmentList::const_iterator
8252 it = mMediumAttachments->begin();
8253 it != mMediumAttachments->end();
8254 ++it)
8255 {
8256 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8257 if (pMedium.isNull())
8258 continue;
8259 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8260 AssertComRC(rc);
8261 }
8262 }
8263
8264 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8265 {
8266 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8267 if (mData->mFirstSnapshot)
8268 {
8269 // snapshots tree is protected by machine write lock; strictly
8270 // this isn't necessary here since we're deleting the entire
8271 // machine, but otherwise we assert in Snapshot::uninit()
8272 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8273 mData->mFirstSnapshot->uninit();
8274 mData->mFirstSnapshot.setNull();
8275 }
8276
8277 mData->mCurrentSnapshot.setNull();
8278 }
8279
8280 /* free data structures (the essential mData structure is not freed here
8281 * since it may be still in use) */
8282 mMediumAttachments.free();
8283 mStorageControllers.free();
8284 mUSBControllers.free();
8285 mHWData.free();
8286 mUserData.free();
8287 mSSData.free();
8288}
8289
8290/**
8291 * Returns a pointer to the Machine object for this machine that acts like a
8292 * parent for complex machine data objects such as shared folders, etc.
8293 *
8294 * For primary Machine objects and for SnapshotMachine objects, returns this
8295 * object's pointer itself. For SessionMachine objects, returns the peer
8296 * (primary) machine pointer.
8297 */
8298Machine *Machine::i_getMachine()
8299{
8300 if (i_isSessionMachine())
8301 return (Machine*)mPeer;
8302 return this;
8303}
8304
8305/**
8306 * Makes sure that there are no machine state dependents. If necessary, waits
8307 * for the number of dependents to drop to zero.
8308 *
8309 * Make sure this method is called from under this object's write lock to
8310 * guarantee that no new dependents may be added when this method returns
8311 * control to the caller.
8312 *
8313 * @note Locks this object for writing. The lock will be released while waiting
8314 * (if necessary).
8315 *
8316 * @warning To be used only in methods that change the machine state!
8317 */
8318void Machine::i_ensureNoStateDependencies()
8319{
8320 AssertReturnVoid(isWriteLockOnCurrentThread());
8321
8322 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8323
8324 /* Wait for all state dependents if necessary */
8325 if (mData->mMachineStateDeps != 0)
8326 {
8327 /* lazy semaphore creation */
8328 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8329 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8330
8331 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8332 mData->mMachineStateDeps));
8333
8334 ++mData->mMachineStateChangePending;
8335
8336 /* reset the semaphore before waiting, the last dependent will signal
8337 * it */
8338 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8339
8340 alock.release();
8341
8342 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8343
8344 alock.acquire();
8345
8346 -- mData->mMachineStateChangePending;
8347 }
8348}
8349
8350/**
8351 * Changes the machine state and informs callbacks.
8352 *
8353 * This method is not intended to fail so it either returns S_OK or asserts (and
8354 * returns a failure).
8355 *
8356 * @note Locks this object for writing.
8357 */
8358HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8359{
8360 LogFlowThisFuncEnter();
8361 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8362 Assert(aMachineState != MachineState_Null);
8363
8364 AutoCaller autoCaller(this);
8365 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8366
8367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8368
8369 /* wait for state dependents to drop to zero */
8370 i_ensureNoStateDependencies();
8371
8372 MachineState_T const enmOldState = mData->mMachineState;
8373 if (enmOldState != aMachineState)
8374 {
8375 mData->mMachineState = aMachineState;
8376 RTTimeNow(&mData->mLastStateChange);
8377
8378#ifdef VBOX_WITH_DTRACE_R3_MAIN
8379 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8380#endif
8381 mParent->i_onMachineStateChanged(mData->mUuid, aMachineState);
8382 }
8383
8384 LogFlowThisFuncLeave();
8385 return S_OK;
8386}
8387
8388/**
8389 * Searches for a shared folder with the given logical name
8390 * in the collection of shared folders.
8391 *
8392 * @param aName logical name of the shared folder
8393 * @param aSharedFolder where to return the found object
8394 * @param aSetError whether to set the error info if the folder is
8395 * not found
8396 * @return
8397 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8398 *
8399 * @note
8400 * must be called from under the object's lock!
8401 */
8402HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8403 ComObjPtr<SharedFolder> &aSharedFolder,
8404 bool aSetError /* = false */)
8405{
8406 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8407 for (HWData::SharedFolderList::const_iterator
8408 it = mHWData->mSharedFolders.begin();
8409 it != mHWData->mSharedFolders.end();
8410 ++it)
8411 {
8412 SharedFolder *pSF = *it;
8413 AutoCaller autoCaller(pSF);
8414 if (pSF->i_getName() == aName)
8415 {
8416 aSharedFolder = pSF;
8417 rc = S_OK;
8418 break;
8419 }
8420 }
8421
8422 if (aSetError && FAILED(rc))
8423 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8424
8425 return rc;
8426}
8427
8428/**
8429 * Initializes all machine instance data from the given settings structures
8430 * from XML. The exception is the machine UUID which needs special handling
8431 * depending on the caller's use case, so the caller needs to set that herself.
8432 *
8433 * This gets called in several contexts during machine initialization:
8434 *
8435 * -- When machine XML exists on disk already and needs to be loaded into memory,
8436 * for example, from #i_registeredInit() to load all registered machines on
8437 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8438 * attached to the machine should be part of some media registry already.
8439 *
8440 * -- During OVF import, when a machine config has been constructed from an
8441 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8442 * ensure that the media listed as attachments in the config (which have
8443 * been imported from the OVF) receive the correct registry ID.
8444 *
8445 * -- During VM cloning.
8446 *
8447 * @param config Machine settings from XML.
8448 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8449 * for each attached medium in the config.
8450 * @return
8451 */
8452HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8453 const Guid *puuidRegistry)
8454{
8455 // copy name, description, OS type, teleporter, UTC etc.
8456 mUserData->s = config.machineUserData;
8457
8458 // look up the object by Id to check it is valid
8459 ComObjPtr<GuestOSType> pGuestOSType;
8460 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8461 if (!pGuestOSType.isNull())
8462 mUserData->s.strOsType = pGuestOSType->i_id();
8463
8464 // stateFile (optional)
8465 if (config.strStateFile.isEmpty())
8466 mSSData->strStateFilePath.setNull();
8467 else
8468 {
8469 Utf8Str stateFilePathFull(config.strStateFile);
8470 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8471 if (RT_FAILURE(vrc))
8472 return setErrorBoth(E_FAIL, vrc,
8473 tr("Invalid saved state file path '%s' (%Rrc)"),
8474 config.strStateFile.c_str(),
8475 vrc);
8476 mSSData->strStateFilePath = stateFilePathFull;
8477 }
8478
8479 // snapshot folder needs special processing so set it again
8480 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8481 if (FAILED(rc)) return rc;
8482
8483 /* Copy the extra data items (config may or may not be the same as
8484 * mData->pMachineConfigFile) if necessary. When loading the XML files
8485 * from disk they are the same, but not for OVF import. */
8486 if (mData->pMachineConfigFile != &config)
8487 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8488
8489 /* currentStateModified (optional, default is true) */
8490 mData->mCurrentStateModified = config.fCurrentStateModified;
8491
8492 mData->mLastStateChange = config.timeLastStateChange;
8493
8494 /*
8495 * note: all mUserData members must be assigned prior this point because
8496 * we need to commit changes in order to let mUserData be shared by all
8497 * snapshot machine instances.
8498 */
8499 mUserData.commitCopy();
8500
8501 // machine registry, if present (must be loaded before snapshots)
8502 if (config.canHaveOwnMediaRegistry())
8503 {
8504 // determine machine folder
8505 Utf8Str strMachineFolder = i_getSettingsFileFull();
8506 strMachineFolder.stripFilename();
8507 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8508 config.mediaRegistry,
8509 strMachineFolder);
8510 if (FAILED(rc)) return rc;
8511 }
8512
8513 /* Snapshot node (optional) */
8514 size_t cRootSnapshots;
8515 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8516 {
8517 // there must be only one root snapshot
8518 Assert(cRootSnapshots == 1);
8519
8520 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8521
8522 rc = i_loadSnapshot(snap,
8523 config.uuidCurrentSnapshot,
8524 NULL); // no parent == first snapshot
8525 if (FAILED(rc)) return rc;
8526 }
8527
8528 // hardware data
8529 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8530 if (FAILED(rc)) return rc;
8531
8532 /*
8533 * NOTE: the assignment below must be the last thing to do,
8534 * otherwise it will be not possible to change the settings
8535 * somewhere in the code above because all setters will be
8536 * blocked by i_checkStateDependency(MutableStateDep).
8537 */
8538
8539 /* set the machine state to Aborted or Saved when appropriate */
8540 if (config.fAborted)
8541 {
8542 mSSData->strStateFilePath.setNull();
8543
8544 /* no need to use i_setMachineState() during init() */
8545 mData->mMachineState = MachineState_Aborted;
8546 }
8547 else if (!mSSData->strStateFilePath.isEmpty())
8548 {
8549 /* no need to use i_setMachineState() during init() */
8550 mData->mMachineState = MachineState_Saved;
8551 }
8552
8553 // after loading settings, we are no longer different from the XML on disk
8554 mData->flModifications = 0;
8555
8556 return S_OK;
8557}
8558
8559/**
8560 * Recursively loads all snapshots starting from the given.
8561 *
8562 * @param data snapshot settings.
8563 * @param aCurSnapshotId Current snapshot ID from the settings file.
8564 * @param aParentSnapshot Parent snapshot.
8565 */
8566HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8567 const Guid &aCurSnapshotId,
8568 Snapshot *aParentSnapshot)
8569{
8570 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8571 AssertReturn(!i_isSessionMachine(), E_FAIL);
8572
8573 HRESULT rc = S_OK;
8574
8575 Utf8Str strStateFile;
8576 if (!data.strStateFile.isEmpty())
8577 {
8578 /* optional */
8579 strStateFile = data.strStateFile;
8580 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8581 if (RT_FAILURE(vrc))
8582 return setErrorBoth(E_FAIL, vrc,
8583 tr("Invalid saved state file path '%s' (%Rrc)"),
8584 strStateFile.c_str(),
8585 vrc);
8586 }
8587
8588 /* create a snapshot machine object */
8589 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8590 pSnapshotMachine.createObject();
8591 rc = pSnapshotMachine->initFromSettings(this,
8592 data.hardware,
8593 &data.debugging,
8594 &data.autostart,
8595 data.uuid.ref(),
8596 strStateFile);
8597 if (FAILED(rc)) return rc;
8598
8599 /* create a snapshot object */
8600 ComObjPtr<Snapshot> pSnapshot;
8601 pSnapshot.createObject();
8602 /* initialize the snapshot */
8603 rc = pSnapshot->init(mParent, // VirtualBox object
8604 data.uuid,
8605 data.strName,
8606 data.strDescription,
8607 data.timestamp,
8608 pSnapshotMachine,
8609 aParentSnapshot);
8610 if (FAILED(rc)) return rc;
8611
8612 /* memorize the first snapshot if necessary */
8613 if (!mData->mFirstSnapshot)
8614 mData->mFirstSnapshot = pSnapshot;
8615
8616 /* memorize the current snapshot when appropriate */
8617 if ( !mData->mCurrentSnapshot
8618 && pSnapshot->i_getId() == aCurSnapshotId
8619 )
8620 mData->mCurrentSnapshot = pSnapshot;
8621
8622 // now create the children
8623 for (settings::SnapshotsList::const_iterator
8624 it = data.llChildSnapshots.begin();
8625 it != data.llChildSnapshots.end();
8626 ++it)
8627 {
8628 const settings::Snapshot &childData = *it;
8629 // recurse
8630 rc = i_loadSnapshot(childData,
8631 aCurSnapshotId,
8632 pSnapshot); // parent = the one we created above
8633 if (FAILED(rc)) return rc;
8634 }
8635
8636 return rc;
8637}
8638
8639/**
8640 * Loads settings into mHWData.
8641 *
8642 * @param puuidRegistry Registry ID.
8643 * @param puuidSnapshot Snapshot ID
8644 * @param data Reference to the hardware settings.
8645 * @param pDbg Pointer to the debugging settings.
8646 * @param pAutostart Pointer to the autostart settings.
8647 */
8648HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8649 const Guid *puuidSnapshot,
8650 const settings::Hardware &data,
8651 const settings::Debugging *pDbg,
8652 const settings::Autostart *pAutostart)
8653{
8654 AssertReturn(!i_isSessionMachine(), E_FAIL);
8655
8656 HRESULT rc = S_OK;
8657
8658 try
8659 {
8660 ComObjPtr<GuestOSType> pGuestOSType;
8661 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8662
8663 /* The hardware version attribute (optional). */
8664 mHWData->mHWVersion = data.strVersion;
8665 mHWData->mHardwareUUID = data.uuid;
8666
8667 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8668 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8669 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8670 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8671 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8672 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8673 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8674 mHWData->mPAEEnabled = data.fPAE;
8675 mHWData->mLongMode = data.enmLongMode;
8676 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8677 mHWData->mAPIC = data.fAPIC;
8678 mHWData->mX2APIC = data.fX2APIC;
8679 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8680 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8681 mHWData->mSpecCtrl = data.fSpecCtrl;
8682 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8683 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8684 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8685 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8686 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8687 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8688 mHWData->mCPUCount = data.cCPUs;
8689 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8690 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8691 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8692 mHWData->mCpuProfile = data.strCpuProfile;
8693
8694 // cpu
8695 if (mHWData->mCPUHotPlugEnabled)
8696 {
8697 for (settings::CpuList::const_iterator
8698 it = data.llCpus.begin();
8699 it != data.llCpus.end();
8700 ++it)
8701 {
8702 const settings::Cpu &cpu = *it;
8703
8704 mHWData->mCPUAttached[cpu.ulId] = true;
8705 }
8706 }
8707
8708 // cpuid leafs
8709 for (settings::CpuIdLeafsList::const_iterator
8710 it = data.llCpuIdLeafs.begin();
8711 it != data.llCpuIdLeafs.end();
8712 ++it)
8713 {
8714 const settings::CpuIdLeaf &rLeaf= *it;
8715 if ( rLeaf.idx < UINT32_C(0x20)
8716 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8717 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8718 mHWData->mCpuIdLeafList.push_back(rLeaf);
8719 /* else: just ignore */
8720 }
8721
8722 mHWData->mMemorySize = data.ulMemorySizeMB;
8723 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8724
8725 // boot order
8726 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8727 {
8728 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8729 if (it == data.mapBootOrder.end())
8730 mHWData->mBootOrder[i] = DeviceType_Null;
8731 else
8732 mHWData->mBootOrder[i] = it->second;
8733 }
8734
8735 mHWData->mFirmwareType = data.firmwareType;
8736 mHWData->mPointingHIDType = data.pointingHIDType;
8737 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8738 mHWData->mChipsetType = data.chipsetType;
8739 mHWData->mParavirtProvider = data.paravirtProvider;
8740 mHWData->mParavirtDebug = data.strParavirtDebug;
8741 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8742 mHWData->mHPETEnabled = data.fHPETEnabled;
8743
8744 /* GraphicsAdapter */
8745 rc = mGraphicsAdapter->i_loadSettings(data.graphicsAdapter);
8746 if (FAILED(rc)) return rc;
8747
8748 /* VRDEServer */
8749 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8750 if (FAILED(rc)) return rc;
8751
8752 /* BIOS */
8753 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8754 if (FAILED(rc)) return rc;
8755
8756 /* Recording settings */
8757 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8758 if (FAILED(rc)) return rc;
8759
8760 // Bandwidth control (must come before network adapters)
8761 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8762 if (FAILED(rc)) return rc;
8763
8764 /* USB controllers */
8765 for (settings::USBControllerList::const_iterator
8766 it = data.usbSettings.llUSBControllers.begin();
8767 it != data.usbSettings.llUSBControllers.end();
8768 ++it)
8769 {
8770 const settings::USBController &settingsCtrl = *it;
8771 ComObjPtr<USBController> newCtrl;
8772
8773 newCtrl.createObject();
8774 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8775 mUSBControllers->push_back(newCtrl);
8776 }
8777
8778 /* USB device filters */
8779 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8780 if (FAILED(rc)) return rc;
8781
8782 // network adapters (establish array size first and apply defaults, to
8783 // ensure reading the same settings as we saved, since the list skips
8784 // adapters having defaults)
8785 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8786 size_t oldCount = mNetworkAdapters.size();
8787 if (newCount > oldCount)
8788 {
8789 mNetworkAdapters.resize(newCount);
8790 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8791 {
8792 unconst(mNetworkAdapters[slot]).createObject();
8793 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8794 }
8795 }
8796 else if (newCount < oldCount)
8797 mNetworkAdapters.resize(newCount);
8798 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8799 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8800 for (settings::NetworkAdaptersList::const_iterator
8801 it = data.llNetworkAdapters.begin();
8802 it != data.llNetworkAdapters.end();
8803 ++it)
8804 {
8805 const settings::NetworkAdapter &nic = *it;
8806
8807 /* slot uniqueness is guaranteed by XML Schema */
8808 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8809 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8810 if (FAILED(rc)) return rc;
8811 }
8812
8813 // serial ports (establish defaults first, to ensure reading the same
8814 // settings as we saved, since the list skips ports having defaults)
8815 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8816 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8817 for (settings::SerialPortsList::const_iterator
8818 it = data.llSerialPorts.begin();
8819 it != data.llSerialPorts.end();
8820 ++it)
8821 {
8822 const settings::SerialPort &s = *it;
8823
8824 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8825 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8826 if (FAILED(rc)) return rc;
8827 }
8828
8829 // parallel ports (establish defaults first, to ensure reading the same
8830 // settings as we saved, since the list skips ports having defaults)
8831 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8832 mParallelPorts[i]->i_applyDefaults();
8833 for (settings::ParallelPortsList::const_iterator
8834 it = data.llParallelPorts.begin();
8835 it != data.llParallelPorts.end();
8836 ++it)
8837 {
8838 const settings::ParallelPort &p = *it;
8839
8840 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8841 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8842 if (FAILED(rc)) return rc;
8843 }
8844
8845 /* AudioAdapter */
8846 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8847 if (FAILED(rc)) return rc;
8848
8849 /* storage controllers */
8850 rc = i_loadStorageControllers(data.storage,
8851 puuidRegistry,
8852 puuidSnapshot);
8853 if (FAILED(rc)) return rc;
8854
8855 /* Shared folders */
8856 for (settings::SharedFoldersList::const_iterator
8857 it = data.llSharedFolders.begin();
8858 it != data.llSharedFolders.end();
8859 ++it)
8860 {
8861 const settings::SharedFolder &sf = *it;
8862
8863 ComObjPtr<SharedFolder> sharedFolder;
8864 /* Check for double entries. Not allowed! */
8865 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8866 if (SUCCEEDED(rc))
8867 return setError(VBOX_E_OBJECT_IN_USE,
8868 tr("Shared folder named '%s' already exists"),
8869 sf.strName.c_str());
8870
8871 /* Create the new shared folder. Don't break on error. This will be
8872 * reported when the machine starts. */
8873 sharedFolder.createObject();
8874 rc = sharedFolder->init(i_getMachine(),
8875 sf.strName,
8876 sf.strHostPath,
8877 RT_BOOL(sf.fWritable),
8878 RT_BOOL(sf.fAutoMount),
8879 sf.strAutoMountPoint,
8880 false /* fFailOnError */);
8881 if (FAILED(rc)) return rc;
8882 mHWData->mSharedFolders.push_back(sharedFolder);
8883 }
8884
8885 // Clipboard
8886 mHWData->mClipboardMode = data.clipboardMode;
8887 mHWData->mClipboardFileTransfersEnabled = data.fClipboardFileTransfersEnabled ? TRUE : FALSE;
8888
8889 // drag'n'drop
8890 mHWData->mDnDMode = data.dndMode;
8891
8892 // guest settings
8893 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8894
8895 // IO settings
8896 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8897 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8898
8899 // Host PCI devices
8900 for (settings::HostPCIDeviceAttachmentList::const_iterator
8901 it = data.pciAttachments.begin();
8902 it != data.pciAttachments.end();
8903 ++it)
8904 {
8905 const settings::HostPCIDeviceAttachment &hpda = *it;
8906 ComObjPtr<PCIDeviceAttachment> pda;
8907
8908 pda.createObject();
8909 pda->i_loadSettings(this, hpda);
8910 mHWData->mPCIDeviceAssignments.push_back(pda);
8911 }
8912
8913 /*
8914 * (The following isn't really real hardware, but it lives in HWData
8915 * for reasons of convenience.)
8916 */
8917
8918#ifdef VBOX_WITH_GUEST_PROPS
8919 /* Guest properties (optional) */
8920
8921 /* Only load transient guest properties for configs which have saved
8922 * state, because there shouldn't be any for powered off VMs. The same
8923 * logic applies for snapshots, as offline snapshots shouldn't have
8924 * any such properties. They confuse the code in various places.
8925 * Note: can't rely on the machine state, as it isn't set yet. */
8926 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8927 /* apologies for the hacky unconst() usage, but this needs hacking
8928 * actually inconsistent settings into consistency, otherwise there
8929 * will be some corner cases where the inconsistency survives
8930 * surprisingly long without getting fixed, especially for snapshots
8931 * as there are no config changes. */
8932 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
8933 for (settings::GuestPropertiesList::iterator
8934 it = llGuestProperties.begin();
8935 it != llGuestProperties.end();
8936 /*nothing*/)
8937 {
8938 const settings::GuestProperty &prop = *it;
8939 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
8940 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
8941 if ( fSkipTransientGuestProperties
8942 && ( fFlags & GUEST_PROP_F_TRANSIENT
8943 || fFlags & GUEST_PROP_F_TRANSRESET))
8944 {
8945 it = llGuestProperties.erase(it);
8946 continue;
8947 }
8948 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
8949 mHWData->mGuestProperties[prop.strName] = property;
8950 ++it;
8951 }
8952#endif /* VBOX_WITH_GUEST_PROPS defined */
8953
8954 rc = i_loadDebugging(pDbg);
8955 if (FAILED(rc))
8956 return rc;
8957
8958 mHWData->mAutostart = *pAutostart;
8959
8960 /* default frontend */
8961 mHWData->mDefaultFrontend = data.strDefaultFrontend;
8962 }
8963 catch (std::bad_alloc &)
8964 {
8965 return E_OUTOFMEMORY;
8966 }
8967
8968 AssertComRC(rc);
8969 return rc;
8970}
8971
8972/**
8973 * Called from i_loadHardware() to load the debugging settings of the
8974 * machine.
8975 *
8976 * @param pDbg Pointer to the settings.
8977 */
8978HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
8979{
8980 mHWData->mDebugging = *pDbg;
8981 /* no more processing currently required, this will probably change. */
8982 return S_OK;
8983}
8984
8985/**
8986 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
8987 *
8988 * @param data storage settings.
8989 * @param puuidRegistry media registry ID to set media to or NULL;
8990 * see Machine::i_loadMachineDataFromSettings()
8991 * @param puuidSnapshot snapshot ID
8992 * @return
8993 */
8994HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
8995 const Guid *puuidRegistry,
8996 const Guid *puuidSnapshot)
8997{
8998 AssertReturn(!i_isSessionMachine(), E_FAIL);
8999
9000 HRESULT rc = S_OK;
9001
9002 for (settings::StorageControllersList::const_iterator
9003 it = data.llStorageControllers.begin();
9004 it != data.llStorageControllers.end();
9005 ++it)
9006 {
9007 const settings::StorageController &ctlData = *it;
9008
9009 ComObjPtr<StorageController> pCtl;
9010 /* Try to find one with the name first. */
9011 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9012 if (SUCCEEDED(rc))
9013 return setError(VBOX_E_OBJECT_IN_USE,
9014 tr("Storage controller named '%s' already exists"),
9015 ctlData.strName.c_str());
9016
9017 pCtl.createObject();
9018 rc = pCtl->init(this,
9019 ctlData.strName,
9020 ctlData.storageBus,
9021 ctlData.ulInstance,
9022 ctlData.fBootable);
9023 if (FAILED(rc)) return rc;
9024
9025 mStorageControllers->push_back(pCtl);
9026
9027 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9028 if (FAILED(rc)) return rc;
9029
9030 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9031 if (FAILED(rc)) return rc;
9032
9033 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9034 if (FAILED(rc)) return rc;
9035
9036 /* Load the attached devices now. */
9037 rc = i_loadStorageDevices(pCtl,
9038 ctlData,
9039 puuidRegistry,
9040 puuidSnapshot);
9041 if (FAILED(rc)) return rc;
9042 }
9043
9044 return S_OK;
9045}
9046
9047/**
9048 * Called from i_loadStorageControllers for a controller's devices.
9049 *
9050 * @param aStorageController
9051 * @param data
9052 * @param puuidRegistry media registry ID to set media to or NULL; see
9053 * Machine::i_loadMachineDataFromSettings()
9054 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9055 * @return
9056 */
9057HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9058 const settings::StorageController &data,
9059 const Guid *puuidRegistry,
9060 const Guid *puuidSnapshot)
9061{
9062 HRESULT rc = S_OK;
9063
9064 /* paranoia: detect duplicate attachments */
9065 for (settings::AttachedDevicesList::const_iterator
9066 it = data.llAttachedDevices.begin();
9067 it != data.llAttachedDevices.end();
9068 ++it)
9069 {
9070 const settings::AttachedDevice &ad = *it;
9071
9072 for (settings::AttachedDevicesList::const_iterator it2 = it;
9073 it2 != data.llAttachedDevices.end();
9074 ++it2)
9075 {
9076 if (it == it2)
9077 continue;
9078
9079 const settings::AttachedDevice &ad2 = *it2;
9080
9081 if ( ad.lPort == ad2.lPort
9082 && ad.lDevice == ad2.lDevice)
9083 {
9084 return setError(E_FAIL,
9085 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9086 aStorageController->i_getName().c_str(),
9087 ad.lPort,
9088 ad.lDevice,
9089 mUserData->s.strName.c_str());
9090 }
9091 }
9092 }
9093
9094 for (settings::AttachedDevicesList::const_iterator
9095 it = data.llAttachedDevices.begin();
9096 it != data.llAttachedDevices.end();
9097 ++it)
9098 {
9099 const settings::AttachedDevice &dev = *it;
9100 ComObjPtr<Medium> medium;
9101
9102 switch (dev.deviceType)
9103 {
9104 case DeviceType_Floppy:
9105 case DeviceType_DVD:
9106 if (dev.strHostDriveSrc.isNotEmpty())
9107 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9108 false /* fRefresh */, medium);
9109 else
9110 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9111 dev.uuid,
9112 false /* fRefresh */,
9113 false /* aSetError */,
9114 medium);
9115 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9116 // This is not an error. The host drive or UUID might have vanished, so just go
9117 // ahead without this removeable medium attachment
9118 rc = S_OK;
9119 break;
9120
9121 case DeviceType_HardDisk:
9122 {
9123 /* find a hard disk by UUID */
9124 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9125 if (FAILED(rc))
9126 {
9127 if (i_isSnapshotMachine())
9128 {
9129 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9130 // so the user knows that the bad disk is in a snapshot somewhere
9131 com::ErrorInfo info;
9132 return setError(E_FAIL,
9133 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9134 puuidSnapshot->raw(),
9135 info.getText().raw());
9136 }
9137 else
9138 return rc;
9139 }
9140
9141 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9142
9143 if (medium->i_getType() == MediumType_Immutable)
9144 {
9145 if (i_isSnapshotMachine())
9146 return setError(E_FAIL,
9147 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9148 "of the virtual machine '%s' ('%s')"),
9149 medium->i_getLocationFull().c_str(),
9150 dev.uuid.raw(),
9151 puuidSnapshot->raw(),
9152 mUserData->s.strName.c_str(),
9153 mData->m_strConfigFileFull.c_str());
9154
9155 return setError(E_FAIL,
9156 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9157 medium->i_getLocationFull().c_str(),
9158 dev.uuid.raw(),
9159 mUserData->s.strName.c_str(),
9160 mData->m_strConfigFileFull.c_str());
9161 }
9162
9163 if (medium->i_getType() == MediumType_MultiAttach)
9164 {
9165 if (i_isSnapshotMachine())
9166 return setError(E_FAIL,
9167 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9168 "of the virtual machine '%s' ('%s')"),
9169 medium->i_getLocationFull().c_str(),
9170 dev.uuid.raw(),
9171 puuidSnapshot->raw(),
9172 mUserData->s.strName.c_str(),
9173 mData->m_strConfigFileFull.c_str());
9174
9175 return setError(E_FAIL,
9176 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9177 medium->i_getLocationFull().c_str(),
9178 dev.uuid.raw(),
9179 mUserData->s.strName.c_str(),
9180 mData->m_strConfigFileFull.c_str());
9181 }
9182
9183 if ( !i_isSnapshotMachine()
9184 && medium->i_getChildren().size() != 0
9185 )
9186 return setError(E_FAIL,
9187 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9188 "because it has %d differencing child hard disks"),
9189 medium->i_getLocationFull().c_str(),
9190 dev.uuid.raw(),
9191 mUserData->s.strName.c_str(),
9192 mData->m_strConfigFileFull.c_str(),
9193 medium->i_getChildren().size());
9194
9195 if (i_findAttachment(*mMediumAttachments.data(),
9196 medium))
9197 return setError(E_FAIL,
9198 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9199 medium->i_getLocationFull().c_str(),
9200 dev.uuid.raw(),
9201 mUserData->s.strName.c_str(),
9202 mData->m_strConfigFileFull.c_str());
9203
9204 break;
9205 }
9206
9207 default:
9208 return setError(E_FAIL,
9209 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9210 medium->i_getLocationFull().c_str(),
9211 mUserData->s.strName.c_str(),
9212 mData->m_strConfigFileFull.c_str());
9213 }
9214
9215 if (FAILED(rc))
9216 break;
9217
9218 /* Bandwidth groups are loaded at this point. */
9219 ComObjPtr<BandwidthGroup> pBwGroup;
9220
9221 if (!dev.strBwGroup.isEmpty())
9222 {
9223 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9224 if (FAILED(rc))
9225 return setError(E_FAIL,
9226 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9227 medium->i_getLocationFull().c_str(),
9228 dev.strBwGroup.c_str(),
9229 mUserData->s.strName.c_str(),
9230 mData->m_strConfigFileFull.c_str());
9231 pBwGroup->i_reference();
9232 }
9233
9234 const Utf8Str controllerName = aStorageController->i_getName();
9235 ComObjPtr<MediumAttachment> pAttachment;
9236 pAttachment.createObject();
9237 rc = pAttachment->init(this,
9238 medium,
9239 controllerName,
9240 dev.lPort,
9241 dev.lDevice,
9242 dev.deviceType,
9243 false,
9244 dev.fPassThrough,
9245 dev.fTempEject,
9246 dev.fNonRotational,
9247 dev.fDiscard,
9248 dev.fHotPluggable,
9249 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9250 if (FAILED(rc)) break;
9251
9252 /* associate the medium with this machine and snapshot */
9253 if (!medium.isNull())
9254 {
9255 AutoCaller medCaller(medium);
9256 if (FAILED(medCaller.rc())) return medCaller.rc();
9257 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9258
9259 if (i_isSnapshotMachine())
9260 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9261 else
9262 rc = medium->i_addBackReference(mData->mUuid);
9263 /* If the medium->addBackReference fails it sets an appropriate
9264 * error message, so no need to do any guesswork here. */
9265
9266 if (puuidRegistry)
9267 // caller wants registry ID to be set on all attached media (OVF import case)
9268 medium->i_addRegistry(*puuidRegistry);
9269 }
9270
9271 if (FAILED(rc))
9272 break;
9273
9274 /* back up mMediumAttachments to let registeredInit() properly rollback
9275 * on failure (= limited accessibility) */
9276 i_setModified(IsModified_Storage);
9277 mMediumAttachments.backup();
9278 mMediumAttachments->push_back(pAttachment);
9279 }
9280
9281 return rc;
9282}
9283
9284/**
9285 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9286 *
9287 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9288 * @param aSnapshot where to return the found snapshot
9289 * @param aSetError true to set extended error info on failure
9290 */
9291HRESULT Machine::i_findSnapshotById(const Guid &aId,
9292 ComObjPtr<Snapshot> &aSnapshot,
9293 bool aSetError /* = false */)
9294{
9295 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9296
9297 if (!mData->mFirstSnapshot)
9298 {
9299 if (aSetError)
9300 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9301 return E_FAIL;
9302 }
9303
9304 if (aId.isZero())
9305 aSnapshot = mData->mFirstSnapshot;
9306 else
9307 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9308
9309 if (!aSnapshot)
9310 {
9311 if (aSetError)
9312 return setError(E_FAIL,
9313 tr("Could not find a snapshot with UUID {%s}"),
9314 aId.toString().c_str());
9315 return E_FAIL;
9316 }
9317
9318 return S_OK;
9319}
9320
9321/**
9322 * Returns the snapshot with the given name or fails of no such snapshot.
9323 *
9324 * @param strName snapshot name to find
9325 * @param aSnapshot where to return the found snapshot
9326 * @param aSetError true to set extended error info on failure
9327 */
9328HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9329 ComObjPtr<Snapshot> &aSnapshot,
9330 bool aSetError /* = false */)
9331{
9332 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9333
9334 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9335
9336 if (!mData->mFirstSnapshot)
9337 {
9338 if (aSetError)
9339 return setError(VBOX_E_OBJECT_NOT_FOUND,
9340 tr("This machine does not have any snapshots"));
9341 return VBOX_E_OBJECT_NOT_FOUND;
9342 }
9343
9344 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9345
9346 if (!aSnapshot)
9347 {
9348 if (aSetError)
9349 return setError(VBOX_E_OBJECT_NOT_FOUND,
9350 tr("Could not find a snapshot named '%s'"), strName.c_str());
9351 return VBOX_E_OBJECT_NOT_FOUND;
9352 }
9353
9354 return S_OK;
9355}
9356
9357/**
9358 * Returns a storage controller object with the given name.
9359 *
9360 * @param aName storage controller name to find
9361 * @param aStorageController where to return the found storage controller
9362 * @param aSetError true to set extended error info on failure
9363 */
9364HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9365 ComObjPtr<StorageController> &aStorageController,
9366 bool aSetError /* = false */)
9367{
9368 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9369
9370 for (StorageControllerList::const_iterator
9371 it = mStorageControllers->begin();
9372 it != mStorageControllers->end();
9373 ++it)
9374 {
9375 if ((*it)->i_getName() == aName)
9376 {
9377 aStorageController = (*it);
9378 return S_OK;
9379 }
9380 }
9381
9382 if (aSetError)
9383 return setError(VBOX_E_OBJECT_NOT_FOUND,
9384 tr("Could not find a storage controller named '%s'"),
9385 aName.c_str());
9386 return VBOX_E_OBJECT_NOT_FOUND;
9387}
9388
9389/**
9390 * Returns a USB controller object with the given name.
9391 *
9392 * @param aName USB controller name to find
9393 * @param aUSBController where to return the found USB controller
9394 * @param aSetError true to set extended error info on failure
9395 */
9396HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9397 ComObjPtr<USBController> &aUSBController,
9398 bool aSetError /* = false */)
9399{
9400 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9401
9402 for (USBControllerList::const_iterator
9403 it = mUSBControllers->begin();
9404 it != mUSBControllers->end();
9405 ++it)
9406 {
9407 if ((*it)->i_getName() == aName)
9408 {
9409 aUSBController = (*it);
9410 return S_OK;
9411 }
9412 }
9413
9414 if (aSetError)
9415 return setError(VBOX_E_OBJECT_NOT_FOUND,
9416 tr("Could not find a storage controller named '%s'"),
9417 aName.c_str());
9418 return VBOX_E_OBJECT_NOT_FOUND;
9419}
9420
9421/**
9422 * Returns the number of USB controller instance of the given type.
9423 *
9424 * @param enmType USB controller type.
9425 */
9426ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9427{
9428 ULONG cCtrls = 0;
9429
9430 for (USBControllerList::const_iterator
9431 it = mUSBControllers->begin();
9432 it != mUSBControllers->end();
9433 ++it)
9434 {
9435 if ((*it)->i_getControllerType() == enmType)
9436 cCtrls++;
9437 }
9438
9439 return cCtrls;
9440}
9441
9442HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9443 MediumAttachmentList &atts)
9444{
9445 AutoCaller autoCaller(this);
9446 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9447
9448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9449
9450 for (MediumAttachmentList::const_iterator
9451 it = mMediumAttachments->begin();
9452 it != mMediumAttachments->end();
9453 ++it)
9454 {
9455 const ComObjPtr<MediumAttachment> &pAtt = *it;
9456 // should never happen, but deal with NULL pointers in the list.
9457 AssertContinue(!pAtt.isNull());
9458
9459 // getControllerName() needs caller+read lock
9460 AutoCaller autoAttCaller(pAtt);
9461 if (FAILED(autoAttCaller.rc()))
9462 {
9463 atts.clear();
9464 return autoAttCaller.rc();
9465 }
9466 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9467
9468 if (pAtt->i_getControllerName() == aName)
9469 atts.push_back(pAtt);
9470 }
9471
9472 return S_OK;
9473}
9474
9475
9476/**
9477 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9478 * file if the machine name was changed and about creating a new settings file
9479 * if this is a new machine.
9480 *
9481 * @note Must be never called directly but only from #saveSettings().
9482 */
9483HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9484{
9485 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9486
9487 HRESULT rc = S_OK;
9488
9489 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9490
9491 /// @todo need to handle primary group change, too
9492
9493 /* attempt to rename the settings file if machine name is changed */
9494 if ( mUserData->s.fNameSync
9495 && mUserData.isBackedUp()
9496 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9497 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9498 )
9499 {
9500 bool dirRenamed = false;
9501 bool fileRenamed = false;
9502
9503 Utf8Str configFile, newConfigFile;
9504 Utf8Str configFilePrev, newConfigFilePrev;
9505 Utf8Str NVRAMFile, newNVRAMFile;
9506 Utf8Str configDir, newConfigDir;
9507
9508 do
9509 {
9510 int vrc = VINF_SUCCESS;
9511
9512 Utf8Str name = mUserData.backedUpData()->s.strName;
9513 Utf8Str newName = mUserData->s.strName;
9514 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9515 if (group == "/")
9516 group.setNull();
9517 Utf8Str newGroup = mUserData->s.llGroups.front();
9518 if (newGroup == "/")
9519 newGroup.setNull();
9520
9521 configFile = mData->m_strConfigFileFull;
9522
9523 /* first, rename the directory if it matches the group and machine name */
9524 Utf8StrFmt groupPlusName("%s%c%s", group.c_str(), RTPATH_DELIMITER, name.c_str());
9525 /** @todo hack, make somehow use of ComposeMachineFilename */
9526 if (mUserData->s.fDirectoryIncludesUUID)
9527 groupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9528 Utf8StrFmt newGroupPlusName("%s%c%s", newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9529 /** @todo hack, make somehow use of ComposeMachineFilename */
9530 if (mUserData->s.fDirectoryIncludesUUID)
9531 newGroupPlusName.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
9532 configDir = configFile;
9533 configDir.stripFilename();
9534 newConfigDir = configDir;
9535 if ( configDir.length() >= groupPlusName.length()
9536 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9537 groupPlusName.c_str()))
9538 {
9539 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9540 Utf8Str newConfigBaseDir(newConfigDir);
9541 newConfigDir.append(newGroupPlusName);
9542 /* consistency: use \ if appropriate on the platform */
9543 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9544 /* new dir and old dir cannot be equal here because of 'if'
9545 * above and because name != newName */
9546 Assert(configDir != newConfigDir);
9547 if (!fSettingsFileIsNew)
9548 {
9549 /* perform real rename only if the machine is not new */
9550 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9551 if ( vrc == VERR_FILE_NOT_FOUND
9552 || vrc == VERR_PATH_NOT_FOUND)
9553 {
9554 /* create the parent directory, then retry renaming */
9555 Utf8Str parent(newConfigDir);
9556 parent.stripFilename();
9557 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9558 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9559 }
9560 if (RT_FAILURE(vrc))
9561 {
9562 rc = setErrorBoth(E_FAIL, vrc,
9563 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9564 configDir.c_str(),
9565 newConfigDir.c_str(),
9566 vrc);
9567 break;
9568 }
9569 /* delete subdirectories which are no longer needed */
9570 Utf8Str dir(configDir);
9571 dir.stripFilename();
9572 while (dir != newConfigBaseDir && dir != ".")
9573 {
9574 vrc = RTDirRemove(dir.c_str());
9575 if (RT_FAILURE(vrc))
9576 break;
9577 dir.stripFilename();
9578 }
9579 dirRenamed = true;
9580 }
9581 }
9582
9583 newConfigFile.printf("%s%c%s.vbox", newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9584
9585 /* then try to rename the settings file itself */
9586 if (newConfigFile != configFile)
9587 {
9588 /* get the path to old settings file in renamed directory */
9589 configFile.printf("%s%c%s",
9590 newConfigDir.c_str(),
9591 RTPATH_DELIMITER,
9592 RTPathFilename(configFile.c_str()));
9593 if (!fSettingsFileIsNew)
9594 {
9595 /* perform real rename only if the machine is not new */
9596 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9597 if (RT_FAILURE(vrc))
9598 {
9599 rc = setErrorBoth(E_FAIL, vrc,
9600 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9601 configFile.c_str(),
9602 newConfigFile.c_str(),
9603 vrc);
9604 break;
9605 }
9606 fileRenamed = true;
9607 configFilePrev = configFile;
9608 configFilePrev += "-prev";
9609 newConfigFilePrev = newConfigFile;
9610 newConfigFilePrev += "-prev";
9611 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9612 NVRAMFile = mBIOSSettings->i_getNonVolatileStorageFile();
9613 if (NVRAMFile.isNotEmpty())
9614 {
9615 // in the NVRAM file path, replace the old directory with the new directory
9616 if (RTPathStartsWith(NVRAMFile.c_str(), configDir.c_str()))
9617 {
9618 Utf8Str strNVRAMFile = NVRAMFile.c_str() + configDir.length();
9619 NVRAMFile = newConfigDir + strNVRAMFile;
9620 }
9621 newNVRAMFile = newConfigFile;
9622 newNVRAMFile.stripSuffix();
9623 newNVRAMFile += ".nvram";
9624 RTFileRename(NVRAMFile.c_str(), newNVRAMFile.c_str(), 0);
9625 }
9626 }
9627 }
9628
9629 // update m_strConfigFileFull amd mConfigFile
9630 mData->m_strConfigFileFull = newConfigFile;
9631 // compute the relative path too
9632 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9633
9634 // store the old and new so that VirtualBox::i_saveSettings() can update
9635 // the media registry
9636 if ( mData->mRegistered
9637 && (configDir != newConfigDir || configFile != newConfigFile))
9638 {
9639 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9640
9641 if (pfNeedsGlobalSaveSettings)
9642 *pfNeedsGlobalSaveSettings = true;
9643 }
9644
9645 // in the saved state file path, replace the old directory with the new directory
9646 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9647 {
9648 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9649 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9650 }
9651 if (newNVRAMFile.isNotEmpty())
9652 mBIOSSettings->i_updateNonVolatileStorageFile(newNVRAMFile);
9653
9654 // and do the same thing for the saved state file paths of all the online snapshots and NVRAM files of all snapshots
9655 if (mData->mFirstSnapshot)
9656 {
9657 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9658 newConfigDir.c_str());
9659 mData->mFirstSnapshot->i_updateNVRAMPaths(configDir.c_str(),
9660 newConfigDir.c_str());
9661 }
9662 }
9663 while (0);
9664
9665 if (FAILED(rc))
9666 {
9667 /* silently try to rename everything back */
9668 if (fileRenamed)
9669 {
9670 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9671 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9672 if (NVRAMFile.isNotEmpty() && newNVRAMFile.isNotEmpty())
9673 RTFileRename(newNVRAMFile.c_str(), NVRAMFile.c_str(), 0);
9674 }
9675 if (dirRenamed)
9676 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9677 }
9678
9679 if (FAILED(rc)) return rc;
9680 }
9681
9682 if (fSettingsFileIsNew)
9683 {
9684 /* create a virgin config file */
9685 int vrc = VINF_SUCCESS;
9686
9687 /* ensure the settings directory exists */
9688 Utf8Str path(mData->m_strConfigFileFull);
9689 path.stripFilename();
9690 if (!RTDirExists(path.c_str()))
9691 {
9692 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9693 if (RT_FAILURE(vrc))
9694 {
9695 return setErrorBoth(E_FAIL, vrc,
9696 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9697 path.c_str(),
9698 vrc);
9699 }
9700 }
9701
9702 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9703 path = Utf8Str(mData->m_strConfigFileFull);
9704 RTFILE f = NIL_RTFILE;
9705 vrc = RTFileOpen(&f, path.c_str(),
9706 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9707 if (RT_FAILURE(vrc))
9708 return setErrorBoth(E_FAIL, vrc,
9709 tr("Could not create the settings file '%s' (%Rrc)"),
9710 path.c_str(),
9711 vrc);
9712 RTFileClose(f);
9713 }
9714
9715 return rc;
9716}
9717
9718/**
9719 * Saves and commits machine data, user data and hardware data.
9720 *
9721 * Note that on failure, the data remains uncommitted.
9722 *
9723 * @a aFlags may combine the following flags:
9724 *
9725 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9726 * Used when saving settings after an operation that makes them 100%
9727 * correspond to the settings from the current snapshot.
9728 * - SaveS_Force: settings will be saved without doing a deep compare of the
9729 * settings structures. This is used when this is called because snapshots
9730 * have changed to avoid the overhead of the deep compare.
9731 *
9732 * @note Must be called from under this object's write lock. Locks children for
9733 * writing.
9734 *
9735 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9736 * initialized to false and that will be set to true by this function if
9737 * the caller must invoke VirtualBox::i_saveSettings() because the global
9738 * settings have changed. This will happen if a machine rename has been
9739 * saved and the global machine and media registries will therefore need
9740 * updating.
9741 * @param aFlags Flags.
9742 */
9743HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9744 int aFlags /*= 0*/)
9745{
9746 LogFlowThisFuncEnter();
9747
9748 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9749
9750 /* make sure child objects are unable to modify the settings while we are
9751 * saving them */
9752 i_ensureNoStateDependencies();
9753
9754 AssertReturn(!i_isSnapshotMachine(),
9755 E_FAIL);
9756
9757 if (!mData->mAccessible)
9758 return setError(VBOX_E_INVALID_VM_STATE,
9759 tr("The machine is not accessible, so cannot save settings"));
9760
9761 HRESULT rc = S_OK;
9762 bool fNeedsWrite = false;
9763
9764 /* First, prepare to save settings. It will care about renaming the
9765 * settings directory and file if the machine name was changed and about
9766 * creating a new settings file if this is a new machine. */
9767 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9768 if (FAILED(rc)) return rc;
9769
9770 // keep a pointer to the current settings structures
9771 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9772 settings::MachineConfigFile *pNewConfig = NULL;
9773
9774 try
9775 {
9776 // make a fresh one to have everyone write stuff into
9777 pNewConfig = new settings::MachineConfigFile(NULL);
9778 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9779
9780 // now go and copy all the settings data from COM to the settings structures
9781 // (this calls i_saveSettings() on all the COM objects in the machine)
9782 i_copyMachineDataToSettings(*pNewConfig);
9783
9784 if (aFlags & SaveS_ResetCurStateModified)
9785 {
9786 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9787 mData->mCurrentStateModified = FALSE;
9788 fNeedsWrite = true; // always, no need to compare
9789 }
9790 else if (aFlags & SaveS_Force)
9791 {
9792 fNeedsWrite = true; // always, no need to compare
9793 }
9794 else
9795 {
9796 if (!mData->mCurrentStateModified)
9797 {
9798 // do a deep compare of the settings that we just saved with the settings
9799 // previously stored in the config file; this invokes MachineConfigFile::operator==
9800 // which does a deep compare of all the settings, which is expensive but less expensive
9801 // than writing out XML in vain
9802 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9803
9804 // could still be modified if any settings changed
9805 mData->mCurrentStateModified = fAnySettingsChanged;
9806
9807 fNeedsWrite = fAnySettingsChanged;
9808 }
9809 else
9810 fNeedsWrite = true;
9811 }
9812
9813 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9814
9815 if (fNeedsWrite)
9816 // now spit it all out!
9817 pNewConfig->write(mData->m_strConfigFileFull);
9818
9819 mData->pMachineConfigFile = pNewConfig;
9820 delete pOldConfig;
9821 i_commit();
9822
9823 // after saving settings, we are no longer different from the XML on disk
9824 mData->flModifications = 0;
9825 }
9826 catch (HRESULT err)
9827 {
9828 // we assume that error info is set by the thrower
9829 rc = err;
9830
9831 // restore old config
9832 delete pNewConfig;
9833 mData->pMachineConfigFile = pOldConfig;
9834 }
9835 catch (...)
9836 {
9837 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9838 }
9839
9840 if (fNeedsWrite)
9841 {
9842 /* Fire the data change event, even on failure (since we've already
9843 * committed all data). This is done only for SessionMachines because
9844 * mutable Machine instances are always not registered (i.e. private
9845 * to the client process that creates them) and thus don't need to
9846 * inform callbacks. */
9847 if (i_isSessionMachine())
9848 mParent->i_onMachineDataChanged(mData->mUuid);
9849 }
9850
9851 LogFlowThisFunc(("rc=%08X\n", rc));
9852 LogFlowThisFuncLeave();
9853 return rc;
9854}
9855
9856/**
9857 * Implementation for saving the machine settings into the given
9858 * settings::MachineConfigFile instance. This copies machine extradata
9859 * from the previous machine config file in the instance data, if any.
9860 *
9861 * This gets called from two locations:
9862 *
9863 * -- Machine::i_saveSettings(), during the regular XML writing;
9864 *
9865 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9866 * exported to OVF and we write the VirtualBox proprietary XML
9867 * into a <vbox:Machine> tag.
9868 *
9869 * This routine fills all the fields in there, including snapshots, *except*
9870 * for the following:
9871 *
9872 * -- fCurrentStateModified. There is some special logic associated with that.
9873 *
9874 * The caller can then call MachineConfigFile::write() or do something else
9875 * with it.
9876 *
9877 * Caller must hold the machine lock!
9878 *
9879 * This throws XML errors and HRESULT, so the caller must have a catch block!
9880 */
9881void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9882{
9883 // deep copy extradata, being extra careful with self assignment (the STL
9884 // map assignment on Mac OS X clang based Xcode isn't checking)
9885 if (&config != mData->pMachineConfigFile)
9886 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9887
9888 config.uuid = mData->mUuid;
9889
9890 // copy name, description, OS type, teleport, UTC etc.
9891 config.machineUserData = mUserData->s;
9892
9893 if ( mData->mMachineState == MachineState_Saved
9894 || mData->mMachineState == MachineState_Restoring
9895 // when doing certain snapshot operations we may or may not have
9896 // a saved state in the current state, so keep everything as is
9897 || ( ( mData->mMachineState == MachineState_Snapshotting
9898 || mData->mMachineState == MachineState_DeletingSnapshot
9899 || mData->mMachineState == MachineState_RestoringSnapshot)
9900 && (!mSSData->strStateFilePath.isEmpty())
9901 )
9902 )
9903 {
9904 Assert(!mSSData->strStateFilePath.isEmpty());
9905 /* try to make the file name relative to the settings file dir */
9906 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9907 }
9908 else
9909 {
9910 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9911 config.strStateFile.setNull();
9912 }
9913
9914 if (mData->mCurrentSnapshot)
9915 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9916 else
9917 config.uuidCurrentSnapshot.clear();
9918
9919 config.timeLastStateChange = mData->mLastStateChange;
9920 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9921 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9922
9923 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9924 if (FAILED(rc)) throw rc;
9925
9926 // save machine's media registry if this is VirtualBox 4.0 or later
9927 if (config.canHaveOwnMediaRegistry())
9928 {
9929 // determine machine folder
9930 Utf8Str strMachineFolder = i_getSettingsFileFull();
9931 strMachineFolder.stripFilename();
9932 mParent->i_saveMediaRegistry(config.mediaRegistry,
9933 i_getId(), // only media with registry ID == machine UUID
9934 strMachineFolder);
9935 // this throws HRESULT
9936 }
9937
9938 // save snapshots
9939 rc = i_saveAllSnapshots(config);
9940 if (FAILED(rc)) throw rc;
9941}
9942
9943/**
9944 * Saves all snapshots of the machine into the given machine config file. Called
9945 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9946 * @param config
9947 * @return
9948 */
9949HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9950{
9951 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9952
9953 HRESULT rc = S_OK;
9954
9955 try
9956 {
9957 config.llFirstSnapshot.clear();
9958
9959 if (mData->mFirstSnapshot)
9960 {
9961 // the settings use a list for "the first snapshot"
9962 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
9963
9964 // get reference to the snapshot on the list and work on that
9965 // element straight in the list to avoid excessive copying later
9966 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
9967 if (FAILED(rc)) throw rc;
9968 }
9969
9970// if (mType == IsSessionMachine)
9971// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
9972
9973 }
9974 catch (HRESULT err)
9975 {
9976 /* we assume that error info is set by the thrower */
9977 rc = err;
9978 }
9979 catch (...)
9980 {
9981 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9982 }
9983
9984 return rc;
9985}
9986
9987/**
9988 * Saves the VM hardware configuration. It is assumed that the
9989 * given node is empty.
9990 *
9991 * @param data Reference to the settings object for the hardware config.
9992 * @param pDbg Pointer to the settings object for the debugging config
9993 * which happens to live in mHWData.
9994 * @param pAutostart Pointer to the settings object for the autostart config
9995 * which happens to live in mHWData.
9996 */
9997HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
9998 settings::Autostart *pAutostart)
9999{
10000 HRESULT rc = S_OK;
10001
10002 try
10003 {
10004 /* The hardware version attribute (optional).
10005 Automatically upgrade from 1 to current default hardware version
10006 when there is no saved state. (ugly!) */
10007 if ( mHWData->mHWVersion == "1"
10008 && mSSData->strStateFilePath.isEmpty()
10009 )
10010 mHWData->mHWVersion.printf("%d", SchemaDefs::DefaultHardwareVersion);
10011
10012 data.strVersion = mHWData->mHWVersion;
10013 data.uuid = mHWData->mHardwareUUID;
10014
10015 // CPU
10016 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10017 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10018 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10019 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10020 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10021 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10022 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10023 data.fPAE = !!mHWData->mPAEEnabled;
10024 data.enmLongMode = mHWData->mLongMode;
10025 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10026 data.fAPIC = !!mHWData->mAPIC;
10027 data.fX2APIC = !!mHWData->mX2APIC;
10028 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10029 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10030 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10031 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10032 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10033 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10034 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10035 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10036 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10037 data.cCPUs = mHWData->mCPUCount;
10038 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10039 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10040 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10041 data.strCpuProfile = mHWData->mCpuProfile;
10042
10043 data.llCpus.clear();
10044 if (data.fCpuHotPlug)
10045 {
10046 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10047 {
10048 if (mHWData->mCPUAttached[idx])
10049 {
10050 settings::Cpu cpu;
10051 cpu.ulId = idx;
10052 data.llCpus.push_back(cpu);
10053 }
10054 }
10055 }
10056
10057 /* Standard and Extended CPUID leafs. */
10058 data.llCpuIdLeafs.clear();
10059 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10060
10061 // memory
10062 data.ulMemorySizeMB = mHWData->mMemorySize;
10063 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10064
10065 // firmware
10066 data.firmwareType = mHWData->mFirmwareType;
10067
10068 // HID
10069 data.pointingHIDType = mHWData->mPointingHIDType;
10070 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10071
10072 // chipset
10073 data.chipsetType = mHWData->mChipsetType;
10074
10075 // paravirt
10076 data.paravirtProvider = mHWData->mParavirtProvider;
10077 data.strParavirtDebug = mHWData->mParavirtDebug;
10078
10079 // emulated USB card reader
10080 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10081
10082 // HPET
10083 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10084
10085 // boot order
10086 data.mapBootOrder.clear();
10087 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10088 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10089
10090 /* VRDEServer settings (optional) */
10091 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10092 if (FAILED(rc)) throw rc;
10093
10094 /* BIOS settings (required) */
10095 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10096 if (FAILED(rc)) throw rc;
10097
10098 /* Recording settings (required) */
10099 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10100 if (FAILED(rc)) throw rc;
10101
10102 /* GraphicsAdapter settings (required) */
10103 rc = mGraphicsAdapter->i_saveSettings(data.graphicsAdapter);
10104 if (FAILED(rc)) throw rc;
10105
10106 /* USB Controller (required) */
10107 data.usbSettings.llUSBControllers.clear();
10108 for (USBControllerList::const_iterator
10109 it = mUSBControllers->begin();
10110 it != mUSBControllers->end();
10111 ++it)
10112 {
10113 ComObjPtr<USBController> ctrl = *it;
10114 settings::USBController settingsCtrl;
10115
10116 settingsCtrl.strName = ctrl->i_getName();
10117 settingsCtrl.enmType = ctrl->i_getControllerType();
10118
10119 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10120 }
10121
10122 /* USB device filters (required) */
10123 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10124 if (FAILED(rc)) throw rc;
10125
10126 /* Network adapters (required) */
10127 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10128 data.llNetworkAdapters.clear();
10129 /* Write out only the nominal number of network adapters for this
10130 * chipset type. Since Machine::commit() hasn't been called there
10131 * may be extra NIC settings in the vector. */
10132 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10133 {
10134 settings::NetworkAdapter nic;
10135 nic.ulSlot = (uint32_t)slot;
10136 /* paranoia check... must not be NULL, but must not crash either. */
10137 if (mNetworkAdapters[slot])
10138 {
10139 if (mNetworkAdapters[slot]->i_hasDefaults())
10140 continue;
10141
10142 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10143 if (FAILED(rc)) throw rc;
10144
10145 data.llNetworkAdapters.push_back(nic);
10146 }
10147 }
10148
10149 /* Serial ports */
10150 data.llSerialPorts.clear();
10151 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10152 {
10153 if (mSerialPorts[slot]->i_hasDefaults())
10154 continue;
10155
10156 settings::SerialPort s;
10157 s.ulSlot = slot;
10158 rc = mSerialPorts[slot]->i_saveSettings(s);
10159 if (FAILED(rc)) return rc;
10160
10161 data.llSerialPorts.push_back(s);
10162 }
10163
10164 /* Parallel ports */
10165 data.llParallelPorts.clear();
10166 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10167 {
10168 if (mParallelPorts[slot]->i_hasDefaults())
10169 continue;
10170
10171 settings::ParallelPort p;
10172 p.ulSlot = slot;
10173 rc = mParallelPorts[slot]->i_saveSettings(p);
10174 if (FAILED(rc)) return rc;
10175
10176 data.llParallelPorts.push_back(p);
10177 }
10178
10179 /* Audio adapter */
10180 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10181 if (FAILED(rc)) return rc;
10182
10183 rc = i_saveStorageControllers(data.storage);
10184 if (FAILED(rc)) return rc;
10185
10186 /* Shared folders */
10187 data.llSharedFolders.clear();
10188 for (HWData::SharedFolderList::const_iterator
10189 it = mHWData->mSharedFolders.begin();
10190 it != mHWData->mSharedFolders.end();
10191 ++it)
10192 {
10193 SharedFolder *pSF = *it;
10194 AutoCaller sfCaller(pSF);
10195 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10196 settings::SharedFolder sf;
10197 sf.strName = pSF->i_getName();
10198 sf.strHostPath = pSF->i_getHostPath();
10199 sf.fWritable = !!pSF->i_isWritable();
10200 sf.fAutoMount = !!pSF->i_isAutoMounted();
10201 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10202
10203 data.llSharedFolders.push_back(sf);
10204 }
10205
10206 // clipboard
10207 data.clipboardMode = mHWData->mClipboardMode;
10208 data.fClipboardFileTransfersEnabled = RT_BOOL(mHWData->mClipboardFileTransfersEnabled);
10209
10210 // drag'n'drop
10211 data.dndMode = mHWData->mDnDMode;
10212
10213 /* Guest */
10214 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10215
10216 // IO settings
10217 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10218 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10219
10220 /* BandwidthControl (required) */
10221 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10222 if (FAILED(rc)) throw rc;
10223
10224 /* Host PCI devices */
10225 data.pciAttachments.clear();
10226 for (HWData::PCIDeviceAssignmentList::const_iterator
10227 it = mHWData->mPCIDeviceAssignments.begin();
10228 it != mHWData->mPCIDeviceAssignments.end();
10229 ++it)
10230 {
10231 ComObjPtr<PCIDeviceAttachment> pda = *it;
10232 settings::HostPCIDeviceAttachment hpda;
10233
10234 rc = pda->i_saveSettings(hpda);
10235 if (FAILED(rc)) throw rc;
10236
10237 data.pciAttachments.push_back(hpda);
10238 }
10239
10240 // guest properties
10241 data.llGuestProperties.clear();
10242#ifdef VBOX_WITH_GUEST_PROPS
10243 for (HWData::GuestPropertyMap::const_iterator
10244 it = mHWData->mGuestProperties.begin();
10245 it != mHWData->mGuestProperties.end();
10246 ++it)
10247 {
10248 HWData::GuestProperty property = it->second;
10249
10250 /* Remove transient guest properties at shutdown unless we
10251 * are saving state. Note that restoring snapshot intentionally
10252 * keeps them, they will be removed if appropriate once the final
10253 * machine state is set (as crashes etc. need to work). */
10254 if ( ( mData->mMachineState == MachineState_PoweredOff
10255 || mData->mMachineState == MachineState_Aborted
10256 || mData->mMachineState == MachineState_Teleported)
10257 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10258 continue;
10259 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10260 prop.strName = it->first;
10261 prop.strValue = property.strValue;
10262 prop.timestamp = (uint64_t)property.mTimestamp;
10263 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10264 GuestPropWriteFlags(property.mFlags, szFlags);
10265 prop.strFlags = szFlags;
10266
10267 data.llGuestProperties.push_back(prop);
10268 }
10269
10270 /* I presume this doesn't require a backup(). */
10271 mData->mGuestPropertiesModified = FALSE;
10272#endif /* VBOX_WITH_GUEST_PROPS defined */
10273
10274 *pDbg = mHWData->mDebugging;
10275 *pAutostart = mHWData->mAutostart;
10276
10277 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10278 }
10279 catch (std::bad_alloc &)
10280 {
10281 return E_OUTOFMEMORY;
10282 }
10283
10284 AssertComRC(rc);
10285 return rc;
10286}
10287
10288/**
10289 * Saves the storage controller configuration.
10290 *
10291 * @param data storage settings.
10292 */
10293HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10294{
10295 data.llStorageControllers.clear();
10296
10297 for (StorageControllerList::const_iterator
10298 it = mStorageControllers->begin();
10299 it != mStorageControllers->end();
10300 ++it)
10301 {
10302 HRESULT rc;
10303 ComObjPtr<StorageController> pCtl = *it;
10304
10305 settings::StorageController ctl;
10306 ctl.strName = pCtl->i_getName();
10307 ctl.controllerType = pCtl->i_getControllerType();
10308 ctl.storageBus = pCtl->i_getStorageBus();
10309 ctl.ulInstance = pCtl->i_getInstance();
10310 ctl.fBootable = pCtl->i_getBootable();
10311
10312 /* Save the port count. */
10313 ULONG portCount;
10314 rc = pCtl->COMGETTER(PortCount)(&portCount);
10315 ComAssertComRCRet(rc, rc);
10316 ctl.ulPortCount = portCount;
10317
10318 /* Save fUseHostIOCache */
10319 BOOL fUseHostIOCache;
10320 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10321 ComAssertComRCRet(rc, rc);
10322 ctl.fUseHostIOCache = !!fUseHostIOCache;
10323
10324 /* save the devices now. */
10325 rc = i_saveStorageDevices(pCtl, ctl);
10326 ComAssertComRCRet(rc, rc);
10327
10328 data.llStorageControllers.push_back(ctl);
10329 }
10330
10331 return S_OK;
10332}
10333
10334/**
10335 * Saves the hard disk configuration.
10336 */
10337HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10338 settings::StorageController &data)
10339{
10340 MediumAttachmentList atts;
10341
10342 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10343 if (FAILED(rc)) return rc;
10344
10345 data.llAttachedDevices.clear();
10346 for (MediumAttachmentList::const_iterator
10347 it = atts.begin();
10348 it != atts.end();
10349 ++it)
10350 {
10351 settings::AttachedDevice dev;
10352 IMediumAttachment *iA = *it;
10353 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10354 Medium *pMedium = pAttach->i_getMedium();
10355
10356 dev.deviceType = pAttach->i_getType();
10357 dev.lPort = pAttach->i_getPort();
10358 dev.lDevice = pAttach->i_getDevice();
10359 dev.fPassThrough = pAttach->i_getPassthrough();
10360 dev.fHotPluggable = pAttach->i_getHotPluggable();
10361 if (pMedium)
10362 {
10363 if (pMedium->i_isHostDrive())
10364 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10365 else
10366 dev.uuid = pMedium->i_getId();
10367 dev.fTempEject = pAttach->i_getTempEject();
10368 dev.fNonRotational = pAttach->i_getNonRotational();
10369 dev.fDiscard = pAttach->i_getDiscard();
10370 }
10371
10372 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10373
10374 data.llAttachedDevices.push_back(dev);
10375 }
10376
10377 return S_OK;
10378}
10379
10380/**
10381 * Saves machine state settings as defined by aFlags
10382 * (SaveSTS_* values).
10383 *
10384 * @param aFlags Combination of SaveSTS_* flags.
10385 *
10386 * @note Locks objects for writing.
10387 */
10388HRESULT Machine::i_saveStateSettings(int aFlags)
10389{
10390 if (aFlags == 0)
10391 return S_OK;
10392
10393 AutoCaller autoCaller(this);
10394 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10395
10396 /* This object's write lock is also necessary to serialize file access
10397 * (prevent concurrent reads and writes) */
10398 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10399
10400 HRESULT rc = S_OK;
10401
10402 Assert(mData->pMachineConfigFile);
10403
10404 try
10405 {
10406 if (aFlags & SaveSTS_CurStateModified)
10407 mData->pMachineConfigFile->fCurrentStateModified = true;
10408
10409 if (aFlags & SaveSTS_StateFilePath)
10410 {
10411 if (!mSSData->strStateFilePath.isEmpty())
10412 /* try to make the file name relative to the settings file dir */
10413 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10414 else
10415 mData->pMachineConfigFile->strStateFile.setNull();
10416 }
10417
10418 if (aFlags & SaveSTS_StateTimeStamp)
10419 {
10420 Assert( mData->mMachineState != MachineState_Aborted
10421 || mSSData->strStateFilePath.isEmpty());
10422
10423 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10424
10425 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10426/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10427 }
10428
10429 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10430 }
10431 catch (...)
10432 {
10433 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10434 }
10435
10436 return rc;
10437}
10438
10439/**
10440 * Ensures that the given medium is added to a media registry. If this machine
10441 * was created with 4.0 or later, then the machine registry is used. Otherwise
10442 * the global VirtualBox media registry is used.
10443 *
10444 * Caller must NOT hold machine lock, media tree or any medium locks!
10445 *
10446 * @param pMedium
10447 */
10448void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10449{
10450 /* Paranoia checks: do not hold machine or media tree locks. */
10451 AssertReturnVoid(!isWriteLockOnCurrentThread());
10452 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10453
10454 ComObjPtr<Medium> pBase;
10455 {
10456 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10457 pBase = pMedium->i_getBase();
10458 }
10459
10460 /* Paranoia checks: do not hold medium locks. */
10461 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10462 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10463
10464 // decide which medium registry to use now that the medium is attached:
10465 Guid uuid;
10466 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10467 if (fCanHaveOwnMediaRegistry)
10468 // machine XML is VirtualBox 4.0 or higher:
10469 uuid = i_getId(); // machine UUID
10470 else
10471 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10472
10473 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10474 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10475 if (pMedium->i_addRegistry(uuid))
10476 mParent->i_markRegistryModified(uuid);
10477
10478 /* For more complex hard disk structures it can happen that the base
10479 * medium isn't yet associated with any medium registry. Do that now. */
10480 if (pMedium != pBase)
10481 {
10482 /* Tree lock needed by Medium::addRegistry when recursing. */
10483 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10484 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10485 {
10486 treeLock.release();
10487 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10488 treeLock.acquire();
10489 }
10490 if (pBase->i_addRegistryRecursive(uuid))
10491 {
10492 treeLock.release();
10493 mParent->i_markRegistryModified(uuid);
10494 }
10495 }
10496}
10497
10498/**
10499 * Creates differencing hard disks for all normal hard disks attached to this
10500 * machine and a new set of attachments to refer to created disks.
10501 *
10502 * Used when taking a snapshot or when deleting the current state. Gets called
10503 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10504 *
10505 * This method assumes that mMediumAttachments contains the original hard disk
10506 * attachments it needs to create diffs for. On success, these attachments will
10507 * be replaced with the created diffs.
10508 *
10509 * Attachments with non-normal hard disks are left as is.
10510 *
10511 * If @a aOnline is @c false then the original hard disks that require implicit
10512 * diffs will be locked for reading. Otherwise it is assumed that they are
10513 * already locked for writing (when the VM was started). Note that in the latter
10514 * case it is responsibility of the caller to lock the newly created diffs for
10515 * writing if this method succeeds.
10516 *
10517 * @param aProgress Progress object to run (must contain at least as
10518 * many operations left as the number of hard disks
10519 * attached).
10520 * @param aWeight Weight of this operation.
10521 * @param aOnline Whether the VM was online prior to this operation.
10522 *
10523 * @note The progress object is not marked as completed, neither on success nor
10524 * on failure. This is a responsibility of the caller.
10525 *
10526 * @note Locks this object and the media tree for writing.
10527 */
10528HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10529 ULONG aWeight,
10530 bool aOnline)
10531{
10532 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10533
10534 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10535 AssertReturn(!!pProgressControl, E_INVALIDARG);
10536
10537 AutoCaller autoCaller(this);
10538 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10539
10540 AutoMultiWriteLock2 alock(this->lockHandle(),
10541 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10542
10543 /* must be in a protective state because we release the lock below */
10544 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10545 || mData->mMachineState == MachineState_OnlineSnapshotting
10546 || mData->mMachineState == MachineState_LiveSnapshotting
10547 || mData->mMachineState == MachineState_RestoringSnapshot
10548 || mData->mMachineState == MachineState_DeletingSnapshot
10549 , E_FAIL);
10550
10551 HRESULT rc = S_OK;
10552
10553 // use appropriate locked media map (online or offline)
10554 MediumLockListMap lockedMediaOffline;
10555 MediumLockListMap *lockedMediaMap;
10556 if (aOnline)
10557 lockedMediaMap = &mData->mSession.mLockedMedia;
10558 else
10559 lockedMediaMap = &lockedMediaOffline;
10560
10561 try
10562 {
10563 if (!aOnline)
10564 {
10565 /* lock all attached hard disks early to detect "in use"
10566 * situations before creating actual diffs */
10567 for (MediumAttachmentList::const_iterator
10568 it = mMediumAttachments->begin();
10569 it != mMediumAttachments->end();
10570 ++it)
10571 {
10572 MediumAttachment *pAtt = *it;
10573 if (pAtt->i_getType() == DeviceType_HardDisk)
10574 {
10575 Medium *pMedium = pAtt->i_getMedium();
10576 Assert(pMedium);
10577
10578 MediumLockList *pMediumLockList(new MediumLockList());
10579 alock.release();
10580 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10581 NULL /* pToLockWrite */,
10582 false /* fMediumLockWriteAll */,
10583 NULL,
10584 *pMediumLockList);
10585 alock.acquire();
10586 if (FAILED(rc))
10587 {
10588 delete pMediumLockList;
10589 throw rc;
10590 }
10591 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10592 if (FAILED(rc))
10593 {
10594 throw setError(rc,
10595 tr("Collecting locking information for all attached media failed"));
10596 }
10597 }
10598 }
10599
10600 /* Now lock all media. If this fails, nothing is locked. */
10601 alock.release();
10602 rc = lockedMediaMap->Lock();
10603 alock.acquire();
10604 if (FAILED(rc))
10605 {
10606 throw setError(rc,
10607 tr("Locking of attached media failed"));
10608 }
10609 }
10610
10611 /* remember the current list (note that we don't use backup() since
10612 * mMediumAttachments may be already backed up) */
10613 MediumAttachmentList atts = *mMediumAttachments.data();
10614
10615 /* start from scratch */
10616 mMediumAttachments->clear();
10617
10618 /* go through remembered attachments and create diffs for normal hard
10619 * disks and attach them */
10620 for (MediumAttachmentList::const_iterator
10621 it = atts.begin();
10622 it != atts.end();
10623 ++it)
10624 {
10625 MediumAttachment *pAtt = *it;
10626
10627 DeviceType_T devType = pAtt->i_getType();
10628 Medium *pMedium = pAtt->i_getMedium();
10629
10630 if ( devType != DeviceType_HardDisk
10631 || pMedium == NULL
10632 || pMedium->i_getType() != MediumType_Normal)
10633 {
10634 /* copy the attachment as is */
10635
10636 /** @todo the progress object created in SessionMachine::TakeSnaphot
10637 * only expects operations for hard disks. Later other
10638 * device types need to show up in the progress as well. */
10639 if (devType == DeviceType_HardDisk)
10640 {
10641 if (pMedium == NULL)
10642 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10643 aWeight); // weight
10644 else
10645 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10646 pMedium->i_getBase()->i_getName().c_str()).raw(),
10647 aWeight); // weight
10648 }
10649
10650 mMediumAttachments->push_back(pAtt);
10651 continue;
10652 }
10653
10654 /* need a diff */
10655 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10656 pMedium->i_getBase()->i_getName().c_str()).raw(),
10657 aWeight); // weight
10658
10659 Utf8Str strFullSnapshotFolder;
10660 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10661
10662 ComObjPtr<Medium> diff;
10663 diff.createObject();
10664 // store the diff in the same registry as the parent
10665 // (this cannot fail here because we can't create implicit diffs for
10666 // unregistered images)
10667 Guid uuidRegistryParent;
10668 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10669 Assert(fInRegistry); NOREF(fInRegistry);
10670 rc = diff->init(mParent,
10671 pMedium->i_getPreferredDiffFormat(),
10672 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10673 uuidRegistryParent,
10674 DeviceType_HardDisk);
10675 if (FAILED(rc)) throw rc;
10676
10677 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10678 * the push_back? Looks like we're going to release medium with the
10679 * wrong kind of lock (general issue with if we fail anywhere at all)
10680 * and an orphaned VDI in the snapshots folder. */
10681
10682 /* update the appropriate lock list */
10683 MediumLockList *pMediumLockList;
10684 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10685 AssertComRCThrowRC(rc);
10686 if (aOnline)
10687 {
10688 alock.release();
10689 /* The currently attached medium will be read-only, change
10690 * the lock type to read. */
10691 rc = pMediumLockList->Update(pMedium, false);
10692 alock.acquire();
10693 AssertComRCThrowRC(rc);
10694 }
10695
10696 /* release the locks before the potentially lengthy operation */
10697 alock.release();
10698 rc = pMedium->i_createDiffStorage(diff,
10699 pMedium->i_getPreferredDiffVariant(),
10700 pMediumLockList,
10701 NULL /* aProgress */,
10702 true /* aWait */,
10703 false /* aNotify */);
10704 alock.acquire();
10705 if (FAILED(rc)) throw rc;
10706
10707 /* actual lock list update is done in Machine::i_commitMedia */
10708
10709 rc = diff->i_addBackReference(mData->mUuid);
10710 AssertComRCThrowRC(rc);
10711
10712 /* add a new attachment */
10713 ComObjPtr<MediumAttachment> attachment;
10714 attachment.createObject();
10715 rc = attachment->init(this,
10716 diff,
10717 pAtt->i_getControllerName(),
10718 pAtt->i_getPort(),
10719 pAtt->i_getDevice(),
10720 DeviceType_HardDisk,
10721 true /* aImplicit */,
10722 false /* aPassthrough */,
10723 false /* aTempEject */,
10724 pAtt->i_getNonRotational(),
10725 pAtt->i_getDiscard(),
10726 pAtt->i_getHotPluggable(),
10727 pAtt->i_getBandwidthGroup());
10728 if (FAILED(rc)) throw rc;
10729
10730 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10731 AssertComRCThrowRC(rc);
10732 mMediumAttachments->push_back(attachment);
10733 }
10734 }
10735 catch (HRESULT aRC) { rc = aRC; }
10736
10737 /* unlock all hard disks we locked when there is no VM */
10738 if (!aOnline)
10739 {
10740 ErrorInfoKeeper eik;
10741
10742 HRESULT rc1 = lockedMediaMap->Clear();
10743 AssertComRC(rc1);
10744 }
10745
10746 return rc;
10747}
10748
10749/**
10750 * Deletes implicit differencing hard disks created either by
10751 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10752 * mMediumAttachments.
10753 *
10754 * Note that to delete hard disks created by #attachDevice() this method is
10755 * called from #i_rollbackMedia() when the changes are rolled back.
10756 *
10757 * @note Locks this object and the media tree for writing.
10758 */
10759HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10760{
10761 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10762
10763 AutoCaller autoCaller(this);
10764 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10765
10766 AutoMultiWriteLock2 alock(this->lockHandle(),
10767 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10768
10769 /* We absolutely must have backed up state. */
10770 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10771
10772 /* Check if there are any implicitly created diff images. */
10773 bool fImplicitDiffs = false;
10774 for (MediumAttachmentList::const_iterator
10775 it = mMediumAttachments->begin();
10776 it != mMediumAttachments->end();
10777 ++it)
10778 {
10779 const ComObjPtr<MediumAttachment> &pAtt = *it;
10780 if (pAtt->i_isImplicit())
10781 {
10782 fImplicitDiffs = true;
10783 break;
10784 }
10785 }
10786 /* If there is nothing to do, leave early. This saves lots of image locking
10787 * effort. It also avoids a MachineStateChanged event without real reason.
10788 * This is important e.g. when loading a VM config, because there should be
10789 * no events. Otherwise API clients can become thoroughly confused for
10790 * inaccessible VMs (the code for loading VM configs uses this method for
10791 * cleanup if the config makes no sense), as they take such events as an
10792 * indication that the VM is alive, and they would force the VM config to
10793 * be reread, leading to an endless loop. */
10794 if (!fImplicitDiffs)
10795 return S_OK;
10796
10797 HRESULT rc = S_OK;
10798 MachineState_T oldState = mData->mMachineState;
10799
10800 /* will release the lock before the potentially lengthy operation,
10801 * so protect with the special state (unless already protected) */
10802 if ( oldState != MachineState_Snapshotting
10803 && oldState != MachineState_OnlineSnapshotting
10804 && oldState != MachineState_LiveSnapshotting
10805 && oldState != MachineState_RestoringSnapshot
10806 && oldState != MachineState_DeletingSnapshot
10807 && oldState != MachineState_DeletingSnapshotOnline
10808 && oldState != MachineState_DeletingSnapshotPaused
10809 )
10810 i_setMachineState(MachineState_SettingUp);
10811
10812 // use appropriate locked media map (online or offline)
10813 MediumLockListMap lockedMediaOffline;
10814 MediumLockListMap *lockedMediaMap;
10815 if (aOnline)
10816 lockedMediaMap = &mData->mSession.mLockedMedia;
10817 else
10818 lockedMediaMap = &lockedMediaOffline;
10819
10820 try
10821 {
10822 if (!aOnline)
10823 {
10824 /* lock all attached hard disks early to detect "in use"
10825 * situations before deleting actual diffs */
10826 for (MediumAttachmentList::const_iterator
10827 it = mMediumAttachments->begin();
10828 it != mMediumAttachments->end();
10829 ++it)
10830 {
10831 MediumAttachment *pAtt = *it;
10832 if (pAtt->i_getType() == DeviceType_HardDisk)
10833 {
10834 Medium *pMedium = pAtt->i_getMedium();
10835 Assert(pMedium);
10836
10837 MediumLockList *pMediumLockList(new MediumLockList());
10838 alock.release();
10839 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10840 NULL /* pToLockWrite */,
10841 false /* fMediumLockWriteAll */,
10842 NULL,
10843 *pMediumLockList);
10844 alock.acquire();
10845
10846 if (FAILED(rc))
10847 {
10848 delete pMediumLockList;
10849 throw rc;
10850 }
10851
10852 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10853 if (FAILED(rc))
10854 throw rc;
10855 }
10856 }
10857
10858 if (FAILED(rc))
10859 throw rc;
10860 } // end of offline
10861
10862 /* Lock lists are now up to date and include implicitly created media */
10863
10864 /* Go through remembered attachments and delete all implicitly created
10865 * diffs and fix up the attachment information */
10866 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10867 MediumAttachmentList implicitAtts;
10868 for (MediumAttachmentList::const_iterator
10869 it = mMediumAttachments->begin();
10870 it != mMediumAttachments->end();
10871 ++it)
10872 {
10873 ComObjPtr<MediumAttachment> pAtt = *it;
10874 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10875 if (pMedium.isNull())
10876 continue;
10877
10878 // Implicit attachments go on the list for deletion and back references are removed.
10879 if (pAtt->i_isImplicit())
10880 {
10881 /* Deassociate and mark for deletion */
10882 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10883 rc = pMedium->i_removeBackReference(mData->mUuid);
10884 if (FAILED(rc))
10885 throw rc;
10886 implicitAtts.push_back(pAtt);
10887 continue;
10888 }
10889
10890 /* Was this medium attached before? */
10891 if (!i_findAttachment(oldAtts, pMedium))
10892 {
10893 /* no: de-associate */
10894 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10895 rc = pMedium->i_removeBackReference(mData->mUuid);
10896 if (FAILED(rc))
10897 throw rc;
10898 continue;
10899 }
10900 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10901 }
10902
10903 /* If there are implicit attachments to delete, throw away the lock
10904 * map contents (which will unlock all media) since the medium
10905 * attachments will be rolled back. Below we need to completely
10906 * recreate the lock map anyway since it is infinitely complex to
10907 * do this incrementally (would need reconstructing each attachment
10908 * change, which would be extremely hairy). */
10909 if (implicitAtts.size() != 0)
10910 {
10911 ErrorInfoKeeper eik;
10912
10913 HRESULT rc1 = lockedMediaMap->Clear();
10914 AssertComRC(rc1);
10915 }
10916
10917 /* rollback hard disk changes */
10918 mMediumAttachments.rollback();
10919
10920 MultiResult mrc(S_OK);
10921
10922 // Delete unused implicit diffs.
10923 if (implicitAtts.size() != 0)
10924 {
10925 alock.release();
10926
10927 for (MediumAttachmentList::const_iterator
10928 it = implicitAtts.begin();
10929 it != implicitAtts.end();
10930 ++it)
10931 {
10932 // Remove medium associated with this attachment.
10933 ComObjPtr<MediumAttachment> pAtt = *it;
10934 Assert(pAtt);
10935 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10936 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10937 Assert(pMedium);
10938
10939 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10940 // continue on delete failure, just collect error messages
10941 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10942 pMedium->i_getLocationFull().c_str() ));
10943 mrc = rc;
10944 }
10945 // Clear the list of deleted implicit attachments now, while not
10946 // holding the lock, as it will ultimately trigger Medium::uninit()
10947 // calls which assume that the media tree lock isn't held.
10948 implicitAtts.clear();
10949
10950 alock.acquire();
10951
10952 /* if there is a VM recreate media lock map as mentioned above,
10953 * otherwise it is a waste of time and we leave things unlocked */
10954 if (aOnline)
10955 {
10956 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
10957 /* must never be NULL, but better safe than sorry */
10958 if (!pMachine.isNull())
10959 {
10960 alock.release();
10961 rc = mData->mSession.mMachine->i_lockMedia();
10962 alock.acquire();
10963 if (FAILED(rc))
10964 throw rc;
10965 }
10966 }
10967 }
10968 }
10969 catch (HRESULT aRC) {rc = aRC;}
10970
10971 if (mData->mMachineState == MachineState_SettingUp)
10972 i_setMachineState(oldState);
10973
10974 /* unlock all hard disks we locked when there is no VM */
10975 if (!aOnline)
10976 {
10977 ErrorInfoKeeper eik;
10978
10979 HRESULT rc1 = lockedMediaMap->Clear();
10980 AssertComRC(rc1);
10981 }
10982
10983 return rc;
10984}
10985
10986
10987/**
10988 * Looks through the given list of media attachments for one with the given parameters
10989 * and returns it, or NULL if not found. The list is a parameter so that backup lists
10990 * can be searched as well if needed.
10991 *
10992 * @param ll
10993 * @param aControllerName
10994 * @param aControllerPort
10995 * @param aDevice
10996 * @return
10997 */
10998MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
10999 const Utf8Str &aControllerName,
11000 LONG aControllerPort,
11001 LONG aDevice)
11002{
11003 for (MediumAttachmentList::const_iterator
11004 it = ll.begin();
11005 it != ll.end();
11006 ++it)
11007 {
11008 MediumAttachment *pAttach = *it;
11009 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11010 return pAttach;
11011 }
11012
11013 return NULL;
11014}
11015
11016/**
11017 * Looks through the given list of media attachments for one with the given parameters
11018 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11019 * can be searched as well if needed.
11020 *
11021 * @param ll
11022 * @param pMedium
11023 * @return
11024 */
11025MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11026 ComObjPtr<Medium> pMedium)
11027{
11028 for (MediumAttachmentList::const_iterator
11029 it = ll.begin();
11030 it != ll.end();
11031 ++it)
11032 {
11033 MediumAttachment *pAttach = *it;
11034 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11035 if (pMediumThis == pMedium)
11036 return pAttach;
11037 }
11038
11039 return NULL;
11040}
11041
11042/**
11043 * Looks through the given list of media attachments for one with the given parameters
11044 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11045 * can be searched as well if needed.
11046 *
11047 * @param ll
11048 * @param id
11049 * @return
11050 */
11051MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11052 Guid &id)
11053{
11054 for (MediumAttachmentList::const_iterator
11055 it = ll.begin();
11056 it != ll.end();
11057 ++it)
11058 {
11059 MediumAttachment *pAttach = *it;
11060 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11061 if (pMediumThis->i_getId() == id)
11062 return pAttach;
11063 }
11064
11065 return NULL;
11066}
11067
11068/**
11069 * Main implementation for Machine::DetachDevice. This also gets called
11070 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11071 *
11072 * @param pAttach Medium attachment to detach.
11073 * @param writeLock Machine write lock which the caller must have locked once.
11074 * This may be released temporarily in here.
11075 * @param pSnapshot If NULL, then the detachment is for the current machine.
11076 * Otherwise this is for a SnapshotMachine, and this must be
11077 * its snapshot.
11078 * @return
11079 */
11080HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11081 AutoWriteLock &writeLock,
11082 Snapshot *pSnapshot)
11083{
11084 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11085 DeviceType_T mediumType = pAttach->i_getType();
11086
11087 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11088
11089 if (pAttach->i_isImplicit())
11090 {
11091 /* attempt to implicitly delete the implicitly created diff */
11092
11093 /// @todo move the implicit flag from MediumAttachment to Medium
11094 /// and forbid any hard disk operation when it is implicit. Or maybe
11095 /// a special media state for it to make it even more simple.
11096
11097 Assert(mMediumAttachments.isBackedUp());
11098
11099 /* will release the lock before the potentially lengthy operation, so
11100 * protect with the special state */
11101 MachineState_T oldState = mData->mMachineState;
11102 i_setMachineState(MachineState_SettingUp);
11103
11104 writeLock.release();
11105
11106 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11107 true /*aWait*/,
11108 false /*aNotify*/);
11109
11110 writeLock.acquire();
11111
11112 i_setMachineState(oldState);
11113
11114 if (FAILED(rc)) return rc;
11115 }
11116
11117 i_setModified(IsModified_Storage);
11118 mMediumAttachments.backup();
11119 mMediumAttachments->remove(pAttach);
11120
11121 if (!oldmedium.isNull())
11122 {
11123 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11124 if (pSnapshot)
11125 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11126 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11127 else if (mediumType != DeviceType_HardDisk)
11128 oldmedium->i_removeBackReference(mData->mUuid);
11129 }
11130
11131 return S_OK;
11132}
11133
11134/**
11135 * Goes thru all media of the given list and
11136 *
11137 * 1) calls i_detachDevice() on each of them for this machine and
11138 * 2) adds all Medium objects found in the process to the given list,
11139 * depending on cleanupMode.
11140 *
11141 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11142 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11143 * media to the list.
11144 *
11145 * This gets called from Machine::Unregister, both for the actual Machine and
11146 * the SnapshotMachine objects that might be found in the snapshots.
11147 *
11148 * Requires caller and locking. The machine lock must be passed in because it
11149 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11150 *
11151 * @param writeLock Machine lock from top-level caller; this gets passed to
11152 * i_detachDevice.
11153 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11154 * object if called for a SnapshotMachine.
11155 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11156 * added to llMedia; if Full, then all media get added;
11157 * otherwise no media get added.
11158 * @param llMedia Caller's list to receive Medium objects which got detached so
11159 * caller can close() them, depending on cleanupMode.
11160 * @return
11161 */
11162HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11163 Snapshot *pSnapshot,
11164 CleanupMode_T cleanupMode,
11165 MediaList &llMedia)
11166{
11167 Assert(isWriteLockOnCurrentThread());
11168
11169 HRESULT rc;
11170
11171 // make a temporary list because i_detachDevice invalidates iterators into
11172 // mMediumAttachments
11173 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11174
11175 for (MediumAttachmentList::iterator
11176 it = llAttachments2.begin();
11177 it != llAttachments2.end();
11178 ++it)
11179 {
11180 ComObjPtr<MediumAttachment> &pAttach = *it;
11181 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11182
11183 if (!pMedium.isNull())
11184 {
11185 AutoCaller mac(pMedium);
11186 if (FAILED(mac.rc())) return mac.rc();
11187 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11188 DeviceType_T devType = pMedium->i_getDeviceType();
11189 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11190 && devType == DeviceType_HardDisk)
11191 || (cleanupMode == CleanupMode_Full)
11192 )
11193 {
11194 llMedia.push_back(pMedium);
11195 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11196 /* Not allowed to keep this lock as below we need the parent
11197 * medium lock, and the lock order is parent to child. */
11198 lock.release();
11199 /*
11200 * Search for medias which are not attached to any machine, but
11201 * in the chain to an attached disk. Mediums are only consided
11202 * if they are:
11203 * - have only one child
11204 * - no references to any machines
11205 * - are of normal medium type
11206 */
11207 while (!pParent.isNull())
11208 {
11209 AutoCaller mac1(pParent);
11210 if (FAILED(mac1.rc())) return mac1.rc();
11211 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11212 if (pParent->i_getChildren().size() == 1)
11213 {
11214 if ( pParent->i_getMachineBackRefCount() == 0
11215 && pParent->i_getType() == MediumType_Normal
11216 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11217 llMedia.push_back(pParent);
11218 }
11219 else
11220 break;
11221 pParent = pParent->i_getParent();
11222 }
11223 }
11224 }
11225
11226 // real machine: then we need to use the proper method
11227 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11228
11229 if (FAILED(rc))
11230 return rc;
11231 }
11232
11233 return S_OK;
11234}
11235
11236/**
11237 * Perform deferred hard disk detachments.
11238 *
11239 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11240 * changed (not backed up).
11241 *
11242 * If @a aOnline is @c true then this method will also unlock the old hard
11243 * disks for which the new implicit diffs were created and will lock these new
11244 * diffs for writing.
11245 *
11246 * @param aOnline Whether the VM was online prior to this operation.
11247 *
11248 * @note Locks this object for writing!
11249 */
11250void Machine::i_commitMedia(bool aOnline /*= false*/)
11251{
11252 AutoCaller autoCaller(this);
11253 AssertComRCReturnVoid(autoCaller.rc());
11254
11255 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11256
11257 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11258
11259 HRESULT rc = S_OK;
11260
11261 /* no attach/detach operations -- nothing to do */
11262 if (!mMediumAttachments.isBackedUp())
11263 return;
11264
11265 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11266 bool fMediaNeedsLocking = false;
11267
11268 /* enumerate new attachments */
11269 for (MediumAttachmentList::const_iterator
11270 it = mMediumAttachments->begin();
11271 it != mMediumAttachments->end();
11272 ++it)
11273 {
11274 MediumAttachment *pAttach = *it;
11275
11276 pAttach->i_commit();
11277
11278 Medium *pMedium = pAttach->i_getMedium();
11279 bool fImplicit = pAttach->i_isImplicit();
11280
11281 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11282 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11283 fImplicit));
11284
11285 /** @todo convert all this Machine-based voodoo to MediumAttachment
11286 * based commit logic. */
11287 if (fImplicit)
11288 {
11289 /* convert implicit attachment to normal */
11290 pAttach->i_setImplicit(false);
11291
11292 if ( aOnline
11293 && pMedium
11294 && pAttach->i_getType() == DeviceType_HardDisk
11295 )
11296 {
11297 /* update the appropriate lock list */
11298 MediumLockList *pMediumLockList;
11299 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11300 AssertComRC(rc);
11301 if (pMediumLockList)
11302 {
11303 /* unlock if there's a need to change the locking */
11304 if (!fMediaNeedsLocking)
11305 {
11306 rc = mData->mSession.mLockedMedia.Unlock();
11307 AssertComRC(rc);
11308 fMediaNeedsLocking = true;
11309 }
11310 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11311 AssertComRC(rc);
11312 rc = pMediumLockList->Append(pMedium, true);
11313 AssertComRC(rc);
11314 }
11315 }
11316
11317 continue;
11318 }
11319
11320 if (pMedium)
11321 {
11322 /* was this medium attached before? */
11323 for (MediumAttachmentList::iterator
11324 oldIt = oldAtts.begin();
11325 oldIt != oldAtts.end();
11326 ++oldIt)
11327 {
11328 MediumAttachment *pOldAttach = *oldIt;
11329 if (pOldAttach->i_getMedium() == pMedium)
11330 {
11331 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11332
11333 /* yes: remove from old to avoid de-association */
11334 oldAtts.erase(oldIt);
11335 break;
11336 }
11337 }
11338 }
11339 }
11340
11341 /* enumerate remaining old attachments and de-associate from the
11342 * current machine state */
11343 for (MediumAttachmentList::const_iterator
11344 it = oldAtts.begin();
11345 it != oldAtts.end();
11346 ++it)
11347 {
11348 MediumAttachment *pAttach = *it;
11349 Medium *pMedium = pAttach->i_getMedium();
11350
11351 /* Detach only hard disks, since DVD/floppy media is detached
11352 * instantly in MountMedium. */
11353 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11354 {
11355 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11356
11357 /* now de-associate from the current machine state */
11358 rc = pMedium->i_removeBackReference(mData->mUuid);
11359 AssertComRC(rc);
11360
11361 if (aOnline)
11362 {
11363 /* unlock since medium is not used anymore */
11364 MediumLockList *pMediumLockList;
11365 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11366 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11367 {
11368 /* this happens for online snapshots, there the attachment
11369 * is changing, but only to a diff image created under
11370 * the old one, so there is no separate lock list */
11371 Assert(!pMediumLockList);
11372 }
11373 else
11374 {
11375 AssertComRC(rc);
11376 if (pMediumLockList)
11377 {
11378 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11379 AssertComRC(rc);
11380 }
11381 }
11382 }
11383 }
11384 }
11385
11386 /* take media locks again so that the locking state is consistent */
11387 if (fMediaNeedsLocking)
11388 {
11389 Assert(aOnline);
11390 rc = mData->mSession.mLockedMedia.Lock();
11391 AssertComRC(rc);
11392 }
11393
11394 /* commit the hard disk changes */
11395 mMediumAttachments.commit();
11396
11397 if (i_isSessionMachine())
11398 {
11399 /*
11400 * Update the parent machine to point to the new owner.
11401 * This is necessary because the stored parent will point to the
11402 * session machine otherwise and cause crashes or errors later
11403 * when the session machine gets invalid.
11404 */
11405 /** @todo Change the MediumAttachment class to behave like any other
11406 * class in this regard by creating peer MediumAttachment
11407 * objects for session machines and share the data with the peer
11408 * machine.
11409 */
11410 for (MediumAttachmentList::const_iterator
11411 it = mMediumAttachments->begin();
11412 it != mMediumAttachments->end();
11413 ++it)
11414 (*it)->i_updateParentMachine(mPeer);
11415
11416 /* attach new data to the primary machine and reshare it */
11417 mPeer->mMediumAttachments.attach(mMediumAttachments);
11418 }
11419
11420 return;
11421}
11422
11423/**
11424 * Perform deferred deletion of implicitly created diffs.
11425 *
11426 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11427 * changed (not backed up).
11428 *
11429 * @note Locks this object for writing!
11430 */
11431void Machine::i_rollbackMedia()
11432{
11433 AutoCaller autoCaller(this);
11434 AssertComRCReturnVoid(autoCaller.rc());
11435
11436 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11437 LogFlowThisFunc(("Entering rollbackMedia\n"));
11438
11439 HRESULT rc = S_OK;
11440
11441 /* no attach/detach operations -- nothing to do */
11442 if (!mMediumAttachments.isBackedUp())
11443 return;
11444
11445 /* enumerate new attachments */
11446 for (MediumAttachmentList::const_iterator
11447 it = mMediumAttachments->begin();
11448 it != mMediumAttachments->end();
11449 ++it)
11450 {
11451 MediumAttachment *pAttach = *it;
11452 /* Fix up the backrefs for DVD/floppy media. */
11453 if (pAttach->i_getType() != DeviceType_HardDisk)
11454 {
11455 Medium *pMedium = pAttach->i_getMedium();
11456 if (pMedium)
11457 {
11458 rc = pMedium->i_removeBackReference(mData->mUuid);
11459 AssertComRC(rc);
11460 }
11461 }
11462
11463 (*it)->i_rollback();
11464
11465 pAttach = *it;
11466 /* Fix up the backrefs for DVD/floppy media. */
11467 if (pAttach->i_getType() != DeviceType_HardDisk)
11468 {
11469 Medium *pMedium = pAttach->i_getMedium();
11470 if (pMedium)
11471 {
11472 rc = pMedium->i_addBackReference(mData->mUuid);
11473 AssertComRC(rc);
11474 }
11475 }
11476 }
11477
11478 /** @todo convert all this Machine-based voodoo to MediumAttachment
11479 * based rollback logic. */
11480 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11481
11482 return;
11483}
11484
11485/**
11486 * Returns true if the settings file is located in the directory named exactly
11487 * as the machine; this means, among other things, that the machine directory
11488 * should be auto-renamed.
11489 *
11490 * @param aSettingsDir if not NULL, the full machine settings file directory
11491 * name will be assigned there.
11492 *
11493 * @note Doesn't lock anything.
11494 * @note Not thread safe (must be called from this object's lock).
11495 */
11496bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11497{
11498 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11499 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11500 if (aSettingsDir)
11501 *aSettingsDir = strMachineDirName;
11502 strMachineDirName.stripPath(); // vmname
11503 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11504 strConfigFileOnly.stripPath() // vmname.vbox
11505 .stripSuffix(); // vmname
11506 /** @todo hack, make somehow use of ComposeMachineFilename */
11507 if (mUserData->s.fDirectoryIncludesUUID)
11508 strConfigFileOnly.appendPrintf(" (%RTuuid)", mData->mUuid.raw());
11509
11510 AssertReturn(!strMachineDirName.isEmpty(), false);
11511 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11512
11513 return strMachineDirName == strConfigFileOnly;
11514}
11515
11516/**
11517 * Discards all changes to machine settings.
11518 *
11519 * @param aNotify Whether to notify the direct session about changes or not.
11520 *
11521 * @note Locks objects for writing!
11522 */
11523void Machine::i_rollback(bool aNotify)
11524{
11525 AutoCaller autoCaller(this);
11526 AssertComRCReturn(autoCaller.rc(), (void)0);
11527
11528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11529
11530 if (!mStorageControllers.isNull())
11531 {
11532 if (mStorageControllers.isBackedUp())
11533 {
11534 /* unitialize all new devices (absent in the backed up list). */
11535 StorageControllerList *backedList = mStorageControllers.backedUpData();
11536 for (StorageControllerList::const_iterator
11537 it = mStorageControllers->begin();
11538 it != mStorageControllers->end();
11539 ++it)
11540 {
11541 if ( std::find(backedList->begin(), backedList->end(), *it)
11542 == backedList->end()
11543 )
11544 {
11545 (*it)->uninit();
11546 }
11547 }
11548
11549 /* restore the list */
11550 mStorageControllers.rollback();
11551 }
11552
11553 /* rollback any changes to devices after restoring the list */
11554 if (mData->flModifications & IsModified_Storage)
11555 {
11556 for (StorageControllerList::const_iterator
11557 it = mStorageControllers->begin();
11558 it != mStorageControllers->end();
11559 ++it)
11560 {
11561 (*it)->i_rollback();
11562 }
11563 }
11564 }
11565
11566 if (!mUSBControllers.isNull())
11567 {
11568 if (mUSBControllers.isBackedUp())
11569 {
11570 /* unitialize all new devices (absent in the backed up list). */
11571 USBControllerList *backedList = mUSBControllers.backedUpData();
11572 for (USBControllerList::const_iterator
11573 it = mUSBControllers->begin();
11574 it != mUSBControllers->end();
11575 ++it)
11576 {
11577 if ( std::find(backedList->begin(), backedList->end(), *it)
11578 == backedList->end()
11579 )
11580 {
11581 (*it)->uninit();
11582 }
11583 }
11584
11585 /* restore the list */
11586 mUSBControllers.rollback();
11587 }
11588
11589 /* rollback any changes to devices after restoring the list */
11590 if (mData->flModifications & IsModified_USB)
11591 {
11592 for (USBControllerList::const_iterator
11593 it = mUSBControllers->begin();
11594 it != mUSBControllers->end();
11595 ++it)
11596 {
11597 (*it)->i_rollback();
11598 }
11599 }
11600 }
11601
11602 mUserData.rollback();
11603
11604 mHWData.rollback();
11605
11606 if (mData->flModifications & IsModified_Storage)
11607 i_rollbackMedia();
11608
11609 if (mBIOSSettings)
11610 mBIOSSettings->i_rollback();
11611
11612 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11613 mRecordingSettings->i_rollback();
11614
11615 if (mGraphicsAdapter && (mData->flModifications & IsModified_GraphicsAdapter))
11616 mGraphicsAdapter->i_rollback();
11617
11618 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11619 mVRDEServer->i_rollback();
11620
11621 if (mAudioAdapter && (mData->flModifications & IsModified_AudioAdapter))
11622 mAudioAdapter->i_rollback();
11623
11624 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11625 mUSBDeviceFilters->i_rollback();
11626
11627 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11628 mBandwidthControl->i_rollback();
11629
11630 if (!mHWData.isNull())
11631 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11632 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11633 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11634 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11635
11636 if (mData->flModifications & IsModified_NetworkAdapters)
11637 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11638 if ( mNetworkAdapters[slot]
11639 && mNetworkAdapters[slot]->i_isModified())
11640 {
11641 mNetworkAdapters[slot]->i_rollback();
11642 networkAdapters[slot] = mNetworkAdapters[slot];
11643 }
11644
11645 if (mData->flModifications & IsModified_SerialPorts)
11646 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11647 if ( mSerialPorts[slot]
11648 && mSerialPorts[slot]->i_isModified())
11649 {
11650 mSerialPorts[slot]->i_rollback();
11651 serialPorts[slot] = mSerialPorts[slot];
11652 }
11653
11654 if (mData->flModifications & IsModified_ParallelPorts)
11655 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11656 if ( mParallelPorts[slot]
11657 && mParallelPorts[slot]->i_isModified())
11658 {
11659 mParallelPorts[slot]->i_rollback();
11660 parallelPorts[slot] = mParallelPorts[slot];
11661 }
11662
11663 if (aNotify)
11664 {
11665 /* inform the direct session about changes */
11666
11667 ComObjPtr<Machine> that = this;
11668 uint32_t flModifications = mData->flModifications;
11669 alock.release();
11670
11671 if (flModifications & IsModified_SharedFolders)
11672 that->i_onSharedFolderChange();
11673
11674 if (flModifications & IsModified_VRDEServer)
11675 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11676 if (flModifications & IsModified_USB)
11677 that->i_onUSBControllerChange();
11678
11679 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11680 if (networkAdapters[slot])
11681 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11682 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11683 if (serialPorts[slot])
11684 that->i_onSerialPortChange(serialPorts[slot]);
11685 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11686 if (parallelPorts[slot])
11687 that->i_onParallelPortChange(parallelPorts[slot]);
11688
11689 if (flModifications & IsModified_Storage)
11690 {
11691 for (StorageControllerList::const_iterator
11692 it = mStorageControllers->begin();
11693 it != mStorageControllers->end();
11694 ++it)
11695 {
11696 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11697 }
11698 }
11699
11700
11701#if 0
11702 if (flModifications & IsModified_BandwidthControl)
11703 that->onBandwidthControlChange();
11704#endif
11705 }
11706}
11707
11708/**
11709 * Commits all the changes to machine settings.
11710 *
11711 * Note that this operation is supposed to never fail.
11712 *
11713 * @note Locks this object and children for writing.
11714 */
11715void Machine::i_commit()
11716{
11717 AutoCaller autoCaller(this);
11718 AssertComRCReturnVoid(autoCaller.rc());
11719
11720 AutoCaller peerCaller(mPeer);
11721 AssertComRCReturnVoid(peerCaller.rc());
11722
11723 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11724
11725 /*
11726 * use safe commit to ensure Snapshot machines (that share mUserData)
11727 * will still refer to a valid memory location
11728 */
11729 mUserData.commitCopy();
11730
11731 mHWData.commit();
11732
11733 if (mMediumAttachments.isBackedUp())
11734 i_commitMedia(Global::IsOnline(mData->mMachineState));
11735
11736 mBIOSSettings->i_commit();
11737 mRecordingSettings->i_commit();
11738 mGraphicsAdapter->i_commit();
11739 mVRDEServer->i_commit();
11740 mAudioAdapter->i_commit();
11741 mUSBDeviceFilters->i_commit();
11742 mBandwidthControl->i_commit();
11743
11744 /* Since mNetworkAdapters is a list which might have been changed (resized)
11745 * without using the Backupable<> template we need to handle the copying
11746 * of the list entries manually, including the creation of peers for the
11747 * new objects. */
11748 bool commitNetworkAdapters = false;
11749 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11750 if (mPeer)
11751 {
11752 /* commit everything, even the ones which will go away */
11753 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11754 mNetworkAdapters[slot]->i_commit();
11755 /* copy over the new entries, creating a peer and uninit the original */
11756 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11757 for (size_t slot = 0; slot < newSize; slot++)
11758 {
11759 /* look if this adapter has a peer device */
11760 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11761 if (!peer)
11762 {
11763 /* no peer means the adapter is a newly created one;
11764 * create a peer owning data this data share it with */
11765 peer.createObject();
11766 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11767 }
11768 mPeer->mNetworkAdapters[slot] = peer;
11769 }
11770 /* uninit any no longer needed network adapters */
11771 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11772 mNetworkAdapters[slot]->uninit();
11773 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11774 {
11775 if (mPeer->mNetworkAdapters[slot])
11776 mPeer->mNetworkAdapters[slot]->uninit();
11777 }
11778 /* Keep the original network adapter count until this point, so that
11779 * discarding a chipset type change will not lose settings. */
11780 mNetworkAdapters.resize(newSize);
11781 mPeer->mNetworkAdapters.resize(newSize);
11782 }
11783 else
11784 {
11785 /* we have no peer (our parent is the newly created machine);
11786 * just commit changes to the network adapters */
11787 commitNetworkAdapters = true;
11788 }
11789 if (commitNetworkAdapters)
11790 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11791 mNetworkAdapters[slot]->i_commit();
11792
11793 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11794 mSerialPorts[slot]->i_commit();
11795 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11796 mParallelPorts[slot]->i_commit();
11797
11798 bool commitStorageControllers = false;
11799
11800 if (mStorageControllers.isBackedUp())
11801 {
11802 mStorageControllers.commit();
11803
11804 if (mPeer)
11805 {
11806 /* Commit all changes to new controllers (this will reshare data with
11807 * peers for those who have peers) */
11808 StorageControllerList *newList = new StorageControllerList();
11809 for (StorageControllerList::const_iterator
11810 it = mStorageControllers->begin();
11811 it != mStorageControllers->end();
11812 ++it)
11813 {
11814 (*it)->i_commit();
11815
11816 /* look if this controller has a peer device */
11817 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11818 if (!peer)
11819 {
11820 /* no peer means the device is a newly created one;
11821 * create a peer owning data this device share it with */
11822 peer.createObject();
11823 peer->init(mPeer, *it, true /* aReshare */);
11824 }
11825 else
11826 {
11827 /* remove peer from the old list */
11828 mPeer->mStorageControllers->remove(peer);
11829 }
11830 /* and add it to the new list */
11831 newList->push_back(peer);
11832 }
11833
11834 /* uninit old peer's controllers that are left */
11835 for (StorageControllerList::const_iterator
11836 it = mPeer->mStorageControllers->begin();
11837 it != mPeer->mStorageControllers->end();
11838 ++it)
11839 {
11840 (*it)->uninit();
11841 }
11842
11843 /* attach new list of controllers to our peer */
11844 mPeer->mStorageControllers.attach(newList);
11845 }
11846 else
11847 {
11848 /* we have no peer (our parent is the newly created machine);
11849 * just commit changes to devices */
11850 commitStorageControllers = true;
11851 }
11852 }
11853 else
11854 {
11855 /* the list of controllers itself is not changed,
11856 * just commit changes to controllers themselves */
11857 commitStorageControllers = true;
11858 }
11859
11860 if (commitStorageControllers)
11861 {
11862 for (StorageControllerList::const_iterator
11863 it = mStorageControllers->begin();
11864 it != mStorageControllers->end();
11865 ++it)
11866 {
11867 (*it)->i_commit();
11868 }
11869 }
11870
11871 bool commitUSBControllers = false;
11872
11873 if (mUSBControllers.isBackedUp())
11874 {
11875 mUSBControllers.commit();
11876
11877 if (mPeer)
11878 {
11879 /* Commit all changes to new controllers (this will reshare data with
11880 * peers for those who have peers) */
11881 USBControllerList *newList = new USBControllerList();
11882 for (USBControllerList::const_iterator
11883 it = mUSBControllers->begin();
11884 it != mUSBControllers->end();
11885 ++it)
11886 {
11887 (*it)->i_commit();
11888
11889 /* look if this controller has a peer device */
11890 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11891 if (!peer)
11892 {
11893 /* no peer means the device is a newly created one;
11894 * create a peer owning data this device share it with */
11895 peer.createObject();
11896 peer->init(mPeer, *it, true /* aReshare */);
11897 }
11898 else
11899 {
11900 /* remove peer from the old list */
11901 mPeer->mUSBControllers->remove(peer);
11902 }
11903 /* and add it to the new list */
11904 newList->push_back(peer);
11905 }
11906
11907 /* uninit old peer's controllers that are left */
11908 for (USBControllerList::const_iterator
11909 it = mPeer->mUSBControllers->begin();
11910 it != mPeer->mUSBControllers->end();
11911 ++it)
11912 {
11913 (*it)->uninit();
11914 }
11915
11916 /* attach new list of controllers to our peer */
11917 mPeer->mUSBControllers.attach(newList);
11918 }
11919 else
11920 {
11921 /* we have no peer (our parent is the newly created machine);
11922 * just commit changes to devices */
11923 commitUSBControllers = true;
11924 }
11925 }
11926 else
11927 {
11928 /* the list of controllers itself is not changed,
11929 * just commit changes to controllers themselves */
11930 commitUSBControllers = true;
11931 }
11932
11933 if (commitUSBControllers)
11934 {
11935 for (USBControllerList::const_iterator
11936 it = mUSBControllers->begin();
11937 it != mUSBControllers->end();
11938 ++it)
11939 {
11940 (*it)->i_commit();
11941 }
11942 }
11943
11944 if (i_isSessionMachine())
11945 {
11946 /* attach new data to the primary machine and reshare it */
11947 mPeer->mUserData.attach(mUserData);
11948 mPeer->mHWData.attach(mHWData);
11949 /* mmMediumAttachments is reshared by fixupMedia */
11950 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11951 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11952 }
11953}
11954
11955/**
11956 * Copies all the hardware data from the given machine.
11957 *
11958 * Currently, only called when the VM is being restored from a snapshot. In
11959 * particular, this implies that the VM is not running during this method's
11960 * call.
11961 *
11962 * @note This method must be called from under this object's lock.
11963 *
11964 * @note This method doesn't call #i_commit(), so all data remains backed up and
11965 * unsaved.
11966 */
11967void Machine::i_copyFrom(Machine *aThat)
11968{
11969 AssertReturnVoid(!i_isSnapshotMachine());
11970 AssertReturnVoid(aThat->i_isSnapshotMachine());
11971
11972 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11973
11974 mHWData.assignCopy(aThat->mHWData);
11975
11976 // create copies of all shared folders (mHWData after attaching a copy
11977 // contains just references to original objects)
11978 for (HWData::SharedFolderList::iterator
11979 it = mHWData->mSharedFolders.begin();
11980 it != mHWData->mSharedFolders.end();
11981 ++it)
11982 {
11983 ComObjPtr<SharedFolder> folder;
11984 folder.createObject();
11985 HRESULT rc = folder->initCopy(i_getMachine(), *it);
11986 AssertComRC(rc);
11987 *it = folder;
11988 }
11989
11990 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
11991 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
11992 mGraphicsAdapter->i_copyFrom(aThat->mGraphicsAdapter);
11993 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
11994 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
11995 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
11996 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
11997
11998 /* create private copies of all controllers */
11999 mStorageControllers.backup();
12000 mStorageControllers->clear();
12001 for (StorageControllerList::const_iterator
12002 it = aThat->mStorageControllers->begin();
12003 it != aThat->mStorageControllers->end();
12004 ++it)
12005 {
12006 ComObjPtr<StorageController> ctrl;
12007 ctrl.createObject();
12008 ctrl->initCopy(this, *it);
12009 mStorageControllers->push_back(ctrl);
12010 }
12011
12012 /* create private copies of all USB controllers */
12013 mUSBControllers.backup();
12014 mUSBControllers->clear();
12015 for (USBControllerList::const_iterator
12016 it = aThat->mUSBControllers->begin();
12017 it != aThat->mUSBControllers->end();
12018 ++it)
12019 {
12020 ComObjPtr<USBController> ctrl;
12021 ctrl.createObject();
12022 ctrl->initCopy(this, *it);
12023 mUSBControllers->push_back(ctrl);
12024 }
12025
12026 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12027 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12028 {
12029 if (mNetworkAdapters[slot].isNotNull())
12030 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12031 else
12032 {
12033 unconst(mNetworkAdapters[slot]).createObject();
12034 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12035 }
12036 }
12037 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12038 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12039 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12040 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12041}
12042
12043/**
12044 * Returns whether the given storage controller is hotplug capable.
12045 *
12046 * @returns true if the controller supports hotplugging
12047 * false otherwise.
12048 * @param enmCtrlType The controller type to check for.
12049 */
12050bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12051{
12052 ComPtr<ISystemProperties> systemProperties;
12053 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12054 if (FAILED(rc))
12055 return false;
12056
12057 BOOL aHotplugCapable = FALSE;
12058 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12059
12060 return RT_BOOL(aHotplugCapable);
12061}
12062
12063#ifdef VBOX_WITH_RESOURCE_USAGE_API
12064
12065void Machine::i_getDiskList(MediaList &list)
12066{
12067 for (MediumAttachmentList::const_iterator
12068 it = mMediumAttachments->begin();
12069 it != mMediumAttachments->end();
12070 ++it)
12071 {
12072 MediumAttachment *pAttach = *it;
12073 /* just in case */
12074 AssertContinue(pAttach);
12075
12076 AutoCaller localAutoCallerA(pAttach);
12077 if (FAILED(localAutoCallerA.rc())) continue;
12078
12079 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12080
12081 if (pAttach->i_getType() == DeviceType_HardDisk)
12082 list.push_back(pAttach->i_getMedium());
12083 }
12084}
12085
12086void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12087{
12088 AssertReturnVoid(isWriteLockOnCurrentThread());
12089 AssertPtrReturnVoid(aCollector);
12090
12091 pm::CollectorHAL *hal = aCollector->getHAL();
12092 /* Create sub metrics */
12093 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12094 "Percentage of processor time spent in user mode by the VM process.");
12095 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12096 "Percentage of processor time spent in kernel mode by the VM process.");
12097 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12098 "Size of resident portion of VM process in memory.");
12099 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12100 "Actual size of all VM disks combined.");
12101 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12102 "Network receive rate.");
12103 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12104 "Network transmit rate.");
12105 /* Create and register base metrics */
12106 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12107 cpuLoadUser, cpuLoadKernel);
12108 aCollector->registerBaseMetric(cpuLoad);
12109 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12110 ramUsageUsed);
12111 aCollector->registerBaseMetric(ramUsage);
12112 MediaList disks;
12113 i_getDiskList(disks);
12114 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12115 diskUsageUsed);
12116 aCollector->registerBaseMetric(diskUsage);
12117
12118 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12119 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12120 new pm::AggregateAvg()));
12121 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12122 new pm::AggregateMin()));
12123 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12124 new pm::AggregateMax()));
12125 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12126 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12127 new pm::AggregateAvg()));
12128 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12129 new pm::AggregateMin()));
12130 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12131 new pm::AggregateMax()));
12132
12133 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12134 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12135 new pm::AggregateAvg()));
12136 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12137 new pm::AggregateMin()));
12138 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12139 new pm::AggregateMax()));
12140
12141 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12142 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12143 new pm::AggregateAvg()));
12144 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12145 new pm::AggregateMin()));
12146 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12147 new pm::AggregateMax()));
12148
12149
12150 /* Guest metrics collector */
12151 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12152 aCollector->registerGuest(mCollectorGuest);
12153 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12154
12155 /* Create sub metrics */
12156 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12157 "Percentage of processor time spent in user mode as seen by the guest.");
12158 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12159 "Percentage of processor time spent in kernel mode as seen by the guest.");
12160 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12161 "Percentage of processor time spent idling as seen by the guest.");
12162
12163 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12164 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12165 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12166 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12167 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12168 pm::SubMetric *guestMemCache = new pm::SubMetric(
12169 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12170
12171 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12172 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12173
12174 /* Create and register base metrics */
12175 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12176 machineNetRx, machineNetTx);
12177 aCollector->registerBaseMetric(machineNetRate);
12178
12179 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12180 guestLoadUser, guestLoadKernel, guestLoadIdle);
12181 aCollector->registerBaseMetric(guestCpuLoad);
12182
12183 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12184 guestMemTotal, guestMemFree,
12185 guestMemBalloon, guestMemShared,
12186 guestMemCache, guestPagedTotal);
12187 aCollector->registerBaseMetric(guestCpuMem);
12188
12189 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12190 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12191 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12192 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12193
12194 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12195 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12196 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12197 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12198
12199 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12200 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12201 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12202 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12203
12204 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12205 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12206 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12207 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12208
12209 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12210 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12211 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12212 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12213
12214 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12215 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12216 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12217 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12218
12219 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12220 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12221 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12223
12224 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12225 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12226 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12228
12229 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12233
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12238
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12243}
12244
12245void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12246{
12247 AssertReturnVoid(isWriteLockOnCurrentThread());
12248
12249 if (aCollector)
12250 {
12251 aCollector->unregisterMetricsFor(aMachine);
12252 aCollector->unregisterBaseMetricsFor(aMachine);
12253 }
12254}
12255
12256#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12257
12258
12259////////////////////////////////////////////////////////////////////////////////
12260
12261DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12262
12263HRESULT SessionMachine::FinalConstruct()
12264{
12265 LogFlowThisFunc(("\n"));
12266
12267 mClientToken = NULL;
12268
12269 return BaseFinalConstruct();
12270}
12271
12272void SessionMachine::FinalRelease()
12273{
12274 LogFlowThisFunc(("\n"));
12275
12276 Assert(!mClientToken);
12277 /* paranoia, should not hang around any more */
12278 if (mClientToken)
12279 {
12280 delete mClientToken;
12281 mClientToken = NULL;
12282 }
12283
12284 uninit(Uninit::Unexpected);
12285
12286 BaseFinalRelease();
12287}
12288
12289/**
12290 * @note Must be called only by Machine::LockMachine() from its own write lock.
12291 */
12292HRESULT SessionMachine::init(Machine *aMachine)
12293{
12294 LogFlowThisFuncEnter();
12295 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12296
12297 AssertReturn(aMachine, E_INVALIDARG);
12298
12299 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12300
12301 /* Enclose the state transition NotReady->InInit->Ready */
12302 AutoInitSpan autoInitSpan(this);
12303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12304
12305 HRESULT rc = S_OK;
12306
12307 RT_ZERO(mAuthLibCtx);
12308
12309 /* create the machine client token */
12310 try
12311 {
12312 mClientToken = new ClientToken(aMachine, this);
12313 if (!mClientToken->isReady())
12314 {
12315 delete mClientToken;
12316 mClientToken = NULL;
12317 rc = E_FAIL;
12318 }
12319 }
12320 catch (std::bad_alloc &)
12321 {
12322 rc = E_OUTOFMEMORY;
12323 }
12324 if (FAILED(rc))
12325 return rc;
12326
12327 /* memorize the peer Machine */
12328 unconst(mPeer) = aMachine;
12329 /* share the parent pointer */
12330 unconst(mParent) = aMachine->mParent;
12331
12332 /* take the pointers to data to share */
12333 mData.share(aMachine->mData);
12334 mSSData.share(aMachine->mSSData);
12335
12336 mUserData.share(aMachine->mUserData);
12337 mHWData.share(aMachine->mHWData);
12338 mMediumAttachments.share(aMachine->mMediumAttachments);
12339
12340 mStorageControllers.allocate();
12341 for (StorageControllerList::const_iterator
12342 it = aMachine->mStorageControllers->begin();
12343 it != aMachine->mStorageControllers->end();
12344 ++it)
12345 {
12346 ComObjPtr<StorageController> ctl;
12347 ctl.createObject();
12348 ctl->init(this, *it);
12349 mStorageControllers->push_back(ctl);
12350 }
12351
12352 mUSBControllers.allocate();
12353 for (USBControllerList::const_iterator
12354 it = aMachine->mUSBControllers->begin();
12355 it != aMachine->mUSBControllers->end();
12356 ++it)
12357 {
12358 ComObjPtr<USBController> ctl;
12359 ctl.createObject();
12360 ctl->init(this, *it);
12361 mUSBControllers->push_back(ctl);
12362 }
12363
12364 unconst(mBIOSSettings).createObject();
12365 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12366 unconst(mRecordingSettings).createObject();
12367 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12368 /* create another GraphicsAdapter object that will be mutable */
12369 unconst(mGraphicsAdapter).createObject();
12370 mGraphicsAdapter->init(this, aMachine->mGraphicsAdapter);
12371 /* create another VRDEServer object that will be mutable */
12372 unconst(mVRDEServer).createObject();
12373 mVRDEServer->init(this, aMachine->mVRDEServer);
12374 /* create another audio adapter object that will be mutable */
12375 unconst(mAudioAdapter).createObject();
12376 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12377 /* create a list of serial ports that will be mutable */
12378 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12379 {
12380 unconst(mSerialPorts[slot]).createObject();
12381 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12382 }
12383 /* create a list of parallel ports that will be mutable */
12384 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12385 {
12386 unconst(mParallelPorts[slot]).createObject();
12387 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12388 }
12389
12390 /* create another USB device filters object that will be mutable */
12391 unconst(mUSBDeviceFilters).createObject();
12392 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12393
12394 /* create a list of network adapters that will be mutable */
12395 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12396 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12397 {
12398 unconst(mNetworkAdapters[slot]).createObject();
12399 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12400 }
12401
12402 /* create another bandwidth control object that will be mutable */
12403 unconst(mBandwidthControl).createObject();
12404 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12405
12406 /* default is to delete saved state on Saved -> PoweredOff transition */
12407 mRemoveSavedState = true;
12408
12409 /* Confirm a successful initialization when it's the case */
12410 autoInitSpan.setSucceeded();
12411
12412 miNATNetworksStarted = 0;
12413
12414 LogFlowThisFuncLeave();
12415 return rc;
12416}
12417
12418/**
12419 * Uninitializes this session object. If the reason is other than
12420 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12421 * or the client watcher code.
12422 *
12423 * @param aReason uninitialization reason
12424 *
12425 * @note Locks mParent + this object for writing.
12426 */
12427void SessionMachine::uninit(Uninit::Reason aReason)
12428{
12429 LogFlowThisFuncEnter();
12430 LogFlowThisFunc(("reason=%d\n", aReason));
12431
12432 /*
12433 * Strongly reference ourselves to prevent this object deletion after
12434 * mData->mSession.mMachine.setNull() below (which can release the last
12435 * reference and call the destructor). Important: this must be done before
12436 * accessing any members (and before AutoUninitSpan that does it as well).
12437 * This self reference will be released as the very last step on return.
12438 */
12439 ComObjPtr<SessionMachine> selfRef;
12440 if (aReason != Uninit::Unexpected)
12441 selfRef = this;
12442
12443 /* Enclose the state transition Ready->InUninit->NotReady */
12444 AutoUninitSpan autoUninitSpan(this);
12445 if (autoUninitSpan.uninitDone())
12446 {
12447 LogFlowThisFunc(("Already uninitialized\n"));
12448 LogFlowThisFuncLeave();
12449 return;
12450 }
12451
12452 if (autoUninitSpan.initFailed())
12453 {
12454 /* We've been called by init() because it's failed. It's not really
12455 * necessary (nor it's safe) to perform the regular uninit sequence
12456 * below, the following is enough.
12457 */
12458 LogFlowThisFunc(("Initialization failed.\n"));
12459 /* destroy the machine client token */
12460 if (mClientToken)
12461 {
12462 delete mClientToken;
12463 mClientToken = NULL;
12464 }
12465 uninitDataAndChildObjects();
12466 mData.free();
12467 unconst(mParent) = NULL;
12468 unconst(mPeer) = NULL;
12469 LogFlowThisFuncLeave();
12470 return;
12471 }
12472
12473 MachineState_T lastState;
12474 {
12475 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12476 lastState = mData->mMachineState;
12477 }
12478 NOREF(lastState);
12479
12480#ifdef VBOX_WITH_USB
12481 // release all captured USB devices, but do this before requesting the locks below
12482 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12483 {
12484 /* Console::captureUSBDevices() is called in the VM process only after
12485 * setting the machine state to Starting or Restoring.
12486 * Console::detachAllUSBDevices() will be called upon successful
12487 * termination. So, we need to release USB devices only if there was
12488 * an abnormal termination of a running VM.
12489 *
12490 * This is identical to SessionMachine::DetachAllUSBDevices except
12491 * for the aAbnormal argument. */
12492 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12493 AssertComRC(rc);
12494 NOREF(rc);
12495
12496 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12497 if (service)
12498 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12499 }
12500#endif /* VBOX_WITH_USB */
12501
12502 // we need to lock this object in uninit() because the lock is shared
12503 // with mPeer (as well as data we modify below). mParent lock is needed
12504 // by several calls to it.
12505 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12506
12507#ifdef VBOX_WITH_RESOURCE_USAGE_API
12508 /*
12509 * It is safe to call Machine::i_unregisterMetrics() here because
12510 * PerformanceCollector::samplerCallback no longer accesses guest methods
12511 * holding the lock.
12512 */
12513 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12514 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12515 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12516 if (mCollectorGuest)
12517 {
12518 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12519 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12520 mCollectorGuest = NULL;
12521 }
12522#endif
12523
12524 if (aReason == Uninit::Abnormal)
12525 {
12526 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12527
12528 /* reset the state to Aborted */
12529 if (mData->mMachineState != MachineState_Aborted)
12530 i_setMachineState(MachineState_Aborted);
12531 }
12532
12533 // any machine settings modified?
12534 if (mData->flModifications)
12535 {
12536 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12537 i_rollback(false /* aNotify */);
12538 }
12539
12540 mData->mSession.mPID = NIL_RTPROCESS;
12541
12542 if (aReason == Uninit::Unexpected)
12543 {
12544 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12545 * client watcher thread to update the set of machines that have open
12546 * sessions. */
12547 mParent->i_updateClientWatcher();
12548 }
12549
12550 /* uninitialize all remote controls */
12551 if (mData->mSession.mRemoteControls.size())
12552 {
12553 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12554 mData->mSession.mRemoteControls.size()));
12555
12556 /* Always restart a the beginning, since the iterator is invalidated
12557 * by using erase(). */
12558 for (Data::Session::RemoteControlList::iterator
12559 it = mData->mSession.mRemoteControls.begin();
12560 it != mData->mSession.mRemoteControls.end();
12561 it = mData->mSession.mRemoteControls.begin())
12562 {
12563 ComPtr<IInternalSessionControl> pControl = *it;
12564 mData->mSession.mRemoteControls.erase(it);
12565 multilock.release();
12566 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12567 HRESULT rc = pControl->Uninitialize();
12568 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12569 if (FAILED(rc))
12570 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12571 multilock.acquire();
12572 }
12573 mData->mSession.mRemoteControls.clear();
12574 }
12575
12576 /* Remove all references to the NAT network service. The service will stop
12577 * if all references (also from other VMs) are removed. */
12578 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12579 {
12580 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12581 {
12582 BOOL enabled;
12583 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12584 if ( FAILED(hrc)
12585 || !enabled)
12586 continue;
12587
12588 NetworkAttachmentType_T type;
12589 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12590 if ( SUCCEEDED(hrc)
12591 && type == NetworkAttachmentType_NATNetwork)
12592 {
12593 Bstr name;
12594 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12595 if (SUCCEEDED(hrc))
12596 {
12597 multilock.release();
12598 Utf8Str strName(name);
12599 LogRel(("VM '%s' stops using NAT network '%s'\n",
12600 mUserData->s.strName.c_str(), strName.c_str()));
12601 mParent->i_natNetworkRefDec(strName);
12602 multilock.acquire();
12603 }
12604 }
12605 }
12606 }
12607
12608 /*
12609 * An expected uninitialization can come only from #i_checkForDeath().
12610 * Otherwise it means that something's gone really wrong (for example,
12611 * the Session implementation has released the VirtualBox reference
12612 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12613 * etc). However, it's also possible, that the client releases the IPC
12614 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12615 * but the VirtualBox release event comes first to the server process.
12616 * This case is practically possible, so we should not assert on an
12617 * unexpected uninit, just log a warning.
12618 */
12619
12620 if (aReason == Uninit::Unexpected)
12621 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12622
12623 if (aReason != Uninit::Normal)
12624 {
12625 mData->mSession.mDirectControl.setNull();
12626 }
12627 else
12628 {
12629 /* this must be null here (see #OnSessionEnd()) */
12630 Assert(mData->mSession.mDirectControl.isNull());
12631 Assert(mData->mSession.mState == SessionState_Unlocking);
12632 Assert(!mData->mSession.mProgress.isNull());
12633 }
12634 if (mData->mSession.mProgress)
12635 {
12636 if (aReason == Uninit::Normal)
12637 mData->mSession.mProgress->i_notifyComplete(S_OK);
12638 else
12639 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12640 COM_IIDOF(ISession),
12641 getComponentName(),
12642 tr("The VM session was aborted"));
12643 mData->mSession.mProgress.setNull();
12644 }
12645
12646 if (mConsoleTaskData.mProgress)
12647 {
12648 Assert(aReason == Uninit::Abnormal);
12649 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12650 COM_IIDOF(ISession),
12651 getComponentName(),
12652 tr("The VM session was aborted"));
12653 mConsoleTaskData.mProgress.setNull();
12654 }
12655
12656 /* remove the association between the peer machine and this session machine */
12657 Assert( (SessionMachine*)mData->mSession.mMachine == this
12658 || aReason == Uninit::Unexpected);
12659
12660 /* reset the rest of session data */
12661 mData->mSession.mLockType = LockType_Null;
12662 mData->mSession.mMachine.setNull();
12663 mData->mSession.mState = SessionState_Unlocked;
12664 mData->mSession.mName.setNull();
12665
12666 /* destroy the machine client token before leaving the exclusive lock */
12667 if (mClientToken)
12668 {
12669 delete mClientToken;
12670 mClientToken = NULL;
12671 }
12672
12673 /* fire an event */
12674 mParent->i_onSessionStateChanged(mData->mUuid, SessionState_Unlocked);
12675
12676 uninitDataAndChildObjects();
12677
12678 /* free the essential data structure last */
12679 mData.free();
12680
12681 /* release the exclusive lock before setting the below two to NULL */
12682 multilock.release();
12683
12684 unconst(mParent) = NULL;
12685 unconst(mPeer) = NULL;
12686
12687 AuthLibUnload(&mAuthLibCtx);
12688
12689 LogFlowThisFuncLeave();
12690}
12691
12692// util::Lockable interface
12693////////////////////////////////////////////////////////////////////////////////
12694
12695/**
12696 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12697 * with the primary Machine instance (mPeer).
12698 */
12699RWLockHandle *SessionMachine::lockHandle() const
12700{
12701 AssertReturn(mPeer != NULL, NULL);
12702 return mPeer->lockHandle();
12703}
12704
12705// IInternalMachineControl methods
12706////////////////////////////////////////////////////////////////////////////////
12707
12708/**
12709 * Passes collected guest statistics to performance collector object
12710 */
12711HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12712 ULONG aCpuKernel, ULONG aCpuIdle,
12713 ULONG aMemTotal, ULONG aMemFree,
12714 ULONG aMemBalloon, ULONG aMemShared,
12715 ULONG aMemCache, ULONG aPageTotal,
12716 ULONG aAllocVMM, ULONG aFreeVMM,
12717 ULONG aBalloonedVMM, ULONG aSharedVMM,
12718 ULONG aVmNetRx, ULONG aVmNetTx)
12719{
12720#ifdef VBOX_WITH_RESOURCE_USAGE_API
12721 if (mCollectorGuest)
12722 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12723 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12724 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12725 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12726
12727 return S_OK;
12728#else
12729 NOREF(aValidStats);
12730 NOREF(aCpuUser);
12731 NOREF(aCpuKernel);
12732 NOREF(aCpuIdle);
12733 NOREF(aMemTotal);
12734 NOREF(aMemFree);
12735 NOREF(aMemBalloon);
12736 NOREF(aMemShared);
12737 NOREF(aMemCache);
12738 NOREF(aPageTotal);
12739 NOREF(aAllocVMM);
12740 NOREF(aFreeVMM);
12741 NOREF(aBalloonedVMM);
12742 NOREF(aSharedVMM);
12743 NOREF(aVmNetRx);
12744 NOREF(aVmNetTx);
12745 return E_NOTIMPL;
12746#endif
12747}
12748
12749////////////////////////////////////////////////////////////////////////////////
12750//
12751// SessionMachine task records
12752//
12753////////////////////////////////////////////////////////////////////////////////
12754
12755/**
12756 * Task record for saving the machine state.
12757 */
12758class SessionMachine::SaveStateTask
12759 : public Machine::Task
12760{
12761public:
12762 SaveStateTask(SessionMachine *m,
12763 Progress *p,
12764 const Utf8Str &t,
12765 Reason_T enmReason,
12766 const Utf8Str &strStateFilePath)
12767 : Task(m, p, t),
12768 m_enmReason(enmReason),
12769 m_strStateFilePath(strStateFilePath)
12770 {}
12771
12772private:
12773 void handler()
12774 {
12775 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12776 }
12777
12778 Reason_T m_enmReason;
12779 Utf8Str m_strStateFilePath;
12780
12781 friend class SessionMachine;
12782};
12783
12784/**
12785 * Task thread implementation for SessionMachine::SaveState(), called from
12786 * SessionMachine::taskHandler().
12787 *
12788 * @note Locks this object for writing.
12789 *
12790 * @param task
12791 * @return
12792 */
12793void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12794{
12795 LogFlowThisFuncEnter();
12796
12797 AutoCaller autoCaller(this);
12798 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12799 if (FAILED(autoCaller.rc()))
12800 {
12801 /* we might have been uninitialized because the session was accidentally
12802 * closed by the client, so don't assert */
12803 HRESULT rc = setError(E_FAIL,
12804 tr("The session has been accidentally closed"));
12805 task.m_pProgress->i_notifyComplete(rc);
12806 LogFlowThisFuncLeave();
12807 return;
12808 }
12809
12810 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12811
12812 HRESULT rc = S_OK;
12813
12814 try
12815 {
12816 ComPtr<IInternalSessionControl> directControl;
12817 if (mData->mSession.mLockType == LockType_VM)
12818 directControl = mData->mSession.mDirectControl;
12819 if (directControl.isNull())
12820 throw setError(VBOX_E_INVALID_VM_STATE,
12821 tr("Trying to save state without a running VM"));
12822 alock.release();
12823 BOOL fSuspendedBySave;
12824 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12825 Assert(!fSuspendedBySave);
12826 alock.acquire();
12827
12828 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12829 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12830 throw E_FAIL);
12831
12832 if (SUCCEEDED(rc))
12833 {
12834 mSSData->strStateFilePath = task.m_strStateFilePath;
12835
12836 /* save all VM settings */
12837 rc = i_saveSettings(NULL);
12838 // no need to check whether VirtualBox.xml needs saving also since
12839 // we can't have a name change pending at this point
12840 }
12841 else
12842 {
12843 // On failure, set the state to the state we had at the beginning.
12844 i_setMachineState(task.m_machineStateBackup);
12845 i_updateMachineStateOnClient();
12846
12847 // Delete the saved state file (might have been already created).
12848 // No need to check whether this is shared with a snapshot here
12849 // because we certainly created a fresh saved state file here.
12850 RTFileDelete(task.m_strStateFilePath.c_str());
12851 }
12852 }
12853 catch (HRESULT aRC) { rc = aRC; }
12854
12855 task.m_pProgress->i_notifyComplete(rc);
12856
12857 LogFlowThisFuncLeave();
12858}
12859
12860/**
12861 * @note Locks this object for writing.
12862 */
12863HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12864{
12865 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12866}
12867
12868HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12869{
12870 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12871
12872 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12873 if (FAILED(rc)) return rc;
12874
12875 if ( mData->mMachineState != MachineState_Running
12876 && mData->mMachineState != MachineState_Paused
12877 )
12878 return setError(VBOX_E_INVALID_VM_STATE,
12879 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12880 Global::stringifyMachineState(mData->mMachineState));
12881
12882 ComObjPtr<Progress> pProgress;
12883 pProgress.createObject();
12884 rc = pProgress->init(i_getVirtualBox(),
12885 static_cast<IMachine *>(this) /* aInitiator */,
12886 tr("Saving the execution state of the virtual machine"),
12887 FALSE /* aCancelable */);
12888 if (FAILED(rc))
12889 return rc;
12890
12891 Utf8Str strStateFilePath;
12892 i_composeSavedStateFilename(strStateFilePath);
12893
12894 /* create and start the task on a separate thread (note that it will not
12895 * start working until we release alock) */
12896 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12897 rc = pTask->createThread();
12898 if (FAILED(rc))
12899 return rc;
12900
12901 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12902 i_setMachineState(MachineState_Saving);
12903 i_updateMachineStateOnClient();
12904
12905 pProgress.queryInterfaceTo(aProgress.asOutParam());
12906
12907 return S_OK;
12908}
12909
12910/**
12911 * @note Locks this object for writing.
12912 */
12913HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12914{
12915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12916
12917 HRESULT rc = i_checkStateDependency(MutableStateDep);
12918 if (FAILED(rc)) return rc;
12919
12920 if ( mData->mMachineState != MachineState_PoweredOff
12921 && mData->mMachineState != MachineState_Teleported
12922 && mData->mMachineState != MachineState_Aborted
12923 )
12924 return setError(VBOX_E_INVALID_VM_STATE,
12925 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12926 Global::stringifyMachineState(mData->mMachineState));
12927
12928 com::Utf8Str stateFilePathFull;
12929 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12930 if (RT_FAILURE(vrc))
12931 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12932 tr("Invalid saved state file path '%s' (%Rrc)"),
12933 aSavedStateFile.c_str(),
12934 vrc);
12935
12936 mSSData->strStateFilePath = stateFilePathFull;
12937
12938 /* The below i_setMachineState() will detect the state transition and will
12939 * update the settings file */
12940
12941 return i_setMachineState(MachineState_Saved);
12942}
12943
12944/**
12945 * @note Locks this object for writing.
12946 */
12947HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12948{
12949 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12950
12951 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12952 if (FAILED(rc)) return rc;
12953
12954 if (mData->mMachineState != MachineState_Saved)
12955 return setError(VBOX_E_INVALID_VM_STATE,
12956 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12957 Global::stringifyMachineState(mData->mMachineState));
12958
12959 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12960
12961 /*
12962 * Saved -> PoweredOff transition will be detected in the SessionMachine
12963 * and properly handled.
12964 */
12965 rc = i_setMachineState(MachineState_PoweredOff);
12966 return rc;
12967}
12968
12969
12970/**
12971 * @note Locks the same as #i_setMachineState() does.
12972 */
12973HRESULT SessionMachine::updateState(MachineState_T aState)
12974{
12975 return i_setMachineState(aState);
12976}
12977
12978/**
12979 * @note Locks this object for writing.
12980 */
12981HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12982{
12983 IProgress *pProgress(aProgress);
12984
12985 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12986
12987 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12988
12989 if (mData->mSession.mState != SessionState_Locked)
12990 return VBOX_E_INVALID_OBJECT_STATE;
12991
12992 if (!mData->mSession.mProgress.isNull())
12993 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12994
12995 /* If we didn't reference the NAT network service yet, add a reference to
12996 * force a start */
12997 if (miNATNetworksStarted < 1)
12998 {
12999 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13000 {
13001 BOOL enabled;
13002 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13003 if ( FAILED(hrc)
13004 || !enabled)
13005 continue;
13006
13007 NetworkAttachmentType_T type;
13008 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13009 if ( SUCCEEDED(hrc)
13010 && type == NetworkAttachmentType_NATNetwork)
13011 {
13012 Bstr name;
13013 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13014 if (SUCCEEDED(hrc))
13015 {
13016 Utf8Str strName(name);
13017 LogRel(("VM '%s' starts using NAT network '%s'\n",
13018 mUserData->s.strName.c_str(), strName.c_str()));
13019 mPeer->lockHandle()->unlockWrite();
13020 mParent->i_natNetworkRefInc(strName);
13021#ifdef RT_LOCK_STRICT
13022 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13023#else
13024 mPeer->lockHandle()->lockWrite();
13025#endif
13026 }
13027 }
13028 }
13029 miNATNetworksStarted++;
13030 }
13031
13032 LogFlowThisFunc(("returns S_OK.\n"));
13033 return S_OK;
13034}
13035
13036/**
13037 * @note Locks this object for writing.
13038 */
13039HRESULT SessionMachine::endPowerUp(LONG aResult)
13040{
13041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13042
13043 if (mData->mSession.mState != SessionState_Locked)
13044 return VBOX_E_INVALID_OBJECT_STATE;
13045
13046 /* Finalize the LaunchVMProcess progress object. */
13047 if (mData->mSession.mProgress)
13048 {
13049 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13050 mData->mSession.mProgress.setNull();
13051 }
13052
13053 if (SUCCEEDED((HRESULT)aResult))
13054 {
13055#ifdef VBOX_WITH_RESOURCE_USAGE_API
13056 /* The VM has been powered up successfully, so it makes sense
13057 * now to offer the performance metrics for a running machine
13058 * object. Doing it earlier wouldn't be safe. */
13059 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13060 mData->mSession.mPID);
13061#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13062 }
13063
13064 return S_OK;
13065}
13066
13067/**
13068 * @note Locks this object for writing.
13069 */
13070HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13071{
13072 LogFlowThisFuncEnter();
13073
13074 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13075
13076 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13077 E_FAIL);
13078
13079 /* create a progress object to track operation completion */
13080 ComObjPtr<Progress> pProgress;
13081 pProgress.createObject();
13082 pProgress->init(i_getVirtualBox(),
13083 static_cast<IMachine *>(this) /* aInitiator */,
13084 tr("Stopping the virtual machine"),
13085 FALSE /* aCancelable */);
13086
13087 /* fill in the console task data */
13088 mConsoleTaskData.mLastState = mData->mMachineState;
13089 mConsoleTaskData.mProgress = pProgress;
13090
13091 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13092 i_setMachineState(MachineState_Stopping);
13093
13094 pProgress.queryInterfaceTo(aProgress.asOutParam());
13095
13096 return S_OK;
13097}
13098
13099/**
13100 * @note Locks this object for writing.
13101 */
13102HRESULT SessionMachine::endPoweringDown(LONG aResult,
13103 const com::Utf8Str &aErrMsg)
13104{
13105 HRESULT const hrcResult = (HRESULT)aResult;
13106 LogFlowThisFuncEnter();
13107
13108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13109
13110 AssertReturn( ( (SUCCEEDED(hrcResult) && mData->mMachineState == MachineState_PoweredOff)
13111 || (FAILED(hrcResult) && mData->mMachineState == MachineState_Stopping))
13112 && mConsoleTaskData.mLastState != MachineState_Null,
13113 E_FAIL);
13114
13115 /*
13116 * On failure, set the state to the state we had when BeginPoweringDown()
13117 * was called (this is expected by Console::PowerDown() and the associated
13118 * task). On success the VM process already changed the state to
13119 * MachineState_PoweredOff, so no need to do anything.
13120 */
13121 if (FAILED(hrcResult))
13122 i_setMachineState(mConsoleTaskData.mLastState);
13123
13124 /* notify the progress object about operation completion */
13125 Assert(mConsoleTaskData.mProgress);
13126 if (SUCCEEDED(hrcResult))
13127 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13128 else
13129 {
13130 if (aErrMsg.length())
13131 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult,
13132 COM_IIDOF(ISession),
13133 getComponentName(),
13134 aErrMsg.c_str());
13135 else
13136 mConsoleTaskData.mProgress->i_notifyComplete(hrcResult);
13137 }
13138
13139 /* clear out the temporary saved state data */
13140 mConsoleTaskData.mLastState = MachineState_Null;
13141 mConsoleTaskData.mProgress.setNull();
13142
13143 LogFlowThisFuncLeave();
13144 return S_OK;
13145}
13146
13147
13148/**
13149 * Goes through the USB filters of the given machine to see if the given
13150 * device matches any filter or not.
13151 *
13152 * @note Locks the same as USBController::hasMatchingFilter() does.
13153 */
13154HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13155 BOOL *aMatched,
13156 ULONG *aMaskedInterfaces)
13157{
13158 LogFlowThisFunc(("\n"));
13159
13160#ifdef VBOX_WITH_USB
13161 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13162#else
13163 NOREF(aDevice);
13164 NOREF(aMaskedInterfaces);
13165 *aMatched = FALSE;
13166#endif
13167
13168 return S_OK;
13169}
13170
13171/**
13172 * @note Locks the same as Host::captureUSBDevice() does.
13173 */
13174HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13175{
13176 LogFlowThisFunc(("\n"));
13177
13178#ifdef VBOX_WITH_USB
13179 /* if captureDeviceForVM() fails, it must have set extended error info */
13180 clearError();
13181 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13182 if (FAILED(rc)) return rc;
13183
13184 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13185 AssertReturn(service, E_FAIL);
13186 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13187#else
13188 RT_NOREF(aId, aCaptureFilename);
13189 return E_NOTIMPL;
13190#endif
13191}
13192
13193/**
13194 * @note Locks the same as Host::detachUSBDevice() does.
13195 */
13196HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13197 BOOL aDone)
13198{
13199 LogFlowThisFunc(("\n"));
13200
13201#ifdef VBOX_WITH_USB
13202 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13203 AssertReturn(service, E_FAIL);
13204 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13205#else
13206 NOREF(aId);
13207 NOREF(aDone);
13208 return E_NOTIMPL;
13209#endif
13210}
13211
13212/**
13213 * Inserts all machine filters to the USB proxy service and then calls
13214 * Host::autoCaptureUSBDevices().
13215 *
13216 * Called by Console from the VM process upon VM startup.
13217 *
13218 * @note Locks what called methods lock.
13219 */
13220HRESULT SessionMachine::autoCaptureUSBDevices()
13221{
13222 LogFlowThisFunc(("\n"));
13223
13224#ifdef VBOX_WITH_USB
13225 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13226 AssertComRC(rc);
13227 NOREF(rc);
13228
13229 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13230 AssertReturn(service, E_FAIL);
13231 return service->autoCaptureDevicesForVM(this);
13232#else
13233 return S_OK;
13234#endif
13235}
13236
13237/**
13238 * Removes all machine filters from the USB proxy service and then calls
13239 * Host::detachAllUSBDevices().
13240 *
13241 * Called by Console from the VM process upon normal VM termination or by
13242 * SessionMachine::uninit() upon abnormal VM termination (from under the
13243 * Machine/SessionMachine lock).
13244 *
13245 * @note Locks what called methods lock.
13246 */
13247HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13248{
13249 LogFlowThisFunc(("\n"));
13250
13251#ifdef VBOX_WITH_USB
13252 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13253 AssertComRC(rc);
13254 NOREF(rc);
13255
13256 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13257 AssertReturn(service, E_FAIL);
13258 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13259#else
13260 NOREF(aDone);
13261 return S_OK;
13262#endif
13263}
13264
13265/**
13266 * @note Locks this object for writing.
13267 */
13268HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13269 ComPtr<IProgress> &aProgress)
13270{
13271 LogFlowThisFuncEnter();
13272
13273 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13274 /*
13275 * We don't assert below because it might happen that a non-direct session
13276 * informs us it is closed right after we've been uninitialized -- it's ok.
13277 */
13278
13279 /* get IInternalSessionControl interface */
13280 ComPtr<IInternalSessionControl> control(aSession);
13281
13282 ComAssertRet(!control.isNull(), E_INVALIDARG);
13283
13284 /* Creating a Progress object requires the VirtualBox lock, and
13285 * thus locking it here is required by the lock order rules. */
13286 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13287
13288 if (control == mData->mSession.mDirectControl)
13289 {
13290 /* The direct session is being normally closed by the client process
13291 * ----------------------------------------------------------------- */
13292
13293 /* go to the closing state (essential for all open*Session() calls and
13294 * for #i_checkForDeath()) */
13295 Assert(mData->mSession.mState == SessionState_Locked);
13296 mData->mSession.mState = SessionState_Unlocking;
13297
13298 /* set direct control to NULL to release the remote instance */
13299 mData->mSession.mDirectControl.setNull();
13300 LogFlowThisFunc(("Direct control is set to NULL\n"));
13301
13302 if (mData->mSession.mProgress)
13303 {
13304 /* finalize the progress, someone might wait if a frontend
13305 * closes the session before powering on the VM. */
13306 mData->mSession.mProgress->notifyComplete(E_FAIL,
13307 COM_IIDOF(ISession),
13308 getComponentName(),
13309 tr("The VM session was closed before any attempt to power it on"));
13310 mData->mSession.mProgress.setNull();
13311 }
13312
13313 /* Create the progress object the client will use to wait until
13314 * #i_checkForDeath() is called to uninitialize this session object after
13315 * it releases the IPC semaphore.
13316 * Note! Because we're "reusing" mProgress here, this must be a proxy
13317 * object just like for LaunchVMProcess. */
13318 Assert(mData->mSession.mProgress.isNull());
13319 ComObjPtr<ProgressProxy> progress;
13320 progress.createObject();
13321 ComPtr<IUnknown> pPeer(mPeer);
13322 progress->init(mParent, pPeer,
13323 Bstr(tr("Closing session")).raw(),
13324 FALSE /* aCancelable */);
13325 progress.queryInterfaceTo(aProgress.asOutParam());
13326 mData->mSession.mProgress = progress;
13327 }
13328 else
13329 {
13330 /* the remote session is being normally closed */
13331 bool found = false;
13332 for (Data::Session::RemoteControlList::iterator
13333 it = mData->mSession.mRemoteControls.begin();
13334 it != mData->mSession.mRemoteControls.end();
13335 ++it)
13336 {
13337 if (control == *it)
13338 {
13339 found = true;
13340 // This MUST be erase(it), not remove(*it) as the latter
13341 // triggers a very nasty use after free due to the place where
13342 // the value "lives".
13343 mData->mSession.mRemoteControls.erase(it);
13344 break;
13345 }
13346 }
13347 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13348 E_INVALIDARG);
13349 }
13350
13351 /* signal the client watcher thread, because the client is going away */
13352 mParent->i_updateClientWatcher();
13353
13354 LogFlowThisFuncLeave();
13355 return S_OK;
13356}
13357
13358HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13359{
13360#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13361 ULONG uID;
13362 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13363 if (RT_SUCCESS(rc))
13364 {
13365 if (aID)
13366 *aID = uID;
13367 return S_OK;
13368 }
13369 return E_FAIL;
13370#else
13371 RT_NOREF(aParms, aID);
13372 ReturnComNotImplemented();
13373#endif
13374}
13375
13376HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13377{
13378#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13379 return mParent->i_onClipboardAreaUnregister(aID);
13380#else
13381 RT_NOREF(aID);
13382 ReturnComNotImplemented();
13383#endif
13384}
13385
13386HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13387{
13388#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13389 return mParent->i_onClipboardAreaAttach(aID);
13390#else
13391 RT_NOREF(aID);
13392 ReturnComNotImplemented();
13393#endif
13394}
13395HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13396{
13397#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13398 return mParent->i_onClipboardAreaDetach(aID);
13399#else
13400 RT_NOREF(aID);
13401 ReturnComNotImplemented();
13402#endif
13403}
13404
13405HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13406{
13407#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13408 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13409 if (aID)
13410 *aID = uID;
13411 return S_OK;
13412#else
13413 RT_NOREF(aID);
13414 ReturnComNotImplemented();
13415#endif
13416}
13417
13418HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13419{
13420#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13421 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13422 if (aRefCount)
13423 *aRefCount = uRefCount;
13424 return S_OK;
13425#else
13426 RT_NOREF(aID, aRefCount);
13427 ReturnComNotImplemented();
13428#endif
13429}
13430
13431HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13432 std::vector<com::Utf8Str> &aValues,
13433 std::vector<LONG64> &aTimestamps,
13434 std::vector<com::Utf8Str> &aFlags)
13435{
13436 LogFlowThisFunc(("\n"));
13437
13438#ifdef VBOX_WITH_GUEST_PROPS
13439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13440
13441 size_t cEntries = mHWData->mGuestProperties.size();
13442 aNames.resize(cEntries);
13443 aValues.resize(cEntries);
13444 aTimestamps.resize(cEntries);
13445 aFlags.resize(cEntries);
13446
13447 size_t i = 0;
13448 for (HWData::GuestPropertyMap::const_iterator
13449 it = mHWData->mGuestProperties.begin();
13450 it != mHWData->mGuestProperties.end();
13451 ++it, ++i)
13452 {
13453 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13454 aNames[i] = it->first;
13455 aValues[i] = it->second.strValue;
13456 aTimestamps[i] = it->second.mTimestamp;
13457
13458 /* If it is NULL, keep it NULL. */
13459 if (it->second.mFlags)
13460 {
13461 GuestPropWriteFlags(it->second.mFlags, szFlags);
13462 aFlags[i] = szFlags;
13463 }
13464 else
13465 aFlags[i] = "";
13466 }
13467 return S_OK;
13468#else
13469 ReturnComNotImplemented();
13470#endif
13471}
13472
13473HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13474 const com::Utf8Str &aValue,
13475 LONG64 aTimestamp,
13476 const com::Utf8Str &aFlags)
13477{
13478 LogFlowThisFunc(("\n"));
13479
13480#ifdef VBOX_WITH_GUEST_PROPS
13481 try
13482 {
13483 /*
13484 * Convert input up front.
13485 */
13486 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13487 if (aFlags.length())
13488 {
13489 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13490 AssertRCReturn(vrc, E_INVALIDARG);
13491 }
13492
13493 /*
13494 * Now grab the object lock, validate the state and do the update.
13495 */
13496
13497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13498
13499 if (!Global::IsOnline(mData->mMachineState))
13500 {
13501 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13502 VBOX_E_INVALID_VM_STATE);
13503 }
13504
13505 i_setModified(IsModified_MachineData);
13506 mHWData.backup();
13507
13508 bool fDelete = !aValue.length();
13509 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13510 if (it != mHWData->mGuestProperties.end())
13511 {
13512 if (!fDelete)
13513 {
13514 it->second.strValue = aValue;
13515 it->second.mTimestamp = aTimestamp;
13516 it->second.mFlags = fFlags;
13517 }
13518 else
13519 mHWData->mGuestProperties.erase(it);
13520
13521 mData->mGuestPropertiesModified = TRUE;
13522 }
13523 else if (!fDelete)
13524 {
13525 HWData::GuestProperty prop;
13526 prop.strValue = aValue;
13527 prop.mTimestamp = aTimestamp;
13528 prop.mFlags = fFlags;
13529
13530 mHWData->mGuestProperties[aName] = prop;
13531 mData->mGuestPropertiesModified = TRUE;
13532 }
13533
13534 alock.release();
13535
13536 mParent->i_onGuestPropertyChanged(mData->mUuid, aName, aValue, aFlags);
13537 }
13538 catch (...)
13539 {
13540 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13541 }
13542 return S_OK;
13543#else
13544 ReturnComNotImplemented();
13545#endif
13546}
13547
13548
13549HRESULT SessionMachine::lockMedia()
13550{
13551 AutoMultiWriteLock2 alock(this->lockHandle(),
13552 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13553
13554 AssertReturn( mData->mMachineState == MachineState_Starting
13555 || mData->mMachineState == MachineState_Restoring
13556 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13557
13558 clearError();
13559 alock.release();
13560 return i_lockMedia();
13561}
13562
13563HRESULT SessionMachine::unlockMedia()
13564{
13565 HRESULT hrc = i_unlockMedia();
13566 return hrc;
13567}
13568
13569HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13570 ComPtr<IMediumAttachment> &aNewAttachment)
13571{
13572 // request the host lock first, since might be calling Host methods for getting host drives;
13573 // next, protect the media tree all the while we're in here, as well as our member variables
13574 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13575 this->lockHandle(),
13576 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13577
13578 IMediumAttachment *iAttach = aAttachment;
13579 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13580
13581 Utf8Str ctrlName;
13582 LONG lPort;
13583 LONG lDevice;
13584 bool fTempEject;
13585 {
13586 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13587
13588 /* Need to query the details first, as the IMediumAttachment reference
13589 * might be to the original settings, which we are going to change. */
13590 ctrlName = pAttach->i_getControllerName();
13591 lPort = pAttach->i_getPort();
13592 lDevice = pAttach->i_getDevice();
13593 fTempEject = pAttach->i_getTempEject();
13594 }
13595
13596 if (!fTempEject)
13597 {
13598 /* Remember previously mounted medium. The medium before taking the
13599 * backup is not necessarily the same thing. */
13600 ComObjPtr<Medium> oldmedium;
13601 oldmedium = pAttach->i_getMedium();
13602
13603 i_setModified(IsModified_Storage);
13604 mMediumAttachments.backup();
13605
13606 // The backup operation makes the pAttach reference point to the
13607 // old settings. Re-get the correct reference.
13608 pAttach = i_findAttachment(*mMediumAttachments.data(),
13609 ctrlName,
13610 lPort,
13611 lDevice);
13612
13613 {
13614 AutoCaller autoAttachCaller(this);
13615 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13616
13617 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13618 if (!oldmedium.isNull())
13619 oldmedium->i_removeBackReference(mData->mUuid);
13620
13621 pAttach->i_updateMedium(NULL);
13622 pAttach->i_updateEjected();
13623 }
13624
13625 i_setModified(IsModified_Storage);
13626 }
13627 else
13628 {
13629 {
13630 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13631 pAttach->i_updateEjected();
13632 }
13633 }
13634
13635 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13636
13637 return S_OK;
13638}
13639
13640HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13641 com::Utf8Str &aResult)
13642{
13643 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13644
13645 HRESULT hr = S_OK;
13646
13647 if (!mAuthLibCtx.hAuthLibrary)
13648 {
13649 /* Load the external authentication library. */
13650 Bstr authLibrary;
13651 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13652
13653 Utf8Str filename = authLibrary;
13654
13655 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13656 if (RT_FAILURE(vrc))
13657 hr = setErrorBoth(E_FAIL, vrc,
13658 tr("Could not load the external authentication library '%s' (%Rrc)"),
13659 filename.c_str(), vrc);
13660 }
13661
13662 /* The auth library might need the machine lock. */
13663 alock.release();
13664
13665 if (FAILED(hr))
13666 return hr;
13667
13668 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13669 {
13670 enum VRDEAuthParams
13671 {
13672 parmUuid = 1,
13673 parmGuestJudgement,
13674 parmUser,
13675 parmPassword,
13676 parmDomain,
13677 parmClientId
13678 };
13679
13680 AuthResult result = AuthResultAccessDenied;
13681
13682 Guid uuid(aAuthParams[parmUuid]);
13683 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13684 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13685
13686 result = AuthLibAuthenticate(&mAuthLibCtx,
13687 uuid.raw(), guestJudgement,
13688 aAuthParams[parmUser].c_str(),
13689 aAuthParams[parmPassword].c_str(),
13690 aAuthParams[parmDomain].c_str(),
13691 u32ClientId);
13692
13693 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13694 size_t cbPassword = aAuthParams[parmPassword].length();
13695 if (cbPassword)
13696 {
13697 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13698 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13699 }
13700
13701 if (result == AuthResultAccessGranted)
13702 aResult = "granted";
13703 else
13704 aResult = "denied";
13705
13706 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13707 aAuthParams[parmUser].c_str(), aResult.c_str()));
13708 }
13709 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13710 {
13711 enum VRDEAuthDisconnectParams
13712 {
13713 parmUuid = 1,
13714 parmClientId
13715 };
13716
13717 Guid uuid(aAuthParams[parmUuid]);
13718 uint32_t u32ClientId = 0;
13719 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13720 }
13721 else
13722 {
13723 hr = E_INVALIDARG;
13724 }
13725
13726 return hr;
13727}
13728
13729// public methods only for internal purposes
13730/////////////////////////////////////////////////////////////////////////////
13731
13732#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13733/**
13734 * Called from the client watcher thread to check for expected or unexpected
13735 * death of the client process that has a direct session to this machine.
13736 *
13737 * On Win32 and on OS/2, this method is called only when we've got the
13738 * mutex (i.e. the client has either died or terminated normally) so it always
13739 * returns @c true (the client is terminated, the session machine is
13740 * uninitialized).
13741 *
13742 * On other platforms, the method returns @c true if the client process has
13743 * terminated normally or abnormally and the session machine was uninitialized,
13744 * and @c false if the client process is still alive.
13745 *
13746 * @note Locks this object for writing.
13747 */
13748bool SessionMachine::i_checkForDeath()
13749{
13750 Uninit::Reason reason;
13751 bool terminated = false;
13752
13753 /* Enclose autoCaller with a block because calling uninit() from under it
13754 * will deadlock. */
13755 {
13756 AutoCaller autoCaller(this);
13757 if (!autoCaller.isOk())
13758 {
13759 /* return true if not ready, to cause the client watcher to exclude
13760 * the corresponding session from watching */
13761 LogFlowThisFunc(("Already uninitialized!\n"));
13762 return true;
13763 }
13764
13765 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13766
13767 /* Determine the reason of death: if the session state is Closing here,
13768 * everything is fine. Otherwise it means that the client did not call
13769 * OnSessionEnd() before it released the IPC semaphore. This may happen
13770 * either because the client process has abnormally terminated, or
13771 * because it simply forgot to call ISession::Close() before exiting. We
13772 * threat the latter also as an abnormal termination (see
13773 * Session::uninit() for details). */
13774 reason = mData->mSession.mState == SessionState_Unlocking ?
13775 Uninit::Normal :
13776 Uninit::Abnormal;
13777
13778 if (mClientToken)
13779 terminated = mClientToken->release();
13780 } /* AutoCaller block */
13781
13782 if (terminated)
13783 uninit(reason);
13784
13785 return terminated;
13786}
13787
13788void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13789{
13790 LogFlowThisFunc(("\n"));
13791
13792 strTokenId.setNull();
13793
13794 AutoCaller autoCaller(this);
13795 AssertComRCReturnVoid(autoCaller.rc());
13796
13797 Assert(mClientToken);
13798 if (mClientToken)
13799 mClientToken->getId(strTokenId);
13800}
13801#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13802IToken *SessionMachine::i_getToken()
13803{
13804 LogFlowThisFunc(("\n"));
13805
13806 AutoCaller autoCaller(this);
13807 AssertComRCReturn(autoCaller.rc(), NULL);
13808
13809 Assert(mClientToken);
13810 if (mClientToken)
13811 return mClientToken->getToken();
13812 else
13813 return NULL;
13814}
13815#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13816
13817Machine::ClientToken *SessionMachine::i_getClientToken()
13818{
13819 LogFlowThisFunc(("\n"));
13820
13821 AutoCaller autoCaller(this);
13822 AssertComRCReturn(autoCaller.rc(), NULL);
13823
13824 return mClientToken;
13825}
13826
13827
13828/**
13829 * @note Locks this object for reading.
13830 */
13831HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13832{
13833 LogFlowThisFunc(("\n"));
13834
13835 AutoCaller autoCaller(this);
13836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13837
13838 ComPtr<IInternalSessionControl> directControl;
13839 {
13840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13841 if (mData->mSession.mLockType == LockType_VM)
13842 directControl = mData->mSession.mDirectControl;
13843 }
13844
13845 /* ignore notifications sent after #OnSessionEnd() is called */
13846 if (!directControl)
13847 return S_OK;
13848
13849 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13850}
13851
13852/**
13853 * @note Locks this object for reading.
13854 */
13855HRESULT SessionMachine::i_onNATRedirectRuleChanged(ULONG ulSlot, BOOL aNatRuleRemove, const Utf8Str &aRuleName,
13856 NATProtocol_T aProto, const Utf8Str &aHostIp, LONG aHostPort,
13857 const Utf8Str &aGuestIp, LONG aGuestPort)
13858{
13859 LogFlowThisFunc(("\n"));
13860
13861 AutoCaller autoCaller(this);
13862 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13863
13864 ComPtr<IInternalSessionControl> directControl;
13865 {
13866 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13867 if (mData->mSession.mLockType == LockType_VM)
13868 directControl = mData->mSession.mDirectControl;
13869 }
13870
13871 /* ignore notifications sent after #OnSessionEnd() is called */
13872 if (!directControl)
13873 return S_OK;
13874 /*
13875 * instead acting like callback we ask IVirtualBox deliver corresponding event
13876 */
13877
13878 mParent->i_onNatRedirectChanged(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13879 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13880 return S_OK;
13881}
13882
13883/**
13884 * @note Locks this object for reading.
13885 */
13886HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13887{
13888 LogFlowThisFunc(("\n"));
13889
13890 AutoCaller autoCaller(this);
13891 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13892
13893 ComPtr<IInternalSessionControl> directControl;
13894 {
13895 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13896 if (mData->mSession.mLockType == LockType_VM)
13897 directControl = mData->mSession.mDirectControl;
13898 }
13899
13900 /* ignore notifications sent after #OnSessionEnd() is called */
13901 if (!directControl)
13902 return S_OK;
13903
13904 return directControl->OnAudioAdapterChange(audioAdapter);
13905}
13906
13907/**
13908 * @note Locks this object for reading.
13909 */
13910HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13911{
13912 LogFlowThisFunc(("\n"));
13913
13914 AutoCaller autoCaller(this);
13915 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13916
13917 ComPtr<IInternalSessionControl> directControl;
13918 {
13919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13920 if (mData->mSession.mLockType == LockType_VM)
13921 directControl = mData->mSession.mDirectControl;
13922 }
13923
13924 /* ignore notifications sent after #OnSessionEnd() is called */
13925 if (!directControl)
13926 return S_OK;
13927
13928 return directControl->OnSerialPortChange(serialPort);
13929}
13930
13931/**
13932 * @note Locks this object for reading.
13933 */
13934HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13935{
13936 LogFlowThisFunc(("\n"));
13937
13938 AutoCaller autoCaller(this);
13939 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13940
13941 ComPtr<IInternalSessionControl> directControl;
13942 {
13943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13944 if (mData->mSession.mLockType == LockType_VM)
13945 directControl = mData->mSession.mDirectControl;
13946 }
13947
13948 /* ignore notifications sent after #OnSessionEnd() is called */
13949 if (!directControl)
13950 return S_OK;
13951
13952 return directControl->OnParallelPortChange(parallelPort);
13953}
13954
13955/**
13956 * @note Locks this object for reading.
13957 */
13958HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
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 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
13973
13974 /* ignore notifications sent after #OnSessionEnd() is called */
13975 if (!directControl)
13976 return S_OK;
13977
13978 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 if (mData->mSession.mLockType == LockType_VM)
13995 directControl = mData->mSession.mDirectControl;
13996 }
13997
13998 mParent->i_onMediumChanged(aAttachment);
13999
14000 /* ignore notifications sent after #OnSessionEnd() is called */
14001 if (!directControl)
14002 return S_OK;
14003
14004 return directControl->OnMediumChange(aAttachment, aForce);
14005}
14006
14007HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14008{
14009 LogFlowThisFunc(("\n"));
14010
14011 AutoCaller autoCaller(this);
14012 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14013
14014 ComPtr<IInternalSessionControl> directControl;
14015 {
14016 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14017 if (mData->mSession.mLockType == LockType_VM)
14018 directControl = mData->mSession.mDirectControl;
14019 }
14020
14021 /* ignore notifications sent after #OnSessionEnd() is called */
14022 if (!directControl)
14023 return S_OK;
14024
14025 return directControl->OnVMProcessPriorityChange(aPriority);
14026}
14027
14028/**
14029 * @note Locks this object for reading.
14030 */
14031HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14032{
14033 LogFlowThisFunc(("\n"));
14034
14035 AutoCaller autoCaller(this);
14036 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14037
14038 ComPtr<IInternalSessionControl> directControl;
14039 {
14040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14041 if (mData->mSession.mLockType == LockType_VM)
14042 directControl = mData->mSession.mDirectControl;
14043 }
14044
14045 /* ignore notifications sent after #OnSessionEnd() is called */
14046 if (!directControl)
14047 return S_OK;
14048
14049 return directControl->OnCPUChange(aCPU, aRemove);
14050}
14051
14052HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14053{
14054 LogFlowThisFunc(("\n"));
14055
14056 AutoCaller autoCaller(this);
14057 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14058
14059 ComPtr<IInternalSessionControl> directControl;
14060 {
14061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14062 if (mData->mSession.mLockType == LockType_VM)
14063 directControl = mData->mSession.mDirectControl;
14064 }
14065
14066 /* ignore notifications sent after #OnSessionEnd() is called */
14067 if (!directControl)
14068 return S_OK;
14069
14070 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14071}
14072
14073/**
14074 * @note Locks this object for reading.
14075 */
14076HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14077{
14078 LogFlowThisFunc(("\n"));
14079
14080 AutoCaller autoCaller(this);
14081 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14082
14083 ComPtr<IInternalSessionControl> directControl;
14084 {
14085 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14086 if (mData->mSession.mLockType == LockType_VM)
14087 directControl = mData->mSession.mDirectControl;
14088 }
14089
14090 /* ignore notifications sent after #OnSessionEnd() is called */
14091 if (!directControl)
14092 return S_OK;
14093
14094 return directControl->OnVRDEServerChange(aRestart);
14095}
14096
14097/**
14098 * @note Locks this object for reading.
14099 */
14100HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14101{
14102 LogFlowThisFunc(("\n"));
14103
14104 AutoCaller autoCaller(this);
14105 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14106
14107 ComPtr<IInternalSessionControl> directControl;
14108 {
14109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14110 if (mData->mSession.mLockType == LockType_VM)
14111 directControl = mData->mSession.mDirectControl;
14112 }
14113
14114 /* ignore notifications sent after #OnSessionEnd() is called */
14115 if (!directControl)
14116 return S_OK;
14117
14118 return directControl->OnRecordingChange(aEnable);
14119}
14120
14121/**
14122 * @note Locks this object for reading.
14123 */
14124HRESULT SessionMachine::i_onUSBControllerChange()
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->OnUSBControllerChange();
14143}
14144
14145/**
14146 * @note Locks this object for reading.
14147 */
14148HRESULT SessionMachine::i_onSharedFolderChange()
14149{
14150 LogFlowThisFunc(("\n"));
14151
14152 AutoCaller autoCaller(this);
14153 AssertComRCReturnRC(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->OnSharedFolderChange(FALSE /* aGlobal */);
14167}
14168
14169/**
14170 * @note Locks this object for reading.
14171 */
14172HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14173{
14174 LogFlowThisFunc(("\n"));
14175
14176 AutoCaller autoCaller(this);
14177 AssertComRCReturnRC(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->OnClipboardModeChange(aClipboardMode);
14191}
14192
14193/**
14194 * @note Locks this object for reading.
14195 */
14196HRESULT SessionMachine::i_onClipboardFileTransferModeChange(BOOL aEnable)
14197{
14198 LogFlowThisFunc(("\n"));
14199
14200 AutoCaller autoCaller(this);
14201 AssertComRCReturnRC(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->OnClipboardFileTransferModeChange(aEnable);
14215}
14216
14217/**
14218 * @note Locks this object for reading.
14219 */
14220HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
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->OnDnDModeChange(aDnDMode);
14239}
14240
14241/**
14242 * @note Locks this object for reading.
14243 */
14244HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14245{
14246 LogFlowThisFunc(("\n"));
14247
14248 AutoCaller autoCaller(this);
14249 AssertComRCReturn(autoCaller.rc(), 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->OnBandwidthGroupChange(aBandwidthGroup);
14263}
14264
14265/**
14266 * @note Locks this object for reading.
14267 */
14268HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14269{
14270 LogFlowThisFunc(("\n"));
14271
14272 AutoCaller autoCaller(this);
14273 AssertComRCReturn(autoCaller.rc(), 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->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14287}
14288
14289/**
14290 * Returns @c true if this machine's USB controller reports it has a matching
14291 * filter for the given USB device and @c false otherwise.
14292 *
14293 * @note locks this object for reading.
14294 */
14295bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14296{
14297 AutoCaller autoCaller(this);
14298 /* silently return if not ready -- this method may be called after the
14299 * direct machine session has been called */
14300 if (!autoCaller.isOk())
14301 return false;
14302
14303#ifdef VBOX_WITH_USB
14304 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14305
14306 switch (mData->mMachineState)
14307 {
14308 case MachineState_Starting:
14309 case MachineState_Restoring:
14310 case MachineState_TeleportingIn:
14311 case MachineState_Paused:
14312 case MachineState_Running:
14313 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14314 * elsewhere... */
14315 alock.release();
14316 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14317 default: break;
14318 }
14319#else
14320 NOREF(aDevice);
14321 NOREF(aMaskedIfs);
14322#endif
14323 return false;
14324}
14325
14326/**
14327 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14328 */
14329HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14330 IVirtualBoxErrorInfo *aError,
14331 ULONG aMaskedIfs,
14332 const com::Utf8Str &aCaptureFilename)
14333{
14334 LogFlowThisFunc(("\n"));
14335
14336 AutoCaller autoCaller(this);
14337
14338 /* This notification may happen after the machine object has been
14339 * uninitialized (the session was closed), so don't assert. */
14340 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14341
14342 ComPtr<IInternalSessionControl> directControl;
14343 {
14344 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14345 if (mData->mSession.mLockType == LockType_VM)
14346 directControl = mData->mSession.mDirectControl;
14347 }
14348
14349 /* fail on notifications sent after #OnSessionEnd() is called, it is
14350 * expected by the caller */
14351 if (!directControl)
14352 return E_FAIL;
14353
14354 /* No locks should be held at this point. */
14355 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14356 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14357
14358 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14359}
14360
14361/**
14362 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14363 */
14364HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14365 IVirtualBoxErrorInfo *aError)
14366{
14367 LogFlowThisFunc(("\n"));
14368
14369 AutoCaller autoCaller(this);
14370
14371 /* This notification may happen after the machine object has been
14372 * uninitialized (the session was closed), so don't assert. */
14373 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14374
14375 ComPtr<IInternalSessionControl> directControl;
14376 {
14377 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14378 if (mData->mSession.mLockType == LockType_VM)
14379 directControl = mData->mSession.mDirectControl;
14380 }
14381
14382 /* fail on notifications sent after #OnSessionEnd() is called, it is
14383 * expected by the caller */
14384 if (!directControl)
14385 return E_FAIL;
14386
14387 /* No locks should be held at this point. */
14388 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14389 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14390
14391 return directControl->OnUSBDeviceDetach(aId, aError);
14392}
14393
14394// protected methods
14395/////////////////////////////////////////////////////////////////////////////
14396
14397/**
14398 * Deletes the given file if it is no longer in use by either the current machine state
14399 * (if the machine is "saved") or any of the machine's snapshots.
14400 *
14401 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14402 * but is different for each SnapshotMachine. When calling this, the order of calling this
14403 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14404 * is therefore critical. I know, it's all rather messy.
14405 *
14406 * @param strStateFile
14407 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14408 * the test for whether the saved state file is in use.
14409 */
14410void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14411 Snapshot *pSnapshotToIgnore)
14412{
14413 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14414 if ( (strStateFile.isNotEmpty())
14415 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14416 )
14417 // ... and it must also not be shared with other snapshots
14418 if ( !mData->mFirstSnapshot
14419 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14420 // this checks the SnapshotMachine's state file paths
14421 )
14422 RTFileDelete(strStateFile.c_str());
14423}
14424
14425/**
14426 * Locks the attached media.
14427 *
14428 * All attached hard disks are locked for writing and DVD/floppy are locked for
14429 * reading. Parents of attached hard disks (if any) are locked for reading.
14430 *
14431 * This method also performs accessibility check of all media it locks: if some
14432 * media is inaccessible, the method will return a failure and a bunch of
14433 * extended error info objects per each inaccessible medium.
14434 *
14435 * Note that this method is atomic: if it returns a success, all media are
14436 * locked as described above; on failure no media is locked at all (all
14437 * succeeded individual locks will be undone).
14438 *
14439 * The caller is responsible for doing the necessary state sanity checks.
14440 *
14441 * The locks made by this method must be undone by calling #unlockMedia() when
14442 * no more needed.
14443 */
14444HRESULT SessionMachine::i_lockMedia()
14445{
14446 AutoCaller autoCaller(this);
14447 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14448
14449 AutoMultiWriteLock2 alock(this->lockHandle(),
14450 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14451
14452 /* bail out if trying to lock things with already set up locking */
14453 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14454
14455 MultiResult mrc(S_OK);
14456
14457 /* Collect locking information for all medium objects attached to the VM. */
14458 for (MediumAttachmentList::const_iterator
14459 it = mMediumAttachments->begin();
14460 it != mMediumAttachments->end();
14461 ++it)
14462 {
14463 MediumAttachment *pAtt = *it;
14464 DeviceType_T devType = pAtt->i_getType();
14465 Medium *pMedium = pAtt->i_getMedium();
14466
14467 MediumLockList *pMediumLockList(new MediumLockList());
14468 // There can be attachments without a medium (floppy/dvd), and thus
14469 // it's impossible to create a medium lock list. It still makes sense
14470 // to have the empty medium lock list in the map in case a medium is
14471 // attached later.
14472 if (pMedium != NULL)
14473 {
14474 MediumType_T mediumType = pMedium->i_getType();
14475 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14476 || mediumType == MediumType_Shareable;
14477 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14478
14479 alock.release();
14480 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14481 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14482 false /* fMediumLockWriteAll */,
14483 NULL,
14484 *pMediumLockList);
14485 alock.acquire();
14486 if (FAILED(mrc))
14487 {
14488 delete pMediumLockList;
14489 mData->mSession.mLockedMedia.Clear();
14490 break;
14491 }
14492 }
14493
14494 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14495 if (FAILED(rc))
14496 {
14497 mData->mSession.mLockedMedia.Clear();
14498 mrc = setError(rc,
14499 tr("Collecting locking information for all attached media failed"));
14500 break;
14501 }
14502 }
14503
14504 if (SUCCEEDED(mrc))
14505 {
14506 /* Now lock all media. If this fails, nothing is locked. */
14507 alock.release();
14508 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14509 alock.acquire();
14510 if (FAILED(rc))
14511 {
14512 mrc = setError(rc,
14513 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14514 }
14515 }
14516
14517 return mrc;
14518}
14519
14520/**
14521 * Undoes the locks made by by #lockMedia().
14522 */
14523HRESULT SessionMachine::i_unlockMedia()
14524{
14525 AutoCaller autoCaller(this);
14526 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14527
14528 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14529
14530 /* we may be holding important error info on the current thread;
14531 * preserve it */
14532 ErrorInfoKeeper eik;
14533
14534 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14535 AssertComRC(rc);
14536 return rc;
14537}
14538
14539/**
14540 * Helper to change the machine state (reimplementation).
14541 *
14542 * @note Locks this object for writing.
14543 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14544 * it can cause crashes in random places due to unexpectedly committing
14545 * the current settings. The caller is responsible for that. The call
14546 * to saveStateSettings is fine, because this method does not commit.
14547 */
14548HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14549{
14550 LogFlowThisFuncEnter();
14551 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14552
14553 AutoCaller autoCaller(this);
14554 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14555
14556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14557
14558 MachineState_T oldMachineState = mData->mMachineState;
14559
14560 AssertMsgReturn(oldMachineState != aMachineState,
14561 ("oldMachineState=%s, aMachineState=%s\n",
14562 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14563 E_FAIL);
14564
14565 HRESULT rc = S_OK;
14566
14567 int stsFlags = 0;
14568 bool deleteSavedState = false;
14569
14570 /* detect some state transitions */
14571
14572 if ( ( oldMachineState == MachineState_Saved
14573 && aMachineState == MachineState_Restoring)
14574 || ( ( oldMachineState == MachineState_PoweredOff
14575 || oldMachineState == MachineState_Teleported
14576 || oldMachineState == MachineState_Aborted
14577 )
14578 && ( aMachineState == MachineState_TeleportingIn
14579 || aMachineState == MachineState_Starting
14580 )
14581 )
14582 )
14583 {
14584 /* The EMT thread is about to start */
14585
14586 /* Nothing to do here for now... */
14587
14588 /// @todo NEWMEDIA don't let mDVDDrive and other children
14589 /// change anything when in the Starting/Restoring state
14590 }
14591 else if ( ( oldMachineState == MachineState_Running
14592 || oldMachineState == MachineState_Paused
14593 || oldMachineState == MachineState_Teleporting
14594 || oldMachineState == MachineState_OnlineSnapshotting
14595 || oldMachineState == MachineState_LiveSnapshotting
14596 || oldMachineState == MachineState_Stuck
14597 || oldMachineState == MachineState_Starting
14598 || oldMachineState == MachineState_Stopping
14599 || oldMachineState == MachineState_Saving
14600 || oldMachineState == MachineState_Restoring
14601 || oldMachineState == MachineState_TeleportingPausedVM
14602 || oldMachineState == MachineState_TeleportingIn
14603 )
14604 && ( aMachineState == MachineState_PoweredOff
14605 || aMachineState == MachineState_Saved
14606 || aMachineState == MachineState_Teleported
14607 || aMachineState == MachineState_Aborted
14608 )
14609 )
14610 {
14611 /* The EMT thread has just stopped, unlock attached media. Note that as
14612 * opposed to locking that is done from Console, we do unlocking here
14613 * because the VM process may have aborted before having a chance to
14614 * properly unlock all media it locked. */
14615
14616 unlockMedia();
14617 }
14618
14619 if (oldMachineState == MachineState_Restoring)
14620 {
14621 if (aMachineState != MachineState_Saved)
14622 {
14623 /*
14624 * delete the saved state file once the machine has finished
14625 * restoring from it (note that Console sets the state from
14626 * Restoring to Saved if the VM couldn't restore successfully,
14627 * to give the user an ability to fix an error and retry --
14628 * we keep the saved state file in this case)
14629 */
14630 deleteSavedState = true;
14631 }
14632 }
14633 else if ( oldMachineState == MachineState_Saved
14634 && ( aMachineState == MachineState_PoweredOff
14635 || aMachineState == MachineState_Aborted
14636 || aMachineState == MachineState_Teleported
14637 )
14638 )
14639 {
14640 /*
14641 * delete the saved state after SessionMachine::ForgetSavedState() is called
14642 * or if the VM process (owning a direct VM session) crashed while the
14643 * VM was Saved
14644 */
14645
14646 /// @todo (dmik)
14647 // Not sure that deleting the saved state file just because of the
14648 // client death before it attempted to restore the VM is a good
14649 // thing. But when it crashes we need to go to the Aborted state
14650 // which cannot have the saved state file associated... The only
14651 // way to fix this is to make the Aborted condition not a VM state
14652 // but a bool flag: i.e., when a crash occurs, set it to true and
14653 // change the state to PoweredOff or Saved depending on the
14654 // saved state presence.
14655
14656 deleteSavedState = true;
14657 mData->mCurrentStateModified = TRUE;
14658 stsFlags |= SaveSTS_CurStateModified;
14659 }
14660
14661 if ( aMachineState == MachineState_Starting
14662 || aMachineState == MachineState_Restoring
14663 || aMachineState == MachineState_TeleportingIn
14664 )
14665 {
14666 /* set the current state modified flag to indicate that the current
14667 * state is no more identical to the state in the
14668 * current snapshot */
14669 if (!mData->mCurrentSnapshot.isNull())
14670 {
14671 mData->mCurrentStateModified = TRUE;
14672 stsFlags |= SaveSTS_CurStateModified;
14673 }
14674 }
14675
14676 if (deleteSavedState)
14677 {
14678 if (mRemoveSavedState)
14679 {
14680 Assert(!mSSData->strStateFilePath.isEmpty());
14681
14682 // it is safe to delete the saved state file if ...
14683 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14684 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14685 // ... none of the snapshots share the saved state file
14686 )
14687 RTFileDelete(mSSData->strStateFilePath.c_str());
14688 }
14689
14690 mSSData->strStateFilePath.setNull();
14691 stsFlags |= SaveSTS_StateFilePath;
14692 }
14693
14694 /* redirect to the underlying peer machine */
14695 mPeer->i_setMachineState(aMachineState);
14696
14697 if ( oldMachineState != MachineState_RestoringSnapshot
14698 && ( aMachineState == MachineState_PoweredOff
14699 || aMachineState == MachineState_Teleported
14700 || aMachineState == MachineState_Aborted
14701 || aMachineState == MachineState_Saved))
14702 {
14703 /* the machine has stopped execution
14704 * (or the saved state file was adopted) */
14705 stsFlags |= SaveSTS_StateTimeStamp;
14706 }
14707
14708 if ( ( oldMachineState == MachineState_PoweredOff
14709 || oldMachineState == MachineState_Aborted
14710 || oldMachineState == MachineState_Teleported
14711 )
14712 && aMachineState == MachineState_Saved)
14713 {
14714 /* the saved state file was adopted */
14715 Assert(!mSSData->strStateFilePath.isEmpty());
14716 stsFlags |= SaveSTS_StateFilePath;
14717 }
14718
14719#ifdef VBOX_WITH_GUEST_PROPS
14720 if ( aMachineState == MachineState_PoweredOff
14721 || aMachineState == MachineState_Aborted
14722 || aMachineState == MachineState_Teleported)
14723 {
14724 /* Make sure any transient guest properties get removed from the
14725 * property store on shutdown. */
14726 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14727
14728 /* remove it from the settings representation */
14729 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14730 for (settings::GuestPropertiesList::iterator
14731 it = llGuestProperties.begin();
14732 it != llGuestProperties.end();
14733 /*nothing*/)
14734 {
14735 const settings::GuestProperty &prop = *it;
14736 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14737 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14738 {
14739 it = llGuestProperties.erase(it);
14740 fNeedsSaving = true;
14741 }
14742 else
14743 {
14744 ++it;
14745 }
14746 }
14747
14748 /* Additionally remove it from the HWData representation. Required to
14749 * keep everything in sync, as this is what the API keeps using. */
14750 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14751 for (HWData::GuestPropertyMap::iterator
14752 it = llHWGuestProperties.begin();
14753 it != llHWGuestProperties.end();
14754 /*nothing*/)
14755 {
14756 uint32_t fFlags = it->second.mFlags;
14757 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14758 {
14759 /* iterator where we need to continue after the erase call
14760 * (C++03 is a fact still, and it doesn't return the iterator
14761 * which would allow continuing) */
14762 HWData::GuestPropertyMap::iterator it2 = it;
14763 ++it2;
14764 llHWGuestProperties.erase(it);
14765 it = it2;
14766 fNeedsSaving = true;
14767 }
14768 else
14769 {
14770 ++it;
14771 }
14772 }
14773
14774 if (fNeedsSaving)
14775 {
14776 mData->mCurrentStateModified = TRUE;
14777 stsFlags |= SaveSTS_CurStateModified;
14778 }
14779 }
14780#endif /* VBOX_WITH_GUEST_PROPS */
14781
14782 rc = i_saveStateSettings(stsFlags);
14783
14784 if ( ( oldMachineState != MachineState_PoweredOff
14785 && oldMachineState != MachineState_Aborted
14786 && oldMachineState != MachineState_Teleported
14787 )
14788 && ( aMachineState == MachineState_PoweredOff
14789 || aMachineState == MachineState_Aborted
14790 || aMachineState == MachineState_Teleported
14791 )
14792 )
14793 {
14794 /* we've been shut down for any reason */
14795 /* no special action so far */
14796 }
14797
14798 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14799 LogFlowThisFuncLeave();
14800 return rc;
14801}
14802
14803/**
14804 * Sends the current machine state value to the VM process.
14805 *
14806 * @note Locks this object for reading, then calls a client process.
14807 */
14808HRESULT SessionMachine::i_updateMachineStateOnClient()
14809{
14810 AutoCaller autoCaller(this);
14811 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14812
14813 ComPtr<IInternalSessionControl> directControl;
14814 {
14815 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14816 AssertReturn(!!mData, E_FAIL);
14817 if (mData->mSession.mLockType == LockType_VM)
14818 directControl = mData->mSession.mDirectControl;
14819
14820 /* directControl may be already set to NULL here in #OnSessionEnd()
14821 * called too early by the direct session process while there is still
14822 * some operation (like deleting the snapshot) in progress. The client
14823 * process in this case is waiting inside Session::close() for the
14824 * "end session" process object to complete, while #uninit() called by
14825 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14826 * operation to complete. For now, we accept this inconsistent behavior
14827 * and simply do nothing here. */
14828
14829 if (mData->mSession.mState == SessionState_Unlocking)
14830 return S_OK;
14831 }
14832
14833 /* ignore notifications sent after #OnSessionEnd() is called */
14834 if (!directControl)
14835 return S_OK;
14836
14837 return directControl->UpdateMachineState(mData->mMachineState);
14838}
14839
14840
14841/*static*/
14842HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14843{
14844 va_list args;
14845 va_start(args, pcszMsg);
14846 HRESULT rc = setErrorInternal(aResultCode,
14847 getStaticClassIID(),
14848 getStaticComponentName(),
14849 Utf8Str(pcszMsg, args),
14850 false /* aWarning */,
14851 true /* aLogIt */);
14852 va_end(args);
14853 return rc;
14854}
14855
14856
14857HRESULT Machine::updateState(MachineState_T aState)
14858{
14859 NOREF(aState);
14860 ReturnComNotImplemented();
14861}
14862
14863HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14864{
14865 NOREF(aProgress);
14866 ReturnComNotImplemented();
14867}
14868
14869HRESULT Machine::endPowerUp(LONG aResult)
14870{
14871 NOREF(aResult);
14872 ReturnComNotImplemented();
14873}
14874
14875HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14876{
14877 NOREF(aProgress);
14878 ReturnComNotImplemented();
14879}
14880
14881HRESULT Machine::endPoweringDown(LONG aResult,
14882 const com::Utf8Str &aErrMsg)
14883{
14884 NOREF(aResult);
14885 NOREF(aErrMsg);
14886 ReturnComNotImplemented();
14887}
14888
14889HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14890 BOOL *aMatched,
14891 ULONG *aMaskedInterfaces)
14892{
14893 NOREF(aDevice);
14894 NOREF(aMatched);
14895 NOREF(aMaskedInterfaces);
14896 ReturnComNotImplemented();
14897
14898}
14899
14900HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14901{
14902 NOREF(aId); NOREF(aCaptureFilename);
14903 ReturnComNotImplemented();
14904}
14905
14906HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14907 BOOL aDone)
14908{
14909 NOREF(aId);
14910 NOREF(aDone);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::autoCaptureUSBDevices()
14915{
14916 ReturnComNotImplemented();
14917}
14918
14919HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14920{
14921 NOREF(aDone);
14922 ReturnComNotImplemented();
14923}
14924
14925HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14926 ComPtr<IProgress> &aProgress)
14927{
14928 NOREF(aSession);
14929 NOREF(aProgress);
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::finishOnlineMergeMedium()
14934{
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14939{
14940 RT_NOREF(aParms, aID);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14945{
14946 RT_NOREF(aID);
14947 ReturnComNotImplemented();
14948}
14949
14950HRESULT Machine::clipboardAreaAttach(ULONG aID)
14951{
14952 RT_NOREF(aID);
14953 ReturnComNotImplemented();
14954}
14955HRESULT Machine::clipboardAreaDetach(ULONG aID)
14956{
14957 RT_NOREF(aID);
14958 ReturnComNotImplemented();
14959}
14960
14961HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14962{
14963 RT_NOREF(aID);
14964 ReturnComNotImplemented();
14965}
14966
14967HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14968{
14969 RT_NOREF(aID, aRefCount);
14970 ReturnComNotImplemented();
14971}
14972
14973HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14974 std::vector<com::Utf8Str> &aValues,
14975 std::vector<LONG64> &aTimestamps,
14976 std::vector<com::Utf8Str> &aFlags)
14977{
14978 NOREF(aNames);
14979 NOREF(aValues);
14980 NOREF(aTimestamps);
14981 NOREF(aFlags);
14982 ReturnComNotImplemented();
14983}
14984
14985HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14986 const com::Utf8Str &aValue,
14987 LONG64 aTimestamp,
14988 const com::Utf8Str &aFlags)
14989{
14990 NOREF(aName);
14991 NOREF(aValue);
14992 NOREF(aTimestamp);
14993 NOREF(aFlags);
14994 ReturnComNotImplemented();
14995}
14996
14997HRESULT Machine::lockMedia()
14998{
14999 ReturnComNotImplemented();
15000}
15001
15002HRESULT Machine::unlockMedia()
15003{
15004 ReturnComNotImplemented();
15005}
15006
15007HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15008 ComPtr<IMediumAttachment> &aNewAttachment)
15009{
15010 NOREF(aAttachment);
15011 NOREF(aNewAttachment);
15012 ReturnComNotImplemented();
15013}
15014
15015HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15016 ULONG aCpuUser,
15017 ULONG aCpuKernel,
15018 ULONG aCpuIdle,
15019 ULONG aMemTotal,
15020 ULONG aMemFree,
15021 ULONG aMemBalloon,
15022 ULONG aMemShared,
15023 ULONG aMemCache,
15024 ULONG aPagedTotal,
15025 ULONG aMemAllocTotal,
15026 ULONG aMemFreeTotal,
15027 ULONG aMemBalloonTotal,
15028 ULONG aMemSharedTotal,
15029 ULONG aVmNetRx,
15030 ULONG aVmNetTx)
15031{
15032 NOREF(aValidStats);
15033 NOREF(aCpuUser);
15034 NOREF(aCpuKernel);
15035 NOREF(aCpuIdle);
15036 NOREF(aMemTotal);
15037 NOREF(aMemFree);
15038 NOREF(aMemBalloon);
15039 NOREF(aMemShared);
15040 NOREF(aMemCache);
15041 NOREF(aPagedTotal);
15042 NOREF(aMemAllocTotal);
15043 NOREF(aMemFreeTotal);
15044 NOREF(aMemBalloonTotal);
15045 NOREF(aMemSharedTotal);
15046 NOREF(aVmNetRx);
15047 NOREF(aVmNetTx);
15048 ReturnComNotImplemented();
15049}
15050
15051HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15052 com::Utf8Str &aResult)
15053{
15054 NOREF(aAuthParams);
15055 NOREF(aResult);
15056 ReturnComNotImplemented();
15057}
15058
15059com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15060{
15061 com::Utf8Str strControllerName = "Unknown";
15062 switch (aBusType)
15063 {
15064 case StorageBus_IDE:
15065 {
15066 strControllerName = "IDE";
15067 break;
15068 }
15069 case StorageBus_SATA:
15070 {
15071 strControllerName = "SATA";
15072 break;
15073 }
15074 case StorageBus_SCSI:
15075 {
15076 strControllerName = "SCSI";
15077 break;
15078 }
15079 case StorageBus_Floppy:
15080 {
15081 strControllerName = "Floppy";
15082 break;
15083 }
15084 case StorageBus_SAS:
15085 {
15086 strControllerName = "SAS";
15087 break;
15088 }
15089 case StorageBus_USB:
15090 {
15091 strControllerName = "USB";
15092 break;
15093 }
15094 default:
15095 break;
15096 }
15097 return strControllerName;
15098}
15099
15100HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15101{
15102 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15103
15104 AutoCaller autoCaller(this);
15105 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15106
15107 HRESULT rc = S_OK;
15108
15109 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15110 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15111 rc = getUSBDeviceFilters(usbDeviceFilters);
15112 if (FAILED(rc)) return rc;
15113
15114 NOREF(aFlags);
15115 com::Utf8Str osTypeId;
15116 ComObjPtr<GuestOSType> osType = NULL;
15117
15118 /* Get the guest os type as a string from the VB. */
15119 rc = getOSTypeId(osTypeId);
15120 if (FAILED(rc)) return rc;
15121
15122 /* Get the os type obj that coresponds, can be used to get
15123 * the defaults for this guest OS. */
15124 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15125 if (FAILED(rc)) return rc;
15126
15127 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15128
15129 /* Let the OS type select 64-bit ness. */
15130 mHWData->mLongMode = osType->i_is64Bit()
15131 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15132
15133 /* Let the OS type enable the X2APIC */
15134 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15135
15136 /* This one covers IOAPICEnabled. */
15137 mBIOSSettings->i_applyDefaults(osType);
15138
15139 /* Initialize default record settings. */
15140 mRecordingSettings->i_applyDefaults();
15141
15142 /* Initialize default BIOS settings here */
15143 /* Hardware virtualization must be ON by default */
15144 mHWData->mAPIC = true;
15145 mHWData->mHWVirtExEnabled = true;
15146
15147 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15148 if (FAILED(rc)) return rc;
15149
15150 /* Graphics stuff. */
15151 GraphicsControllerType_T graphicsController;
15152 rc = osType->COMGETTER(RecommendedGraphicsController)(&graphicsController);
15153 if (FAILED(rc)) return rc;
15154
15155 rc = mGraphicsAdapter->COMSETTER(GraphicsControllerType)(graphicsController);
15156 if (FAILED(rc)) return rc;
15157
15158 ULONG vramSize;
15159 rc = osType->COMGETTER(RecommendedVRAM)(&vramSize);
15160 if (FAILED(rc)) return rc;
15161
15162 rc = mGraphicsAdapter->COMSETTER(VRAMSize)(vramSize);
15163 if (FAILED(rc)) return rc;
15164
15165 BOOL fAccelerate2DVideoEnabled;
15166 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&fAccelerate2DVideoEnabled);
15167 if (FAILED(rc)) return rc;
15168
15169 rc = mGraphicsAdapter->COMSETTER(Accelerate2DVideoEnabled)(fAccelerate2DVideoEnabled);
15170 if (FAILED(rc)) return rc;
15171
15172 BOOL fAccelerate3DEnabled;
15173 rc = osType->COMGETTER(Recommended3DAcceleration)(&fAccelerate3DEnabled);
15174 if (FAILED(rc)) return rc;
15175
15176 rc = mGraphicsAdapter->COMSETTER(Accelerate3DEnabled)(fAccelerate3DEnabled);
15177 if (FAILED(rc)) return rc;
15178
15179 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15180 if (FAILED(rc)) return rc;
15181
15182 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15183 if (FAILED(rc)) return rc;
15184
15185 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15186 if (FAILED(rc)) return rc;
15187
15188 BOOL mRTCUseUTC;
15189 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15190 if (FAILED(rc)) return rc;
15191
15192 setRTCUseUTC(mRTCUseUTC);
15193 if (FAILED(rc)) return rc;
15194
15195 /* the setter does more than just the assignment, so use it */
15196 ChipsetType_T enmChipsetType;
15197 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15198 if (FAILED(rc)) return rc;
15199
15200 rc = COMSETTER(ChipsetType)(enmChipsetType);
15201 if (FAILED(rc)) return rc;
15202
15203 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15204 if (FAILED(rc)) return rc;
15205
15206 /* Apply network adapters defaults */
15207 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15208 mNetworkAdapters[slot]->i_applyDefaults(osType);
15209
15210 /* Apply serial port defaults */
15211 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15212 mSerialPorts[slot]->i_applyDefaults(osType);
15213
15214 /* Apply parallel port defaults - not OS dependent*/
15215 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15216 mParallelPorts[slot]->i_applyDefaults();
15217
15218 /* Audio stuff. */
15219 AudioControllerType_T audioController;
15220 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15221 if (FAILED(rc)) return rc;
15222
15223 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15224 if (FAILED(rc)) return rc;
15225
15226 AudioCodecType_T audioCodec;
15227 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15228 if (FAILED(rc)) return rc;
15229
15230 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15231 if (FAILED(rc)) return rc;
15232
15233 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15234 if (FAILED(rc)) return rc;
15235
15236 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15237 if (FAILED(rc)) return rc;
15238
15239 /* Storage Controllers */
15240 StorageControllerType_T hdStorageControllerType;
15241 StorageBus_T hdStorageBusType;
15242 StorageControllerType_T dvdStorageControllerType;
15243 StorageBus_T dvdStorageBusType;
15244 BOOL recommendedFloppy;
15245 ComPtr<IStorageController> floppyController;
15246 ComPtr<IStorageController> hdController;
15247 ComPtr<IStorageController> dvdController;
15248 Utf8Str strFloppyName, strDVDName, strHDName;
15249
15250 /* GUI auto generates controller names using bus type. Do the same*/
15251 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15252
15253 /* Floppy recommended? add one. */
15254 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15255 if (FAILED(rc)) return rc;
15256 if (recommendedFloppy)
15257 {
15258 rc = addStorageController(strFloppyName,
15259 StorageBus_Floppy,
15260 floppyController);
15261 if (FAILED(rc)) return rc;
15262 }
15263
15264 /* Setup one DVD storage controller. */
15265 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15266 if (FAILED(rc)) return rc;
15267
15268 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15269 if (FAILED(rc)) return rc;
15270
15271 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15272
15273 rc = addStorageController(strDVDName,
15274 dvdStorageBusType,
15275 dvdController);
15276 if (FAILED(rc)) return rc;
15277
15278 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15279 if (FAILED(rc)) return rc;
15280
15281 /* Setup one HDD storage controller. */
15282 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15283 if (FAILED(rc)) return rc;
15284
15285 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15286 if (FAILED(rc)) return rc;
15287
15288 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15289
15290 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15291 {
15292 rc = addStorageController(strHDName,
15293 hdStorageBusType,
15294 hdController);
15295 if (FAILED(rc)) return rc;
15296
15297 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15298 if (FAILED(rc)) return rc;
15299 }
15300 else
15301 {
15302 /* The HD controller is the same as DVD: */
15303 hdController = dvdController;
15304 }
15305
15306 /* Limit the AHCI port count if it's used because windows has trouble with
15307 * too many ports and other guest (OS X in particular) may take extra long
15308 * boot: */
15309
15310 // pParent = static_cast<Medium*>(aP)
15311 IStorageController *temp = hdController;
15312 ComObjPtr<StorageController> storageController;
15313 storageController = static_cast<StorageController *>(temp);
15314
15315 // tempHDController = aHDController;
15316 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15317 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15318 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15319 storageController->COMSETTER(PortCount)(1);
15320
15321 /* USB stuff */
15322
15323 bool ohciEnabled = false;
15324
15325 ComPtr<IUSBController> usbController;
15326 BOOL recommendedUSB3;
15327 BOOL recommendedUSB;
15328 BOOL usbProxyAvailable;
15329
15330 getUSBProxyAvailable(&usbProxyAvailable);
15331 if (FAILED(rc)) return rc;
15332
15333 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15334 if (FAILED(rc)) return rc;
15335 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15336 if (FAILED(rc)) return rc;
15337
15338 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15339 {
15340#ifdef VBOX_WITH_EXTPACK
15341 /* USB 3.0 is only available if the proper ExtPack is installed. */
15342 ExtPackManager *aManager = mParent->i_getExtPackManager();
15343 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15344 {
15345 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15346 if (FAILED(rc)) return rc;
15347
15348 /* xHci includes OHCI */
15349 ohciEnabled = true;
15350 }
15351#endif
15352 }
15353 if ( !ohciEnabled
15354 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15355 {
15356 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15357 if (FAILED(rc)) return rc;
15358 ohciEnabled = true;
15359
15360#ifdef VBOX_WITH_EXTPACK
15361 /* USB 2.0 is only available if the proper ExtPack is installed.
15362 * Note. Configuring EHCI here and providing messages about
15363 * the missing extpack isn't exactly clean, but it is a
15364 * necessary evil to patch over legacy compatability issues
15365 * introduced by the new distribution model. */
15366 ExtPackManager *manager = mParent->i_getExtPackManager();
15367 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15368 {
15369 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15370 if (FAILED(rc)) return rc;
15371 }
15372#endif
15373 }
15374
15375 /* Set recommended human interface device types: */
15376 BOOL recommendedUSBHID;
15377 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15378 if (FAILED(rc)) return rc;
15379
15380 if (recommendedUSBHID)
15381 {
15382 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15383 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15384 if (!ohciEnabled && !usbDeviceFilters.isNull())
15385 {
15386 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15387 if (FAILED(rc)) return rc;
15388 }
15389 }
15390
15391 BOOL recommendedUSBTablet;
15392 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15393 if (FAILED(rc)) return rc;
15394
15395 if (recommendedUSBTablet)
15396 {
15397 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15398 if (!ohciEnabled && !usbDeviceFilters.isNull())
15399 {
15400 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15401 if (FAILED(rc)) return rc;
15402 }
15403 }
15404 return S_OK;
15405}
15406
15407/* This isn't handled entirely by the wrapper generator yet. */
15408#ifdef VBOX_WITH_XPCOM
15409NS_DECL_CLASSINFO(SessionMachine)
15410NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15411
15412NS_DECL_CLASSINFO(SnapshotMachine)
15413NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15414#endif
Note: See TracBrowser for help on using the repository browser.

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