VirtualBox

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

Last change on this file since 80849 was 80849, checked in by vboxsync, 5 years ago

Shared Clipboard/URI: Renamed VBOX_WITH_SHARED_CLIPBOARD_URI_LIST -> VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 536.0 KB
Line 
1/* $Id: MachineImpl.cpp 80849 2019-09-17 09:46:51Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#define LOG_GROUP LOG_GROUP_MAIN_MACHINE
19
20/* Make sure all the stdint.h macros are included - must come first! */
21#ifndef __STDC_LIMIT_MACROS
22# define __STDC_LIMIT_MACROS
23#endif
24#ifndef __STDC_CONSTANT_MACROS
25# define __STDC_CONSTANT_MACROS
26#endif
27
28#include "LoggingNew.h"
29#include "VirtualBoxImpl.h"
30#include "MachineImpl.h"
31#include "ClientToken.h"
32#include "ProgressImpl.h"
33#include "ProgressProxyImpl.h"
34#include "MediumAttachmentImpl.h"
35#include "MediumImpl.h"
36#include "MediumLock.h"
37#include "USBControllerImpl.h"
38#include "USBDeviceFiltersImpl.h"
39#include "HostImpl.h"
40#include "SharedFolderImpl.h"
41#include "GuestOSTypeImpl.h"
42#include "VirtualBoxErrorInfoImpl.h"
43#include "StorageControllerImpl.h"
44#include "DisplayImpl.h"
45#include "DisplayUtils.h"
46#include "MachineImplCloneVM.h"
47#include "AutostartDb.h"
48#include "SystemPropertiesImpl.h"
49#include "MachineImplMoveVM.h"
50#include "ExtPackManagerImpl.h"
51#include "MachineLaunchVMCommonWorker.h"
52
53// generated header
54#include "VBoxEvents.h"
55
56#ifdef VBOX_WITH_USB
57# include "USBProxyService.h"
58#endif
59
60#include "AutoCaller.h"
61#include "HashedPw.h"
62#include "Performance.h"
63
64#include <iprt/asm.h>
65#include <iprt/path.h>
66#include <iprt/dir.h>
67#include <iprt/env.h>
68#include <iprt/lockvalidator.h>
69#include <iprt/process.h>
70#include <iprt/cpp/utils.h>
71#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
72#include <iprt/sha.h>
73#include <iprt/string.h>
74#include <iprt/ctype.h>
75
76#include <VBox/com/array.h>
77#include <VBox/com/list.h>
78
79#include <VBox/err.h>
80#include <VBox/param.h>
81#include <VBox/settings.h>
82#include <VBox/VMMDev.h>
83#include <VBox/vmm/ssm.h>
84
85#ifdef VBOX_WITH_GUEST_PROPS
86# include <VBox/HostServices/GuestPropertySvc.h>
87# include <VBox/com/array.h>
88#endif
89
90#include "VBox/com/MultiResult.h"
91
92#include <algorithm>
93
94#ifdef VBOX_WITH_DTRACE_R3_MAIN
95# include "dtrace/VBoxAPI.h"
96#endif
97
98#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
99# define HOSTSUFF_EXE ".exe"
100#else /* !RT_OS_WINDOWS */
101# define HOSTSUFF_EXE ""
102#endif /* !RT_OS_WINDOWS */
103
104// defines / prototypes
105/////////////////////////////////////////////////////////////////////////////
106
107/////////////////////////////////////////////////////////////////////////////
108// Machine::Data structure
109/////////////////////////////////////////////////////////////////////////////
110
111Machine::Data::Data()
112{
113 mRegistered = FALSE;
114 pMachineConfigFile = NULL;
115 /* Contains hints on what has changed when the user is using the VM (config
116 * changes, running the VM, ...). This is used to decide if a config needs
117 * to be written to disk. */
118 flModifications = 0;
119 /* VM modification usually also trigger setting the current state to
120 * "Modified". Although this is not always the case. An e.g. is the VM
121 * initialization phase or when snapshot related data is changed. The
122 * actually behavior is controlled by the following flag. */
123 m_fAllowStateModification = false;
124 mAccessible = FALSE;
125 /* mUuid is initialized in Machine::init() */
126
127 mMachineState = MachineState_PoweredOff;
128 RTTimeNow(&mLastStateChange);
129
130 mMachineStateDeps = 0;
131 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
132 mMachineStateChangePending = 0;
133
134 mCurrentStateModified = TRUE;
135 mGuestPropertiesModified = FALSE;
136
137 mSession.mPID = NIL_RTPROCESS;
138 mSession.mLockType = LockType_Null;
139 mSession.mState = SessionState_Unlocked;
140}
141
142Machine::Data::~Data()
143{
144 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
145 {
146 RTSemEventMultiDestroy(mMachineStateDepsSem);
147 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
148 }
149 if (pMachineConfigFile)
150 {
151 delete pMachineConfigFile;
152 pMachineConfigFile = NULL;
153 }
154}
155
156/////////////////////////////////////////////////////////////////////////////
157// Machine::HWData structure
158/////////////////////////////////////////////////////////////////////////////
159
160Machine::HWData::HWData()
161{
162 /* default values for a newly created machine */
163 mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
164 mMemorySize = 128;
165 mCPUCount = 1;
166 mCPUHotPlugEnabled = false;
167 mMemoryBalloonSize = 0;
168 mPageFusionEnabled = false;
169 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
170 mVRAMSize = 8;
171 mAccelerate3DEnabled = false;
172 mAccelerate2DVideoEnabled = false;
173 mMonitorCount = 1;
174 mHWVirtExEnabled = true;
175 mHWVirtExNestedPagingEnabled = true;
176#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
177 mHWVirtExLargePagesEnabled = true;
178#else
179 /* Not supported on 32 bits hosts. */
180 mHWVirtExLargePagesEnabled = false;
181#endif
182 mHWVirtExVPIDEnabled = true;
183 mHWVirtExUXEnabled = true;
184 mHWVirtExForceEnabled = false;
185 mHWVirtExUseNativeApi = false;
186#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
187 mPAEEnabled = true;
188#else
189 mPAEEnabled = false;
190#endif
191 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
192 mTripleFaultReset = false;
193 mAPIC = true;
194 mX2APIC = false;
195 mIBPBOnVMExit = false;
196 mIBPBOnVMEntry = false;
197 mSpecCtrl = false;
198 mSpecCtrlByHost = false;
199 mL1DFlushOnSched = true;
200 mL1DFlushOnVMEntry = false;
201 mMDSClearOnSched = true;
202 mMDSClearOnVMEntry = false;
203 mNestedHWVirt = false;
204 mHPETEnabled = false;
205 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
206 mCpuIdPortabilityLevel = 0;
207 mCpuProfile = "host";
208
209 /* default boot order: floppy - DVD - HDD */
210 mBootOrder[0] = DeviceType_Floppy;
211 mBootOrder[1] = DeviceType_DVD;
212 mBootOrder[2] = DeviceType_HardDisk;
213 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
214 mBootOrder[i] = DeviceType_Null;
215
216 mClipboardMode = ClipboardMode_Disabled;
217 mDnDMode = DnDMode_Disabled;
218
219 mFirmwareType = FirmwareType_BIOS;
220 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
221 mPointingHIDType = PointingHIDType_PS2Mouse;
222 mChipsetType = ChipsetType_PIIX3;
223 mParavirtProvider = ParavirtProvider_Default;
224 mEmulatedUSBCardReaderEnabled = FALSE;
225
226 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
227 mCPUAttached[i] = false;
228
229 mIOCacheEnabled = true;
230 mIOCacheSize = 5; /* 5MB */
231}
232
233Machine::HWData::~HWData()
234{
235}
236
237/////////////////////////////////////////////////////////////////////////////
238// Machine class
239/////////////////////////////////////////////////////////////////////////////
240
241// constructor / destructor
242/////////////////////////////////////////////////////////////////////////////
243
244Machine::Machine() :
245#ifdef VBOX_WITH_RESOURCE_USAGE_API
246 mCollectorGuest(NULL),
247#endif
248 mPeer(NULL),
249 mParent(NULL),
250 mSerialPorts(),
251 mParallelPorts(),
252 uRegistryNeedsSaving(0)
253{}
254
255Machine::~Machine()
256{}
257
258HRESULT Machine::FinalConstruct()
259{
260 LogFlowThisFunc(("\n"));
261 return BaseFinalConstruct();
262}
263
264void Machine::FinalRelease()
265{
266 LogFlowThisFunc(("\n"));
267 uninit();
268 BaseFinalRelease();
269}
270
271/**
272 * Initializes a new machine instance; this init() variant creates a new, empty machine.
273 * This gets called from VirtualBox::CreateMachine().
274 *
275 * @param aParent Associated parent object
276 * @param strConfigFile Local file system path to the VM settings file (can
277 * be relative to the VirtualBox config directory).
278 * @param strName name for the machine
279 * @param llGroups list of groups for the machine
280 * @param strOsType OS Type string (stored as is if aOsType is NULL).
281 * @param aOsType OS Type of this machine or NULL.
282 * @param aId UUID for the new machine.
283 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
284 * @param fDirectoryIncludesUUID Whether the use a special VM directory naming
285 * scheme (includes the UUID).
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 const Utf8Str &strOsType,
294 GuestOSType *aOsType,
295 const Guid &aId,
296 bool fForceOverwrite,
297 bool fDirectoryIncludesUUID)
298{
299 LogFlowThisFuncEnter();
300 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
301
302 /* Enclose the state transition NotReady->InInit->Ready */
303 AutoInitSpan autoInitSpan(this);
304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
305
306 HRESULT rc = initImpl(aParent, strConfigFile);
307 if (FAILED(rc)) return rc;
308
309 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
310 if (FAILED(rc)) return rc;
311
312 if (SUCCEEDED(rc))
313 {
314 // create an empty machine config
315 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
316
317 rc = initDataAndChildObjects();
318 }
319
320 if (SUCCEEDED(rc))
321 {
322 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
323 mData->mAccessible = TRUE;
324
325 unconst(mData->mUuid) = aId;
326
327 mUserData->s.strName = strName;
328
329 if (llGroups.size())
330 mUserData->s.llGroups = llGroups;
331
332 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
333 // the "name sync" flag determines whether the machine directory gets renamed along
334 // with the machine file; say so if the settings file name is the same as the
335 // settings file parent directory (machine directory)
336 mUserData->s.fNameSync = i_isInOwnDir();
337
338 // initialize the default snapshots folder
339 rc = COMSETTER(SnapshotFolder)(NULL);
340 AssertComRC(rc);
341
342 if (aOsType)
343 {
344 /* Store OS type */
345 mUserData->s.strOsType = aOsType->i_id();
346
347 /* Let the OS type select 64-bit ness. */
348 mHWData->mLongMode = aOsType->i_is64Bit()
349 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
350
351 /* Let the OS type enable the X2APIC */
352 mHWData->mX2APIC = aOsType->i_recommendedX2APIC();
353 }
354 else if (!strOsType.isEmpty())
355 {
356 /* Store OS type */
357 mUserData->s.strOsType = strOsType;
358
359 /* No guest OS type object. Pick some plausible defaults which the
360 * host can handle. There's no way to know or validate anything. */
361 mHWData->mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
362 mHWData->mX2APIC = false;
363 }
364
365 /* Apply BIOS defaults. */
366 mBIOSSettings->i_applyDefaults(aOsType);
367
368 /* Apply record defaults. */
369 mRecordingSettings->i_applyDefaults();
370
371 /* Apply network adapters defaults */
372 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
373 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
374
375 /* Apply serial port defaults */
376 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
377 mSerialPorts[slot]->i_applyDefaults(aOsType);
378
379 /* Apply parallel port defaults */
380 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
381 mParallelPorts[slot]->i_applyDefaults();
382
383 /* At this point the changing of the current state modification
384 * flag is allowed. */
385 i_allowStateModification();
386
387 /* commit all changes made during the initialization */
388 i_commit();
389 }
390
391 /* Confirm a successful initialization when it's the case */
392 if (SUCCEEDED(rc))
393 {
394 if (mData->mAccessible)
395 autoInitSpan.setSucceeded();
396 else
397 autoInitSpan.setLimited();
398 }
399
400 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
401 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
402 mData->mRegistered,
403 mData->mAccessible,
404 rc));
405
406 LogFlowThisFuncLeave();
407
408 return rc;
409}
410
411/**
412 * Initializes a new instance with data from machine XML (formerly Init_Registered).
413 * Gets called in two modes:
414 *
415 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
416 * UUID is specified and we mark the machine as "registered";
417 *
418 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
419 * and the machine remains unregistered until RegisterMachine() is called.
420 *
421 * @param aParent Associated parent object
422 * @param strConfigFile Local file system path to the VM settings file (can
423 * be relative to the VirtualBox config directory).
424 * @param aId UUID of the machine or NULL (see above).
425 *
426 * @return Success indicator. if not S_OK, the machine object is invalid
427 */
428HRESULT Machine::initFromSettings(VirtualBox *aParent,
429 const Utf8Str &strConfigFile,
430 const Guid *aId)
431{
432 LogFlowThisFuncEnter();
433 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
434
435 /* Enclose the state transition NotReady->InInit->Ready */
436 AutoInitSpan autoInitSpan(this);
437 AssertReturn(autoInitSpan.isOk(), E_FAIL);
438
439 HRESULT rc = initImpl(aParent, strConfigFile);
440 if (FAILED(rc)) return rc;
441
442 if (aId)
443 {
444 // loading a registered VM:
445 unconst(mData->mUuid) = *aId;
446 mData->mRegistered = TRUE;
447 // now load the settings from XML:
448 rc = i_registeredInit();
449 // this calls initDataAndChildObjects() and loadSettings()
450 }
451 else
452 {
453 // opening an unregistered VM (VirtualBox::OpenMachine()):
454 rc = initDataAndChildObjects();
455
456 if (SUCCEEDED(rc))
457 {
458 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
459 mData->mAccessible = TRUE;
460
461 try
462 {
463 // load and parse machine XML; this will throw on XML or logic errors
464 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
465
466 // reject VM UUID duplicates, they can happen if someone
467 // tries to register an already known VM config again
468 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
469 true /* fPermitInaccessible */,
470 false /* aDoSetError */,
471 NULL) != VBOX_E_OBJECT_NOT_FOUND)
472 {
473 throw setError(E_FAIL,
474 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
475 mData->m_strConfigFile.c_str());
476 }
477
478 // use UUID from machine config
479 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
480
481 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
482 NULL /* puuidRegistry */);
483 if (FAILED(rc)) throw rc;
484
485 /* At this point the changing of the current state modification
486 * flag is allowed. */
487 i_allowStateModification();
488
489 i_commit();
490 }
491 catch (HRESULT err)
492 {
493 /* we assume that error info is set by the thrower */
494 rc = err;
495 }
496 catch (...)
497 {
498 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
499 }
500 }
501 }
502
503 /* Confirm a successful initialization when it's the case */
504 if (SUCCEEDED(rc))
505 {
506 if (mData->mAccessible)
507 autoInitSpan.setSucceeded();
508 else
509 {
510 autoInitSpan.setLimited();
511
512 // uninit media from this machine's media registry, or else
513 // reloading the settings will fail
514 mParent->i_unregisterMachineMedia(i_getId());
515 }
516 }
517
518 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
519 "rc=%08X\n",
520 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
521 mData->mRegistered, mData->mAccessible, rc));
522
523 LogFlowThisFuncLeave();
524
525 return rc;
526}
527
528/**
529 * Initializes a new instance from a machine config that is already in memory
530 * (import OVF case). Since we are importing, the UUID in the machine
531 * config is ignored and we always generate a fresh one.
532 *
533 * @param aParent Associated parent object.
534 * @param strName Name for the new machine; this overrides what is specified in config.
535 * @param strSettingsFilename File name of .vbox file.
536 * @param config Machine configuration loaded and parsed from XML.
537 *
538 * @return Success indicator. if not S_OK, the machine object is invalid
539 */
540HRESULT Machine::init(VirtualBox *aParent,
541 const Utf8Str &strName,
542 const Utf8Str &strSettingsFilename,
543 const settings::MachineConfigFile &config)
544{
545 LogFlowThisFuncEnter();
546
547 /* Enclose the state transition NotReady->InInit->Ready */
548 AutoInitSpan autoInitSpan(this);
549 AssertReturn(autoInitSpan.isOk(), E_FAIL);
550
551 HRESULT rc = initImpl(aParent, strSettingsFilename);
552 if (FAILED(rc)) return rc;
553
554 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
555 if (FAILED(rc)) return rc;
556
557 rc = initDataAndChildObjects();
558
559 if (SUCCEEDED(rc))
560 {
561 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
562 mData->mAccessible = TRUE;
563
564 // create empty machine config for instance data
565 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
566
567 // generate fresh UUID, ignore machine config
568 unconst(mData->mUuid).create();
569
570 rc = i_loadMachineDataFromSettings(config,
571 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
572
573 // override VM name as well, it may be different
574 mUserData->s.strName = strName;
575
576 if (SUCCEEDED(rc))
577 {
578 /* At this point the changing of the current state modification
579 * flag is allowed. */
580 i_allowStateModification();
581
582 /* commit all changes made during the initialization */
583 i_commit();
584 }
585 }
586
587 /* Confirm a successful initialization when it's the case */
588 if (SUCCEEDED(rc))
589 {
590 if (mData->mAccessible)
591 autoInitSpan.setSucceeded();
592 else
593 {
594 /* Ignore all errors from unregistering, they would destroy
595- * the more interesting error information we already have,
596- * pinpointing the issue with the VM config. */
597 ErrorInfoKeeper eik;
598
599 autoInitSpan.setLimited();
600
601 // uninit media from this machine's media registry, or else
602 // reloading the settings will fail
603 mParent->i_unregisterMachineMedia(i_getId());
604 }
605 }
606
607 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
608 "rc=%08X\n",
609 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
610 mData->mRegistered, mData->mAccessible, rc));
611
612 LogFlowThisFuncLeave();
613
614 return rc;
615}
616
617/**
618 * Shared code between the various init() implementations.
619 * @param aParent The VirtualBox object.
620 * @param strConfigFile Settings file.
621 * @return
622 */
623HRESULT Machine::initImpl(VirtualBox *aParent,
624 const Utf8Str &strConfigFile)
625{
626 LogFlowThisFuncEnter();
627
628 AssertReturn(aParent, E_INVALIDARG);
629 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
630
631 HRESULT rc = S_OK;
632
633 /* share the parent weakly */
634 unconst(mParent) = aParent;
635
636 /* allocate the essential machine data structure (the rest will be
637 * allocated later by initDataAndChildObjects() */
638 mData.allocate();
639
640 /* memorize the config file name (as provided) */
641 mData->m_strConfigFile = strConfigFile;
642
643 /* get the full file name */
644 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
645 if (RT_FAILURE(vrc1))
646 return setErrorBoth(VBOX_E_FILE_ERROR, vrc1,
647 tr("Invalid machine settings file name '%s' (%Rrc)"),
648 strConfigFile.c_str(),
649 vrc1);
650
651 LogFlowThisFuncLeave();
652
653 return rc;
654}
655
656/**
657 * Tries to create a machine settings file in the path stored in the machine
658 * instance data. Used when a new machine is created to fail gracefully if
659 * the settings file could not be written (e.g. because machine dir is read-only).
660 * @return
661 */
662HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
663{
664 HRESULT rc = S_OK;
665
666 // when we create a new machine, we must be able to create the settings file
667 RTFILE f = NIL_RTFILE;
668 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
669 if ( RT_SUCCESS(vrc)
670 || vrc == VERR_SHARING_VIOLATION
671 )
672 {
673 if (RT_SUCCESS(vrc))
674 RTFileClose(f);
675 if (!fForceOverwrite)
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Machine settings file '%s' already exists"),
678 mData->m_strConfigFileFull.c_str());
679 else
680 {
681 /* try to delete the config file, as otherwise the creation
682 * of a new settings file will fail. */
683 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
684 if (RT_FAILURE(vrc2))
685 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc2,
686 tr("Could not delete the existing settings file '%s' (%Rrc)"),
687 mData->m_strConfigFileFull.c_str(), vrc2);
688 }
689 }
690 else if ( vrc != VERR_FILE_NOT_FOUND
691 && vrc != VERR_PATH_NOT_FOUND
692 )
693 rc = setErrorBoth(VBOX_E_FILE_ERROR, vrc,
694 tr("Invalid machine settings file name '%s' (%Rrc)"),
695 mData->m_strConfigFileFull.c_str(),
696 vrc);
697 return rc;
698}
699
700/**
701 * Initializes the registered machine by loading the settings file.
702 * This method is separated from #init() in order to make it possible to
703 * retry the operation after VirtualBox startup instead of refusing to
704 * startup the whole VirtualBox server in case if the settings file of some
705 * registered VM is invalid or inaccessible.
706 *
707 * @note Must be always called from this object's write lock
708 * (unless called from #init() that doesn't need any locking).
709 * @note Locks the mUSBController method for writing.
710 * @note Subclasses must not call this method.
711 */
712HRESULT Machine::i_registeredInit()
713{
714 AssertReturn(!i_isSessionMachine(), E_FAIL);
715 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
716 AssertReturn(mData->mUuid.isValid(), E_FAIL);
717 AssertReturn(!mData->mAccessible, E_FAIL);
718
719 HRESULT rc = initDataAndChildObjects();
720
721 if (SUCCEEDED(rc))
722 {
723 /* Temporarily reset the registered flag in order to let setters
724 * potentially called from loadSettings() succeed (isMutable() used in
725 * all setters will return FALSE for a Machine instance if mRegistered
726 * is TRUE). */
727 mData->mRegistered = FALSE;
728
729 try
730 {
731 // load and parse machine XML; this will throw on XML or logic errors
732 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
733
734 if (mData->mUuid != mData->pMachineConfigFile->uuid)
735 throw setError(E_FAIL,
736 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
737 mData->pMachineConfigFile->uuid.raw(),
738 mData->m_strConfigFileFull.c_str(),
739 mData->mUuid.toString().c_str(),
740 mParent->i_settingsFilePath().c_str());
741
742 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
743 NULL /* const Guid *puuidRegistry */);
744 if (FAILED(rc)) throw rc;
745 }
746 catch (HRESULT err)
747 {
748 /* we assume that error info is set by the thrower */
749 rc = err;
750 }
751 catch (...)
752 {
753 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
754 }
755
756 /* Restore the registered flag (even on failure) */
757 mData->mRegistered = TRUE;
758 }
759
760 if (SUCCEEDED(rc))
761 {
762 /* Set mAccessible to TRUE only if we successfully locked and loaded
763 * the settings file */
764 mData->mAccessible = TRUE;
765
766 /* commit all changes made during loading the settings file */
767 i_commit(); /// @todo r=dj why do we need a commit during init?!? this is very expensive
768 /// @todo r=klaus for some reason the settings loading logic backs up
769 // the settings, and therefore a commit is needed. Should probably be changed.
770 }
771 else
772 {
773 /* If the machine is registered, then, instead of returning a
774 * failure, we mark it as inaccessible and set the result to
775 * success to give it a try later */
776
777 /* fetch the current error info */
778 mData->mAccessError = com::ErrorInfo();
779 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
780
781 /* rollback all changes */
782 i_rollback(false /* aNotify */);
783
784 // uninit media from this machine's media registry, or else
785 // reloading the settings will fail
786 mParent->i_unregisterMachineMedia(i_getId());
787
788 /* uninitialize the common part to make sure all data is reset to
789 * default (null) values */
790 uninitDataAndChildObjects();
791
792 rc = S_OK;
793 }
794
795 return rc;
796}
797
798/**
799 * Uninitializes the instance.
800 * Called either from FinalRelease() or by the parent when it gets destroyed.
801 *
802 * @note The caller of this method must make sure that this object
803 * a) doesn't have active callers on the current thread and b) is not locked
804 * by the current thread; otherwise uninit() will hang either a) due to
805 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
806 * a dead-lock caused by this thread waiting for all callers on the other
807 * threads are done but preventing them from doing so by holding a lock.
808 */
809void Machine::uninit()
810{
811 LogFlowThisFuncEnter();
812
813 Assert(!isWriteLockOnCurrentThread());
814
815 Assert(!uRegistryNeedsSaving);
816 if (uRegistryNeedsSaving)
817 {
818 AutoCaller autoCaller(this);
819 if (SUCCEEDED(autoCaller.rc()))
820 {
821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
822 i_saveSettings(NULL, Machine::SaveS_Force);
823 }
824 }
825
826 /* Enclose the state transition Ready->InUninit->NotReady */
827 AutoUninitSpan autoUninitSpan(this);
828 if (autoUninitSpan.uninitDone())
829 return;
830
831 Assert(!i_isSnapshotMachine());
832 Assert(!i_isSessionMachine());
833 Assert(!!mData);
834
835 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
836 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
837
838 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
839
840 if (!mData->mSession.mMachine.isNull())
841 {
842 /* Theoretically, this can only happen if the VirtualBox server has been
843 * terminated while there were clients running that owned open direct
844 * sessions. Since in this case we are definitely called by
845 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
846 * won't happen on the client watcher thread (because it has a
847 * VirtualBox caller for the duration of the
848 * SessionMachine::i_checkForDeath() call, so that VirtualBox::uninit()
849 * cannot happen until the VirtualBox caller is released). This is
850 * important, because SessionMachine::uninit() cannot correctly operate
851 * after we return from this method (it expects the Machine instance is
852 * still valid). We'll call it ourselves below.
853 */
854 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
855 (SessionMachine*)mData->mSession.mMachine));
856
857 if (Global::IsOnlineOrTransient(mData->mMachineState))
858 {
859 Log1WarningThisFunc(("Setting state to Aborted!\n"));
860 /* set machine state using SessionMachine reimplementation */
861 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
862 }
863
864 /*
865 * Uninitialize SessionMachine using public uninit() to indicate
866 * an unexpected uninitialization.
867 */
868 mData->mSession.mMachine->uninit();
869 /* SessionMachine::uninit() must set mSession.mMachine to null */
870 Assert(mData->mSession.mMachine.isNull());
871 }
872
873 // uninit media from this machine's media registry, if they're still there
874 Guid uuidMachine(i_getId());
875
876 /* the lock is no more necessary (SessionMachine is uninitialized) */
877 alock.release();
878
879 /* XXX This will fail with
880 * "cannot be closed because it is still attached to 1 virtual machines"
881 * because at this point we did not call uninitDataAndChildObjects() yet
882 * and therefore also removeBackReference() for all these mediums was not called! */
883
884 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
885 mParent->i_unregisterMachineMedia(uuidMachine);
886
887 // has machine been modified?
888 if (mData->flModifications)
889 {
890 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
891 i_rollback(false /* aNotify */);
892 }
893
894 if (mData->mAccessible)
895 uninitDataAndChildObjects();
896
897 /* free the essential data structure last */
898 mData.free();
899
900 LogFlowThisFuncLeave();
901}
902
903// Wrapped IMachine properties
904/////////////////////////////////////////////////////////////////////////////
905HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
906{
907 /* mParent is constant during life time, no need to lock */
908 ComObjPtr<VirtualBox> pVirtualBox(mParent);
909 aParent = pVirtualBox;
910
911 return S_OK;
912}
913
914
915HRESULT Machine::getAccessible(BOOL *aAccessible)
916{
917 /* In some cases (medium registry related), it is necessary to be able to
918 * go through the list of all machines. Happens when an inaccessible VM
919 * has a sensible medium registry. */
920 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
921 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
922
923 HRESULT rc = S_OK;
924
925 if (!mData->mAccessible)
926 {
927 /* try to initialize the VM once more if not accessible */
928
929 AutoReinitSpan autoReinitSpan(this);
930 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
931
932#ifdef DEBUG
933 LogFlowThisFunc(("Dumping media backreferences\n"));
934 mParent->i_dumpAllBackRefs();
935#endif
936
937 if (mData->pMachineConfigFile)
938 {
939 // reset the XML file to force loadSettings() (called from i_registeredInit())
940 // to parse it again; the file might have changed
941 delete mData->pMachineConfigFile;
942 mData->pMachineConfigFile = NULL;
943 }
944
945 rc = i_registeredInit();
946
947 if (SUCCEEDED(rc) && mData->mAccessible)
948 {
949 autoReinitSpan.setSucceeded();
950
951 /* make sure interesting parties will notice the accessibility
952 * state change */
953 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
954 mParent->i_onMachineDataChange(mData->mUuid);
955 }
956 }
957
958 if (SUCCEEDED(rc))
959 *aAccessible = mData->mAccessible;
960
961 LogFlowThisFuncLeave();
962
963 return rc;
964}
965
966HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
967{
968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
969
970 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
971 {
972 /* return shortly */
973 aAccessError = NULL;
974 return S_OK;
975 }
976
977 HRESULT rc = S_OK;
978
979 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
980 rc = errorInfo.createObject();
981 if (SUCCEEDED(rc))
982 {
983 errorInfo->init(mData->mAccessError.getResultCode(),
984 mData->mAccessError.getInterfaceID().ref(),
985 Utf8Str(mData->mAccessError.getComponent()).c_str(),
986 Utf8Str(mData->mAccessError.getText()));
987 aAccessError = errorInfo;
988 }
989
990 return rc;
991}
992
993HRESULT Machine::getName(com::Utf8Str &aName)
994{
995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
996
997 aName = mUserData->s.strName;
998
999 return S_OK;
1000}
1001
1002HRESULT Machine::setName(const com::Utf8Str &aName)
1003{
1004 // prohibit setting a UUID only as the machine name, or else it can
1005 // never be found by findMachine()
1006 Guid test(aName);
1007
1008 if (test.isValid())
1009 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
1010
1011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1012
1013 HRESULT rc = i_checkStateDependency(MutableStateDep);
1014 if (FAILED(rc)) return rc;
1015
1016 i_setModified(IsModified_MachineData);
1017 mUserData.backup();
1018 mUserData->s.strName = aName;
1019
1020 return S_OK;
1021}
1022
1023HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1024{
1025 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1026
1027 aDescription = mUserData->s.strDescription;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1033{
1034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 // this can be done in principle in any state as it doesn't affect the VM
1037 // significantly, but play safe by not messing around while complex
1038 // activities are going on
1039 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1040 if (FAILED(rc)) return rc;
1041
1042 i_setModified(IsModified_MachineData);
1043 mUserData.backup();
1044 mUserData->s.strDescription = aDescription;
1045
1046 return S_OK;
1047}
1048
1049HRESULT Machine::getId(com::Guid &aId)
1050{
1051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1052
1053 aId = mData->mUuid;
1054
1055 return S_OK;
1056}
1057
1058HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1059{
1060 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1061 aGroups.resize(mUserData->s.llGroups.size());
1062 size_t i = 0;
1063 for (StringsList::const_iterator
1064 it = mUserData->s.llGroups.begin();
1065 it != mUserData->s.llGroups.end();
1066 ++it, ++i)
1067 aGroups[i] = (*it);
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1073{
1074 StringsList llGroups;
1075 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1076 if (FAILED(rc))
1077 return rc;
1078
1079 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1080
1081 rc = i_checkStateDependency(MutableOrSavedStateDep);
1082 if (FAILED(rc)) return rc;
1083
1084 i_setModified(IsModified_MachineData);
1085 mUserData.backup();
1086 mUserData->s.llGroups = llGroups;
1087
1088 return S_OK;
1089}
1090
1091HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1092{
1093 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1094
1095 aOSTypeId = mUserData->s.strOsType;
1096
1097 return S_OK;
1098}
1099
1100HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1101{
1102 /* look up the object by Id to check it is valid */
1103 ComObjPtr<GuestOSType> pGuestOSType;
1104 mParent->i_findGuestOSType(aOSTypeId, pGuestOSType);
1105
1106 /* when setting, always use the "etalon" value for consistency -- lookup
1107 * by ID is case-insensitive and the input value may have different case */
1108 Utf8Str osTypeId = !pGuestOSType.isNull() ? pGuestOSType->i_id() : aOSTypeId;
1109
1110 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1111
1112 HRESULT rc = i_checkStateDependency(MutableStateDep);
1113 if (FAILED(rc)) return rc;
1114
1115 i_setModified(IsModified_MachineData);
1116 mUserData.backup();
1117 mUserData->s.strOsType = osTypeId;
1118
1119 return S_OK;
1120}
1121
1122HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1123{
1124 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1125
1126 *aFirmwareType = mHWData->mFirmwareType;
1127
1128 return S_OK;
1129}
1130
1131HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1132{
1133 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1134
1135 HRESULT rc = i_checkStateDependency(MutableStateDep);
1136 if (FAILED(rc)) return rc;
1137
1138 i_setModified(IsModified_MachineData);
1139 mHWData.backup();
1140 mHWData->mFirmwareType = aFirmwareType;
1141
1142 return S_OK;
1143}
1144
1145HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1146{
1147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1148
1149 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1150
1151 return S_OK;
1152}
1153
1154HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1155{
1156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1157
1158 HRESULT rc = i_checkStateDependency(MutableStateDep);
1159 if (FAILED(rc)) return rc;
1160
1161 i_setModified(IsModified_MachineData);
1162 mHWData.backup();
1163 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1164
1165 return S_OK;
1166}
1167
1168HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1169{
1170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1171
1172 *aPointingHIDType = mHWData->mPointingHIDType;
1173
1174 return S_OK;
1175}
1176
1177HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1178{
1179 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1180
1181 HRESULT rc = i_checkStateDependency(MutableStateDep);
1182 if (FAILED(rc)) return rc;
1183
1184 i_setModified(IsModified_MachineData);
1185 mHWData.backup();
1186 mHWData->mPointingHIDType = aPointingHIDType;
1187
1188 return S_OK;
1189}
1190
1191HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1192{
1193 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1194
1195 *aChipsetType = mHWData->mChipsetType;
1196
1197 return S_OK;
1198}
1199
1200HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1201{
1202 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1203
1204 HRESULT rc = i_checkStateDependency(MutableStateDep);
1205 if (FAILED(rc)) return rc;
1206
1207 if (aChipsetType != mHWData->mChipsetType)
1208 {
1209 i_setModified(IsModified_MachineData);
1210 mHWData.backup();
1211 mHWData->mChipsetType = aChipsetType;
1212
1213 // Resize network adapter array, to be finalized on commit/rollback.
1214 // We must not throw away entries yet, otherwise settings are lost
1215 // without a way to roll back.
1216 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1217 size_t oldCount = mNetworkAdapters.size();
1218 if (newCount > oldCount)
1219 {
1220 mNetworkAdapters.resize(newCount);
1221 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1222 {
1223 unconst(mNetworkAdapters[slot]).createObject();
1224 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1225 }
1226 }
1227 }
1228
1229 return S_OK;
1230}
1231
1232HRESULT Machine::getParavirtDebug(com::Utf8Str &aParavirtDebug)
1233{
1234 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1235
1236 aParavirtDebug = mHWData->mParavirtDebug;
1237 return S_OK;
1238}
1239
1240HRESULT Machine::setParavirtDebug(const com::Utf8Str &aParavirtDebug)
1241{
1242 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1243
1244 HRESULT rc = i_checkStateDependency(MutableStateDep);
1245 if (FAILED(rc)) return rc;
1246
1247 /** @todo Parse/validate options? */
1248 if (aParavirtDebug != mHWData->mParavirtDebug)
1249 {
1250 i_setModified(IsModified_MachineData);
1251 mHWData.backup();
1252 mHWData->mParavirtDebug = aParavirtDebug;
1253 }
1254
1255 return S_OK;
1256}
1257
1258HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1259{
1260 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1261
1262 *aParavirtProvider = mHWData->mParavirtProvider;
1263
1264 return S_OK;
1265}
1266
1267HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1268{
1269 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1270
1271 HRESULT rc = i_checkStateDependency(MutableStateDep);
1272 if (FAILED(rc)) return rc;
1273
1274 if (aParavirtProvider != mHWData->mParavirtProvider)
1275 {
1276 i_setModified(IsModified_MachineData);
1277 mHWData.backup();
1278 mHWData->mParavirtProvider = aParavirtProvider;
1279 }
1280
1281 return S_OK;
1282}
1283
1284HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1285{
1286 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1287
1288 *aParavirtProvider = mHWData->mParavirtProvider;
1289 switch (mHWData->mParavirtProvider)
1290 {
1291 case ParavirtProvider_None:
1292 case ParavirtProvider_HyperV:
1293 case ParavirtProvider_KVM:
1294 case ParavirtProvider_Minimal:
1295 break;
1296
1297 /* Resolve dynamic provider types to the effective types. */
1298 default:
1299 {
1300 ComObjPtr<GuestOSType> pGuestOSType;
1301 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1302 pGuestOSType);
1303 if (FAILED(hrc2) || pGuestOSType.isNull())
1304 {
1305 *aParavirtProvider = ParavirtProvider_None;
1306 break;
1307 }
1308
1309 Utf8Str guestTypeFamilyId = pGuestOSType->i_familyId();
1310 bool fOsXGuest = guestTypeFamilyId == "MacOS";
1311
1312 switch (mHWData->mParavirtProvider)
1313 {
1314 case ParavirtProvider_Legacy:
1315 {
1316 if (fOsXGuest)
1317 *aParavirtProvider = ParavirtProvider_Minimal;
1318 else
1319 *aParavirtProvider = ParavirtProvider_None;
1320 break;
1321 }
1322
1323 case ParavirtProvider_Default:
1324 {
1325 if (fOsXGuest)
1326 *aParavirtProvider = ParavirtProvider_Minimal;
1327 else if ( mUserData->s.strOsType == "Windows10"
1328 || mUserData->s.strOsType == "Windows10_64"
1329 || mUserData->s.strOsType == "Windows81"
1330 || mUserData->s.strOsType == "Windows81_64"
1331 || mUserData->s.strOsType == "Windows8"
1332 || mUserData->s.strOsType == "Windows8_64"
1333 || mUserData->s.strOsType == "Windows7"
1334 || mUserData->s.strOsType == "Windows7_64"
1335 || mUserData->s.strOsType == "WindowsVista"
1336 || mUserData->s.strOsType == "WindowsVista_64"
1337 || mUserData->s.strOsType == "Windows2012"
1338 || mUserData->s.strOsType == "Windows2012_64"
1339 || mUserData->s.strOsType == "Windows2008"
1340 || mUserData->s.strOsType == "Windows2008_64")
1341 {
1342 *aParavirtProvider = ParavirtProvider_HyperV;
1343 }
1344 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1345 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1346 || mUserData->s.strOsType == "Linux"
1347 || mUserData->s.strOsType == "Linux_64"
1348 || mUserData->s.strOsType == "ArchLinux"
1349 || mUserData->s.strOsType == "ArchLinux_64"
1350 || mUserData->s.strOsType == "Debian"
1351 || mUserData->s.strOsType == "Debian_64"
1352 || mUserData->s.strOsType == "Fedora"
1353 || mUserData->s.strOsType == "Fedora_64"
1354 || mUserData->s.strOsType == "Gentoo"
1355 || mUserData->s.strOsType == "Gentoo_64"
1356 || mUserData->s.strOsType == "Mandriva"
1357 || mUserData->s.strOsType == "Mandriva_64"
1358 || mUserData->s.strOsType == "OpenSUSE"
1359 || mUserData->s.strOsType == "OpenSUSE_64"
1360 || mUserData->s.strOsType == "Oracle"
1361 || mUserData->s.strOsType == "Oracle_64"
1362 || mUserData->s.strOsType == "RedHat"
1363 || mUserData->s.strOsType == "RedHat_64"
1364 || mUserData->s.strOsType == "Turbolinux"
1365 || mUserData->s.strOsType == "Turbolinux_64"
1366 || mUserData->s.strOsType == "Ubuntu"
1367 || mUserData->s.strOsType == "Ubuntu_64"
1368 || mUserData->s.strOsType == "Xandros"
1369 || mUserData->s.strOsType == "Xandros_64")
1370 {
1371 *aParavirtProvider = ParavirtProvider_KVM;
1372 }
1373 else
1374 *aParavirtProvider = ParavirtProvider_None;
1375 break;
1376 }
1377
1378 default: AssertFailedBreak(); /* Shut up MSC. */
1379 }
1380 break;
1381 }
1382 }
1383
1384 Assert( *aParavirtProvider == ParavirtProvider_None
1385 || *aParavirtProvider == ParavirtProvider_Minimal
1386 || *aParavirtProvider == ParavirtProvider_HyperV
1387 || *aParavirtProvider == ParavirtProvider_KVM);
1388 return S_OK;
1389}
1390
1391HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1392{
1393 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1394
1395 aHardwareVersion = mHWData->mHWVersion;
1396
1397 return S_OK;
1398}
1399
1400HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1401{
1402 /* check known version */
1403 Utf8Str hwVersion = aHardwareVersion;
1404 if ( hwVersion.compare("1") != 0
1405 && hwVersion.compare("2") != 0) // VBox 2.1.x and later (VMMDev heap)
1406 return setError(E_INVALIDARG,
1407 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1408
1409 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1410
1411 HRESULT rc = i_checkStateDependency(MutableStateDep);
1412 if (FAILED(rc)) return rc;
1413
1414 i_setModified(IsModified_MachineData);
1415 mHWData.backup();
1416 mHWData->mHWVersion = aHardwareVersion;
1417
1418 return S_OK;
1419}
1420
1421HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1422{
1423 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1424
1425 if (!mHWData->mHardwareUUID.isZero())
1426 aHardwareUUID = mHWData->mHardwareUUID;
1427 else
1428 aHardwareUUID = mData->mUuid;
1429
1430 return S_OK;
1431}
1432
1433HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1434{
1435 if (!aHardwareUUID.isValid())
1436 return E_INVALIDARG;
1437
1438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1439
1440 HRESULT rc = i_checkStateDependency(MutableStateDep);
1441 if (FAILED(rc)) return rc;
1442
1443 i_setModified(IsModified_MachineData);
1444 mHWData.backup();
1445 if (aHardwareUUID == mData->mUuid)
1446 mHWData->mHardwareUUID.clear();
1447 else
1448 mHWData->mHardwareUUID = aHardwareUUID;
1449
1450 return S_OK;
1451}
1452
1453HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1454{
1455 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1456
1457 *aMemorySize = mHWData->mMemorySize;
1458
1459 return S_OK;
1460}
1461
1462HRESULT Machine::setMemorySize(ULONG aMemorySize)
1463{
1464 /* check RAM limits */
1465 if ( aMemorySize < MM_RAM_MIN_IN_MB
1466 || aMemorySize > MM_RAM_MAX_IN_MB
1467 )
1468 return setError(E_INVALIDARG,
1469 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1470 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1471
1472 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1473
1474 HRESULT rc = i_checkStateDependency(MutableStateDep);
1475 if (FAILED(rc)) return rc;
1476
1477 i_setModified(IsModified_MachineData);
1478 mHWData.backup();
1479 mHWData->mMemorySize = aMemorySize;
1480
1481 return S_OK;
1482}
1483
1484HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1485{
1486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1487
1488 *aCPUCount = mHWData->mCPUCount;
1489
1490 return S_OK;
1491}
1492
1493HRESULT Machine::setCPUCount(ULONG aCPUCount)
1494{
1495 /* check CPU limits */
1496 if ( aCPUCount < SchemaDefs::MinCPUCount
1497 || aCPUCount > SchemaDefs::MaxCPUCount
1498 )
1499 return setError(E_INVALIDARG,
1500 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1501 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1502
1503 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1504
1505 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1506 if (mHWData->mCPUHotPlugEnabled)
1507 {
1508 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1509 {
1510 if (mHWData->mCPUAttached[idx])
1511 return setError(E_INVALIDARG,
1512 tr("There is still a CPU attached to socket %lu."
1513 "Detach the CPU before removing the socket"),
1514 aCPUCount, idx+1);
1515 }
1516 }
1517
1518 HRESULT rc = i_checkStateDependency(MutableStateDep);
1519 if (FAILED(rc)) return rc;
1520
1521 i_setModified(IsModified_MachineData);
1522 mHWData.backup();
1523 mHWData->mCPUCount = aCPUCount;
1524
1525 return S_OK;
1526}
1527
1528HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1529{
1530 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1531
1532 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1533
1534 return S_OK;
1535}
1536
1537HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1538{
1539 HRESULT rc = S_OK;
1540
1541 /* check throttle limits */
1542 if ( aCPUExecutionCap < 1
1543 || aCPUExecutionCap > 100
1544 )
1545 return setError(E_INVALIDARG,
1546 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1547 aCPUExecutionCap, 1, 100);
1548
1549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1550
1551 alock.release();
1552 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1553 alock.acquire();
1554 if (FAILED(rc)) return rc;
1555
1556 i_setModified(IsModified_MachineData);
1557 mHWData.backup();
1558 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1559
1560 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1561 if (Global::IsOnline(mData->mMachineState))
1562 i_saveSettings(NULL);
1563
1564 return S_OK;
1565}
1566
1567HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1568{
1569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1570
1571 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1572
1573 return S_OK;
1574}
1575
1576HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1577{
1578 HRESULT rc = S_OK;
1579
1580 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1581
1582 rc = i_checkStateDependency(MutableStateDep);
1583 if (FAILED(rc)) return rc;
1584
1585 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1586 {
1587 if (aCPUHotPlugEnabled)
1588 {
1589 i_setModified(IsModified_MachineData);
1590 mHWData.backup();
1591
1592 /* Add the amount of CPUs currently attached */
1593 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1594 mHWData->mCPUAttached[i] = true;
1595 }
1596 else
1597 {
1598 /*
1599 * We can disable hotplug only if the amount of maximum CPUs is equal
1600 * to the amount of attached CPUs
1601 */
1602 unsigned cCpusAttached = 0;
1603 unsigned iHighestId = 0;
1604
1605 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1606 {
1607 if (mHWData->mCPUAttached[i])
1608 {
1609 cCpusAttached++;
1610 iHighestId = i;
1611 }
1612 }
1613
1614 if ( (cCpusAttached != mHWData->mCPUCount)
1615 || (iHighestId >= mHWData->mCPUCount))
1616 return setError(E_INVALIDARG,
1617 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1618
1619 i_setModified(IsModified_MachineData);
1620 mHWData.backup();
1621 }
1622 }
1623
1624 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1625
1626 return rc;
1627}
1628
1629HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1630{
1631 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1632
1633 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1634
1635 return S_OK;
1636}
1637
1638HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1639{
1640 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1643 if (SUCCEEDED(hrc))
1644 {
1645 i_setModified(IsModified_MachineData);
1646 mHWData.backup();
1647 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1648 }
1649 return hrc;
1650}
1651
1652HRESULT Machine::getCPUProfile(com::Utf8Str &aCPUProfile)
1653{
1654 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1655 aCPUProfile = mHWData->mCpuProfile;
1656 return S_OK;
1657}
1658
1659HRESULT Machine::setCPUProfile(const com::Utf8Str &aCPUProfile)
1660{
1661 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1662 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1663 if (SUCCEEDED(hrc))
1664 {
1665 i_setModified(IsModified_MachineData);
1666 mHWData.backup();
1667 /* Empty equals 'host'. */
1668 if (aCPUProfile.isNotEmpty())
1669 mHWData->mCpuProfile = aCPUProfile;
1670 else
1671 mHWData->mCpuProfile = "host";
1672 }
1673 return hrc;
1674}
1675
1676HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1677{
1678#ifdef VBOX_WITH_USB_CARDREADER
1679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1680
1681 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1682
1683 return S_OK;
1684#else
1685 NOREF(aEmulatedUSBCardReaderEnabled);
1686 return E_NOTIMPL;
1687#endif
1688}
1689
1690HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1691{
1692#ifdef VBOX_WITH_USB_CARDREADER
1693 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1694
1695 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1696 if (FAILED(rc)) return rc;
1697
1698 i_setModified(IsModified_MachineData);
1699 mHWData.backup();
1700 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1701
1702 return S_OK;
1703#else
1704 NOREF(aEmulatedUSBCardReaderEnabled);
1705 return E_NOTIMPL;
1706#endif
1707}
1708
1709HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1710{
1711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1712
1713 *aHPETEnabled = mHWData->mHPETEnabled;
1714
1715 return S_OK;
1716}
1717
1718HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1719{
1720 HRESULT rc = S_OK;
1721
1722 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1723
1724 rc = i_checkStateDependency(MutableStateDep);
1725 if (FAILED(rc)) return rc;
1726
1727 i_setModified(IsModified_MachineData);
1728 mHWData.backup();
1729
1730 mHWData->mHPETEnabled = aHPETEnabled;
1731
1732 return rc;
1733}
1734
1735HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1736{
1737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1738
1739 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1740
1741 return S_OK;
1742}
1743
1744HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1745{
1746 switch (aGraphicsControllerType)
1747 {
1748 case GraphicsControllerType_Null:
1749 case GraphicsControllerType_VBoxVGA:
1750#ifdef VBOX_WITH_VMSVGA
1751 case GraphicsControllerType_VMSVGA:
1752 case GraphicsControllerType_VBoxSVGA:
1753#endif
1754 break;
1755 default:
1756 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1757 }
1758
1759 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1760
1761 HRESULT rc = i_checkStateDependency(MutableStateDep);
1762 if (FAILED(rc)) return rc;
1763
1764 i_setModified(IsModified_MachineData);
1765 mHWData.backup();
1766 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1767
1768 return S_OK;
1769}
1770
1771HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1772{
1773 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1774
1775 *aVRAMSize = mHWData->mVRAMSize;
1776
1777 return S_OK;
1778}
1779
1780HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1781{
1782 /* check VRAM limits */
1783 if (aVRAMSize > SchemaDefs::MaxGuestVRAM)
1784 return setError(E_INVALIDARG,
1785 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1786 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1787
1788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1789
1790 HRESULT rc = i_checkStateDependency(MutableStateDep);
1791 if (FAILED(rc)) return rc;
1792
1793 i_setModified(IsModified_MachineData);
1794 mHWData.backup();
1795 mHWData->mVRAMSize = aVRAMSize;
1796
1797 return S_OK;
1798}
1799
1800/** @todo this method should not be public */
1801HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
1802{
1803 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1804
1805 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
1806
1807 return S_OK;
1808}
1809
1810/**
1811 * Set the memory balloon size.
1812 *
1813 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
1814 * we have to make sure that we never call IGuest from here.
1815 */
1816HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
1817{
1818 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
1819#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
1820 /* check limits */
1821 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
1822 return setError(E_INVALIDARG,
1823 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
1824 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
1825
1826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1827
1828 i_setModified(IsModified_MachineData);
1829 mHWData.backup();
1830 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
1831
1832 return S_OK;
1833#else
1834 NOREF(aMemoryBalloonSize);
1835 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
1836#endif
1837}
1838
1839HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
1840{
1841 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1842
1843 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
1844 return S_OK;
1845}
1846
1847HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
1848{
1849#ifdef VBOX_WITH_PAGE_SHARING
1850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1851
1852 /** @todo must support changes for running vms and keep this in sync with IGuest. */
1853 i_setModified(IsModified_MachineData);
1854 mHWData.backup();
1855 mHWData->mPageFusionEnabled = aPageFusionEnabled;
1856 return S_OK;
1857#else
1858 NOREF(aPageFusionEnabled);
1859 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
1860#endif
1861}
1862
1863HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
1864{
1865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1866
1867 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
1868
1869 return S_OK;
1870}
1871
1872HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
1873{
1874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1875
1876 HRESULT rc = i_checkStateDependency(MutableStateDep);
1877 if (FAILED(rc)) return rc;
1878
1879 /** @todo check validity! */
1880
1881 i_setModified(IsModified_MachineData);
1882 mHWData.backup();
1883 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
1884
1885 return S_OK;
1886}
1887
1888
1889HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
1890{
1891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1892
1893 /** @todo quick workaround for hang with Win10 guest when 2d accel
1894 * is enabled when non-VBoxVGA graphics is configured. */
1895 if (mHWData->mGraphicsControllerType == GraphicsControllerType_VBoxVGA)
1896 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
1897 else
1898 *aAccelerate2DVideoEnabled = FALSE;
1899
1900 return S_OK;
1901}
1902
1903HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
1904{
1905 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1906
1907 HRESULT rc = i_checkStateDependency(MutableStateDep);
1908 if (FAILED(rc)) return rc;
1909
1910 /** @todo check validity! */
1911 i_setModified(IsModified_MachineData);
1912 mHWData.backup();
1913 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
1914
1915 return S_OK;
1916}
1917
1918HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
1919{
1920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1921
1922 *aMonitorCount = mHWData->mMonitorCount;
1923
1924 return S_OK;
1925}
1926
1927HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
1928{
1929 /* make sure monitor count is a sensible number */
1930 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
1931 return setError(E_INVALIDARG,
1932 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
1933 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
1934
1935 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1936
1937 HRESULT rc = i_checkStateDependency(MutableStateDep);
1938 if (FAILED(rc)) return rc;
1939
1940 i_setModified(IsModified_MachineData);
1941 mHWData.backup();
1942 mHWData->mMonitorCount = aMonitorCount;
1943
1944 return S_OK;
1945}
1946
1947HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
1948{
1949 /* mBIOSSettings is constant during life time, no need to lock */
1950 aBIOSSettings = mBIOSSettings;
1951
1952 return S_OK;
1953}
1954
1955HRESULT Machine::getRecordingSettings(ComPtr<IRecordingSettings> &aRecordingSettings)
1956{
1957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1958
1959 aRecordingSettings = mRecordingSettings;
1960
1961 return S_OK;
1962}
1963
1964HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
1965{
1966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1967
1968 switch (aProperty)
1969 {
1970 case CPUPropertyType_PAE:
1971 *aValue = mHWData->mPAEEnabled;
1972 break;
1973
1974 case CPUPropertyType_LongMode:
1975 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
1976 *aValue = TRUE;
1977 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
1978 *aValue = FALSE;
1979#if HC_ARCH_BITS == 64
1980 else
1981 *aValue = TRUE;
1982#else
1983 else
1984 {
1985 *aValue = FALSE;
1986
1987 ComObjPtr<GuestOSType> pGuestOSType;
1988 HRESULT hrc2 = mParent->i_findGuestOSType(mUserData->s.strOsType,
1989 pGuestOSType);
1990 if (SUCCEEDED(hrc2) && !pGuestOSType.isNull())
1991 {
1992 if (pGuestOSType->i_is64Bit())
1993 {
1994 ComObjPtr<Host> pHost = mParent->i_host();
1995 alock.release();
1996
1997 hrc2 = pHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
1998 if (FAILED(hrc2))
1999 *aValue = FALSE;
2000 }
2001 }
2002 }
2003#endif
2004 break;
2005
2006 case CPUPropertyType_TripleFaultReset:
2007 *aValue = mHWData->mTripleFaultReset;
2008 break;
2009
2010 case CPUPropertyType_APIC:
2011 *aValue = mHWData->mAPIC;
2012 break;
2013
2014 case CPUPropertyType_X2APIC:
2015 *aValue = mHWData->mX2APIC;
2016 break;
2017
2018 case CPUPropertyType_IBPBOnVMExit:
2019 *aValue = mHWData->mIBPBOnVMExit;
2020 break;
2021
2022 case CPUPropertyType_IBPBOnVMEntry:
2023 *aValue = mHWData->mIBPBOnVMEntry;
2024 break;
2025
2026 case CPUPropertyType_SpecCtrl:
2027 *aValue = mHWData->mSpecCtrl;
2028 break;
2029
2030 case CPUPropertyType_SpecCtrlByHost:
2031 *aValue = mHWData->mSpecCtrlByHost;
2032 break;
2033
2034 case CPUPropertyType_HWVirt:
2035 *aValue = mHWData->mNestedHWVirt;
2036 break;
2037
2038 case CPUPropertyType_L1DFlushOnEMTScheduling:
2039 *aValue = mHWData->mL1DFlushOnSched;
2040 break;
2041
2042 case CPUPropertyType_L1DFlushOnVMEntry:
2043 *aValue = mHWData->mL1DFlushOnVMEntry;
2044 break;
2045
2046 case CPUPropertyType_MDSClearOnEMTScheduling:
2047 *aValue = mHWData->mMDSClearOnSched;
2048 break;
2049
2050 case CPUPropertyType_MDSClearOnVMEntry:
2051 *aValue = mHWData->mMDSClearOnVMEntry;
2052 break;
2053
2054 default:
2055 return E_INVALIDARG;
2056 }
2057 return S_OK;
2058}
2059
2060HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2061{
2062 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2063
2064 HRESULT rc = i_checkStateDependency(MutableStateDep);
2065 if (FAILED(rc)) return rc;
2066
2067 switch (aProperty)
2068 {
2069 case CPUPropertyType_PAE:
2070 i_setModified(IsModified_MachineData);
2071 mHWData.backup();
2072 mHWData->mPAEEnabled = !!aValue;
2073 break;
2074
2075 case CPUPropertyType_LongMode:
2076 i_setModified(IsModified_MachineData);
2077 mHWData.backup();
2078 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2079 break;
2080
2081 case CPUPropertyType_TripleFaultReset:
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mTripleFaultReset = !!aValue;
2085 break;
2086
2087 case CPUPropertyType_APIC:
2088 if (mHWData->mX2APIC)
2089 aValue = TRUE;
2090 i_setModified(IsModified_MachineData);
2091 mHWData.backup();
2092 mHWData->mAPIC = !!aValue;
2093 break;
2094
2095 case CPUPropertyType_X2APIC:
2096 i_setModified(IsModified_MachineData);
2097 mHWData.backup();
2098 mHWData->mX2APIC = !!aValue;
2099 if (aValue)
2100 mHWData->mAPIC = !!aValue;
2101 break;
2102
2103 case CPUPropertyType_IBPBOnVMExit:
2104 i_setModified(IsModified_MachineData);
2105 mHWData.backup();
2106 mHWData->mIBPBOnVMExit = !!aValue;
2107 break;
2108
2109 case CPUPropertyType_IBPBOnVMEntry:
2110 i_setModified(IsModified_MachineData);
2111 mHWData.backup();
2112 mHWData->mIBPBOnVMEntry = !!aValue;
2113 break;
2114
2115 case CPUPropertyType_SpecCtrl:
2116 i_setModified(IsModified_MachineData);
2117 mHWData.backup();
2118 mHWData->mSpecCtrl = !!aValue;
2119 break;
2120
2121 case CPUPropertyType_SpecCtrlByHost:
2122 i_setModified(IsModified_MachineData);
2123 mHWData.backup();
2124 mHWData->mSpecCtrlByHost = !!aValue;
2125 break;
2126
2127 case CPUPropertyType_HWVirt:
2128 i_setModified(IsModified_MachineData);
2129 mHWData.backup();
2130 mHWData->mNestedHWVirt = !!aValue;
2131 break;
2132
2133 case CPUPropertyType_L1DFlushOnEMTScheduling:
2134 i_setModified(IsModified_MachineData);
2135 mHWData.backup();
2136 mHWData->mL1DFlushOnSched = !!aValue;
2137 break;
2138
2139 case CPUPropertyType_L1DFlushOnVMEntry:
2140 i_setModified(IsModified_MachineData);
2141 mHWData.backup();
2142 mHWData->mL1DFlushOnVMEntry = !!aValue;
2143 break;
2144
2145 case CPUPropertyType_MDSClearOnEMTScheduling:
2146 i_setModified(IsModified_MachineData);
2147 mHWData.backup();
2148 mHWData->mMDSClearOnSched = !!aValue;
2149 break;
2150
2151 case CPUPropertyType_MDSClearOnVMEntry:
2152 i_setModified(IsModified_MachineData);
2153 mHWData.backup();
2154 mHWData->mMDSClearOnVMEntry = !!aValue;
2155 break;
2156
2157 default:
2158 return E_INVALIDARG;
2159 }
2160 return S_OK;
2161}
2162
2163HRESULT Machine::getCPUIDLeafByOrdinal(ULONG aOrdinal, ULONG *aIdx, ULONG *aSubIdx, ULONG *aValEax, ULONG *aValEbx,
2164 ULONG *aValEcx, ULONG *aValEdx)
2165{
2166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2167 if (aOrdinal < mHWData->mCpuIdLeafList.size())
2168 {
2169 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin();
2170 it != mHWData->mCpuIdLeafList.end();
2171 ++it)
2172 {
2173 if (aOrdinal == 0)
2174 {
2175 const settings::CpuIdLeaf &rLeaf= *it;
2176 *aIdx = rLeaf.idx;
2177 *aSubIdx = rLeaf.idxSub;
2178 *aValEax = rLeaf.uEax;
2179 *aValEbx = rLeaf.uEbx;
2180 *aValEcx = rLeaf.uEcx;
2181 *aValEdx = rLeaf.uEdx;
2182 return S_OK;
2183 }
2184 aOrdinal--;
2185 }
2186 }
2187 return E_INVALIDARG;
2188}
2189
2190HRESULT Machine::getCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2191{
2192 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2193
2194 /*
2195 * Search the list.
2196 */
2197 for (settings::CpuIdLeafsList::const_iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); ++it)
2198 {
2199 const settings::CpuIdLeaf &rLeaf= *it;
2200 if ( rLeaf.idx == aIdx
2201 && ( aSubIdx == UINT32_MAX
2202 || rLeaf.idxSub == aSubIdx) )
2203 {
2204 *aValEax = rLeaf.uEax;
2205 *aValEbx = rLeaf.uEbx;
2206 *aValEcx = rLeaf.uEcx;
2207 *aValEdx = rLeaf.uEdx;
2208 return S_OK;
2209 }
2210 }
2211
2212 return E_INVALIDARG;
2213}
2214
2215
2216HRESULT Machine::setCPUIDLeaf(ULONG aIdx, ULONG aSubIdx, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2217{
2218 /*
2219 * Validate input before taking locks and checking state.
2220 */
2221 if (aSubIdx != 0 && aSubIdx != UINT32_MAX)
2222 return setError(E_INVALIDARG, tr("Currently only aSubIdx values 0 and 0xffffffff are supported: %#x"), aSubIdx);
2223 if ( aIdx >= UINT32_C(0x20)
2224 && aIdx - UINT32_C(0x80000000) >= UINT32_C(0x20)
2225 && aIdx - UINT32_C(0xc0000000) >= UINT32_C(0x10) )
2226 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aIdx);
2227
2228 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2229 HRESULT rc = i_checkStateDependency(MutableStateDep);
2230 if (FAILED(rc)) return rc;
2231
2232 /*
2233 * Impose a maximum number of leaves.
2234 */
2235 if (mHWData->mCpuIdLeafList.size() > 256)
2236 return setError(E_FAIL, tr("Max of 256 CPUID override leaves reached"));
2237
2238 /*
2239 * Updating the list is a bit more complicated. So, let's do a remove first followed by an insert.
2240 */
2241 i_setModified(IsModified_MachineData);
2242 mHWData.backup();
2243
2244 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2245 {
2246 settings::CpuIdLeaf &rLeaf= *it;
2247 if ( rLeaf.idx == aIdx
2248 && ( aSubIdx == UINT32_MAX
2249 || rLeaf.idxSub == aSubIdx) )
2250 it = mHWData->mCpuIdLeafList.erase(it);
2251 else
2252 ++it;
2253 }
2254
2255 settings::CpuIdLeaf NewLeaf;
2256 NewLeaf.idx = aIdx;
2257 NewLeaf.idxSub = aSubIdx == UINT32_MAX ? 0 : aSubIdx;
2258 NewLeaf.uEax = aValEax;
2259 NewLeaf.uEbx = aValEbx;
2260 NewLeaf.uEcx = aValEcx;
2261 NewLeaf.uEdx = aValEdx;
2262 mHWData->mCpuIdLeafList.push_back(NewLeaf);
2263 return S_OK;
2264}
2265
2266HRESULT Machine::removeCPUIDLeaf(ULONG aIdx, ULONG aSubIdx)
2267{
2268 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2269
2270 HRESULT rc = i_checkStateDependency(MutableStateDep);
2271 if (FAILED(rc)) return rc;
2272
2273 /*
2274 * Do the removal.
2275 */
2276 bool fModified = mHWData.isBackedUp();
2277 for (settings::CpuIdLeafsList::iterator it = mHWData->mCpuIdLeafList.begin(); it != mHWData->mCpuIdLeafList.end(); )
2278 {
2279 settings::CpuIdLeaf &rLeaf= *it;
2280 if ( rLeaf.idx == aIdx
2281 && ( aSubIdx == UINT32_MAX
2282 || rLeaf.idxSub == aSubIdx) )
2283 {
2284 if (!fModified)
2285 {
2286 fModified = true;
2287 i_setModified(IsModified_MachineData);
2288 mHWData.backup();
2289 // Start from the beginning, since mHWData.backup() creates
2290 // a new list, causing iterator mixup. This makes sure that
2291 // the settings are not unnecessarily marked as modified,
2292 // at the price of extra list walking.
2293 it = mHWData->mCpuIdLeafList.begin();
2294 }
2295 else
2296 it = mHWData->mCpuIdLeafList.erase(it);
2297 }
2298 else
2299 ++it;
2300 }
2301
2302 return S_OK;
2303}
2304
2305HRESULT Machine::removeAllCPUIDLeaves()
2306{
2307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2308
2309 HRESULT rc = i_checkStateDependency(MutableStateDep);
2310 if (FAILED(rc)) return rc;
2311
2312 if (mHWData->mCpuIdLeafList.size() > 0)
2313 {
2314 i_setModified(IsModified_MachineData);
2315 mHWData.backup();
2316
2317 mHWData->mCpuIdLeafList.clear();
2318 }
2319
2320 return S_OK;
2321}
2322HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2323{
2324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2325
2326 switch(aProperty)
2327 {
2328 case HWVirtExPropertyType_Enabled:
2329 *aValue = mHWData->mHWVirtExEnabled;
2330 break;
2331
2332 case HWVirtExPropertyType_VPID:
2333 *aValue = mHWData->mHWVirtExVPIDEnabled;
2334 break;
2335
2336 case HWVirtExPropertyType_NestedPaging:
2337 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2338 break;
2339
2340 case HWVirtExPropertyType_UnrestrictedExecution:
2341 *aValue = mHWData->mHWVirtExUXEnabled;
2342 break;
2343
2344 case HWVirtExPropertyType_LargePages:
2345 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2346#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2347 *aValue = FALSE;
2348#endif
2349 break;
2350
2351 case HWVirtExPropertyType_Force:
2352 *aValue = mHWData->mHWVirtExForceEnabled;
2353 break;
2354
2355 case HWVirtExPropertyType_UseNativeApi:
2356 *aValue = mHWData->mHWVirtExUseNativeApi;
2357 break;
2358
2359 default:
2360 return E_INVALIDARG;
2361 }
2362 return S_OK;
2363}
2364
2365HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2366{
2367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2368
2369 HRESULT rc = i_checkStateDependency(MutableStateDep);
2370 if (FAILED(rc)) return rc;
2371
2372 switch (aProperty)
2373 {
2374 case HWVirtExPropertyType_Enabled:
2375 i_setModified(IsModified_MachineData);
2376 mHWData.backup();
2377 mHWData->mHWVirtExEnabled = !!aValue;
2378 break;
2379
2380 case HWVirtExPropertyType_VPID:
2381 i_setModified(IsModified_MachineData);
2382 mHWData.backup();
2383 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2384 break;
2385
2386 case HWVirtExPropertyType_NestedPaging:
2387 i_setModified(IsModified_MachineData);
2388 mHWData.backup();
2389 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2390 break;
2391
2392 case HWVirtExPropertyType_UnrestrictedExecution:
2393 i_setModified(IsModified_MachineData);
2394 mHWData.backup();
2395 mHWData->mHWVirtExUXEnabled = !!aValue;
2396 break;
2397
2398 case HWVirtExPropertyType_LargePages:
2399 i_setModified(IsModified_MachineData);
2400 mHWData.backup();
2401 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2402 break;
2403
2404 case HWVirtExPropertyType_Force:
2405 i_setModified(IsModified_MachineData);
2406 mHWData.backup();
2407 mHWData->mHWVirtExForceEnabled = !!aValue;
2408 break;
2409
2410 case HWVirtExPropertyType_UseNativeApi:
2411 i_setModified(IsModified_MachineData);
2412 mHWData.backup();
2413 mHWData->mHWVirtExUseNativeApi = !!aValue;
2414 break;
2415
2416 default:
2417 return E_INVALIDARG;
2418 }
2419
2420 return S_OK;
2421}
2422
2423HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2424{
2425 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2426
2427 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2428
2429 return S_OK;
2430}
2431
2432HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2433{
2434 /** @todo (r=dmik):
2435 * 1. Allow to change the name of the snapshot folder containing snapshots
2436 * 2. Rename the folder on disk instead of just changing the property
2437 * value (to be smart and not to leave garbage). Note that it cannot be
2438 * done here because the change may be rolled back. Thus, the right
2439 * place is #saveSettings().
2440 */
2441
2442 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2443
2444 HRESULT rc = i_checkStateDependency(MutableStateDep);
2445 if (FAILED(rc)) return rc;
2446
2447 if (!mData->mCurrentSnapshot.isNull())
2448 return setError(E_FAIL,
2449 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2450
2451 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2452
2453 if (strSnapshotFolder.isEmpty())
2454 strSnapshotFolder = "Snapshots";
2455 int vrc = i_calculateFullPath(strSnapshotFolder,
2456 strSnapshotFolder);
2457 if (RT_FAILURE(vrc))
2458 return setErrorBoth(E_FAIL, vrc,
2459 tr("Invalid snapshot folder '%s' (%Rrc)"),
2460 strSnapshotFolder.c_str(), vrc);
2461
2462 i_setModified(IsModified_MachineData);
2463 mUserData.backup();
2464
2465 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2466
2467 return S_OK;
2468}
2469
2470HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2471{
2472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2473
2474 aMediumAttachments.resize(mMediumAttachments->size());
2475 size_t i = 0;
2476 for (MediumAttachmentList::const_iterator
2477 it = mMediumAttachments->begin();
2478 it != mMediumAttachments->end();
2479 ++it, ++i)
2480 aMediumAttachments[i] = *it;
2481
2482 return S_OK;
2483}
2484
2485HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2486{
2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2488
2489 Assert(!!mVRDEServer);
2490
2491 aVRDEServer = mVRDEServer;
2492
2493 return S_OK;
2494}
2495
2496HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2497{
2498 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2499
2500 aAudioAdapter = mAudioAdapter;
2501
2502 return S_OK;
2503}
2504
2505HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2506{
2507#ifdef VBOX_WITH_VUSB
2508 clearError();
2509 MultiResult rc(S_OK);
2510
2511# ifdef VBOX_WITH_USB
2512 rc = mParent->i_host()->i_checkUSBProxyService();
2513 if (FAILED(rc)) return rc;
2514# endif
2515
2516 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2517
2518 aUSBControllers.resize(mUSBControllers->size());
2519 size_t i = 0;
2520 for (USBControllerList::const_iterator
2521 it = mUSBControllers->begin();
2522 it != mUSBControllers->end();
2523 ++it, ++i)
2524 aUSBControllers[i] = *it;
2525
2526 return S_OK;
2527#else
2528 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2529 * extended error info to indicate that USB is simply not available
2530 * (w/o treating it as a failure), for example, as in OSE */
2531 NOREF(aUSBControllers);
2532 ReturnComNotImplemented();
2533#endif /* VBOX_WITH_VUSB */
2534}
2535
2536HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2537{
2538#ifdef VBOX_WITH_VUSB
2539 clearError();
2540 MultiResult rc(S_OK);
2541
2542# ifdef VBOX_WITH_USB
2543 rc = mParent->i_host()->i_checkUSBProxyService();
2544 if (FAILED(rc)) return rc;
2545# endif
2546
2547 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2548
2549 aUSBDeviceFilters = mUSBDeviceFilters;
2550 return rc;
2551#else
2552 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2553 * extended error info to indicate that USB is simply not available
2554 * (w/o treating it as a failure), for example, as in OSE */
2555 NOREF(aUSBDeviceFilters);
2556 ReturnComNotImplemented();
2557#endif /* VBOX_WITH_VUSB */
2558}
2559
2560HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2561{
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 aSettingsFilePath = mData->m_strConfigFileFull;
2565
2566 return S_OK;
2567}
2568
2569HRESULT Machine::getSettingsAuxFilePath(com::Utf8Str &aSettingsFilePath)
2570{
2571 RT_NOREF(aSettingsFilePath);
2572 ReturnComNotImplemented();
2573}
2574
2575HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2576{
2577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2578
2579 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2580 if (FAILED(rc)) return rc;
2581
2582 if (!mData->pMachineConfigFile->fileExists())
2583 // this is a new machine, and no config file exists yet:
2584 *aSettingsModified = TRUE;
2585 else
2586 *aSettingsModified = (mData->flModifications != 0);
2587
2588 return S_OK;
2589}
2590
2591HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2592{
2593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2594
2595 *aSessionState = mData->mSession.mState;
2596
2597 return S_OK;
2598}
2599
2600HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2601{
2602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2603
2604 aSessionName = mData->mSession.mName;
2605
2606 return S_OK;
2607}
2608
2609HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2610{
2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2612
2613 *aSessionPID = mData->mSession.mPID;
2614
2615 return S_OK;
2616}
2617
2618HRESULT Machine::getState(MachineState_T *aState)
2619{
2620 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2621
2622 *aState = mData->mMachineState;
2623 Assert(mData->mMachineState != MachineState_Null);
2624
2625 return S_OK;
2626}
2627
2628HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2629{
2630 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2631
2632 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2633
2634 return S_OK;
2635}
2636
2637HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2638{
2639 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2640
2641 aStateFilePath = mSSData->strStateFilePath;
2642
2643 return S_OK;
2644}
2645
2646HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 i_getLogFolder(aLogFolder);
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 aCurrentSnapshot = mData->mCurrentSnapshot;
2660
2661 return S_OK;
2662}
2663
2664HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2665{
2666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2667
2668 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2669 ? 0
2670 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2671
2672 return S_OK;
2673}
2674
2675HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2676{
2677 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2678
2679 /* Note: for machines with no snapshots, we always return FALSE
2680 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2681 * reasons :) */
2682
2683 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2684 ? FALSE
2685 : mData->mCurrentStateModified;
2686
2687 return S_OK;
2688}
2689
2690HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2691{
2692 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2693
2694 aSharedFolders.resize(mHWData->mSharedFolders.size());
2695 size_t i = 0;
2696 for (std::list<ComObjPtr<SharedFolder> >::const_iterator
2697 it = mHWData->mSharedFolders.begin();
2698 it != mHWData->mSharedFolders.end();
2699 ++it, ++i)
2700 aSharedFolders[i] = *it;
2701
2702 return S_OK;
2703}
2704
2705HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2706{
2707 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2708
2709 *aClipboardMode = mHWData->mClipboardMode;
2710
2711 return S_OK;
2712}
2713
2714HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2715{
2716 HRESULT rc = S_OK;
2717
2718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2719
2720 alock.release();
2721 rc = i_onClipboardModeChange(aClipboardMode);
2722 alock.acquire();
2723 if (FAILED(rc)) return rc;
2724
2725 i_setModified(IsModified_MachineData);
2726 mHWData.backup();
2727 mHWData->mClipboardMode = aClipboardMode;
2728
2729 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2730 if (Global::IsOnline(mData->mMachineState))
2731 i_saveSettings(NULL);
2732
2733 return S_OK;
2734}
2735
2736HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2737{
2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2739
2740 *aDnDMode = mHWData->mDnDMode;
2741
2742 return S_OK;
2743}
2744
2745HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2746{
2747 HRESULT rc = S_OK;
2748
2749 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2750
2751 alock.release();
2752 rc = i_onDnDModeChange(aDnDMode);
2753
2754 alock.acquire();
2755 if (FAILED(rc)) return rc;
2756
2757 i_setModified(IsModified_MachineData);
2758 mHWData.backup();
2759 mHWData->mDnDMode = aDnDMode;
2760
2761 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2762 if (Global::IsOnline(mData->mMachineState))
2763 i_saveSettings(NULL);
2764
2765 return S_OK;
2766}
2767
2768HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2769{
2770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2771
2772 aStorageControllers.resize(mStorageControllers->size());
2773 size_t i = 0;
2774 for (StorageControllerList::const_iterator
2775 it = mStorageControllers->begin();
2776 it != mStorageControllers->end();
2777 ++it, ++i)
2778 aStorageControllers[i] = *it;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aEnabled = mUserData->s.fTeleporterEnabled;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2793{
2794 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2795
2796 /* Only allow it to be set to true when PoweredOff or Aborted.
2797 (Clearing it is always permitted.) */
2798 if ( aTeleporterEnabled
2799 && mData->mRegistered
2800 && ( !i_isSessionMachine()
2801 || ( mData->mMachineState != MachineState_PoweredOff
2802 && mData->mMachineState != MachineState_Teleported
2803 && mData->mMachineState != MachineState_Aborted
2804 )
2805 )
2806 )
2807 return setError(VBOX_E_INVALID_VM_STATE,
2808 tr("The machine is not powered off (state is %s)"),
2809 Global::stringifyMachineState(mData->mMachineState));
2810
2811 i_setModified(IsModified_MachineData);
2812 mUserData.backup();
2813 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2814
2815 return S_OK;
2816}
2817
2818HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2819{
2820 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2821
2822 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2823
2824 return S_OK;
2825}
2826
2827HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2828{
2829 if (aTeleporterPort >= _64K)
2830 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2831
2832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2833
2834 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2835 if (FAILED(rc)) return rc;
2836
2837 i_setModified(IsModified_MachineData);
2838 mUserData.backup();
2839 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2840
2841 return S_OK;
2842}
2843
2844HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2845{
2846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2847
2848 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2849
2850 return S_OK;
2851}
2852
2853HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2854{
2855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2856
2857 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2858 if (FAILED(rc)) return rc;
2859
2860 i_setModified(IsModified_MachineData);
2861 mUserData.backup();
2862 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2863
2864 return S_OK;
2865}
2866
2867HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2868{
2869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2870 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2871
2872 return S_OK;
2873}
2874
2875HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2876{
2877 /*
2878 * Hash the password first.
2879 */
2880 com::Utf8Str aT = aTeleporterPassword;
2881
2882 if (!aT.isEmpty())
2883 {
2884 if (VBoxIsPasswordHashed(&aT))
2885 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2886 VBoxHashPassword(&aT);
2887 }
2888
2889 /*
2890 * Do the update.
2891 */
2892 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2893 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2894 if (SUCCEEDED(hrc))
2895 {
2896 i_setModified(IsModified_MachineData);
2897 mUserData.backup();
2898 mUserData->s.strTeleporterPassword = aT;
2899 }
2900
2901 return hrc;
2902}
2903
2904HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
2905{
2906 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
2909
2910 return S_OK;
2911}
2912
2913HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
2914{
2915 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2916
2917 /* Only allow it to be set to true when PoweredOff or Aborted.
2918 (Clearing it is always permitted.) */
2919 if ( aRTCUseUTC
2920 && mData->mRegistered
2921 && ( !i_isSessionMachine()
2922 || ( mData->mMachineState != MachineState_PoweredOff
2923 && mData->mMachineState != MachineState_Teleported
2924 && mData->mMachineState != MachineState_Aborted
2925 )
2926 )
2927 )
2928 return setError(VBOX_E_INVALID_VM_STATE,
2929 tr("The machine is not powered off (state is %s)"),
2930 Global::stringifyMachineState(mData->mMachineState));
2931
2932 i_setModified(IsModified_MachineData);
2933 mUserData.backup();
2934 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
2935
2936 return S_OK;
2937}
2938
2939HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
2940{
2941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2942
2943 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
2944
2945 return S_OK;
2946}
2947
2948HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
2949{
2950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2951
2952 HRESULT rc = i_checkStateDependency(MutableStateDep);
2953 if (FAILED(rc)) return rc;
2954
2955 i_setModified(IsModified_MachineData);
2956 mHWData.backup();
2957 mHWData->mIOCacheEnabled = aIOCacheEnabled;
2958
2959 return S_OK;
2960}
2961
2962HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
2963{
2964 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2965
2966 *aIOCacheSize = mHWData->mIOCacheSize;
2967
2968 return S_OK;
2969}
2970
2971HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
2972{
2973 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2974
2975 HRESULT rc = i_checkStateDependency(MutableStateDep);
2976 if (FAILED(rc)) return rc;
2977
2978 i_setModified(IsModified_MachineData);
2979 mHWData.backup();
2980 mHWData->mIOCacheSize = aIOCacheSize;
2981
2982 return S_OK;
2983}
2984
2985
2986/**
2987 * @note Locks objects!
2988 */
2989HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
2990 LockType_T aLockType)
2991{
2992 /* check the session state */
2993 SessionState_T state;
2994 HRESULT rc = aSession->COMGETTER(State)(&state);
2995 if (FAILED(rc)) return rc;
2996
2997 if (state != SessionState_Unlocked)
2998 return setError(VBOX_E_INVALID_OBJECT_STATE,
2999 tr("The given session is busy"));
3000
3001 // get the client's IInternalSessionControl interface
3002 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3003 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3004 E_INVALIDARG);
3005
3006 // session name (only used in some code paths)
3007 Utf8Str strSessionName;
3008
3009 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3010
3011 if (!mData->mRegistered)
3012 return setError(E_UNEXPECTED,
3013 tr("The machine '%s' is not registered"),
3014 mUserData->s.strName.c_str());
3015
3016 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3017
3018 SessionState_T oldState = mData->mSession.mState;
3019 /* Hack: in case the session is closing and there is a progress object
3020 * which allows waiting for the session to be closed, take the opportunity
3021 * and do a limited wait (max. 1 second). This helps a lot when the system
3022 * is busy and thus session closing can take a little while. */
3023 if ( mData->mSession.mState == SessionState_Unlocking
3024 && mData->mSession.mProgress)
3025 {
3026 alock.release();
3027 mData->mSession.mProgress->WaitForCompletion(1000);
3028 alock.acquire();
3029 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3030 }
3031
3032 // try again now
3033 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3034 // (i.e. session machine exists)
3035 && (aLockType == LockType_Shared) // caller wants a shared link to the
3036 // existing session that holds the write lock:
3037 )
3038 {
3039 // OK, share the session... we are now dealing with three processes:
3040 // 1) VBoxSVC (where this code runs);
3041 // 2) process C: the caller's client process (who wants a shared session);
3042 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3043
3044 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3045 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3046 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3047 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3048 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3049
3050 /*
3051 * Release the lock before calling the client process. It's safe here
3052 * since the only thing to do after we get the lock again is to add
3053 * the remote control to the list (which doesn't directly influence
3054 * anything).
3055 */
3056 alock.release();
3057
3058 // get the console of the session holding the write lock (this is a remote call)
3059 ComPtr<IConsole> pConsoleW;
3060 if (mData->mSession.mLockType == LockType_VM)
3061 {
3062 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3063 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3064 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3065 if (FAILED(rc))
3066 // the failure may occur w/o any error info (from RPC), so provide one
3067 return setError(VBOX_E_VM_ERROR,
3068 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3069 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3070 }
3071
3072 // share the session machine and W's console with the caller's session
3073 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3074 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3075 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3076
3077 if (FAILED(rc))
3078 // the failure may occur w/o any error info (from RPC), so provide one
3079 return setError(VBOX_E_VM_ERROR,
3080 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3081 alock.acquire();
3082
3083 // need to revalidate the state after acquiring the lock again
3084 if (mData->mSession.mState != SessionState_Locked)
3085 {
3086 pSessionControl->Uninitialize();
3087 return setError(VBOX_E_INVALID_SESSION_STATE,
3088 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3089 mUserData->s.strName.c_str());
3090 }
3091
3092 // add the caller's session to the list
3093 mData->mSession.mRemoteControls.push_back(pSessionControl);
3094 }
3095 else if ( mData->mSession.mState == SessionState_Locked
3096 || mData->mSession.mState == SessionState_Unlocking
3097 )
3098 {
3099 // sharing not permitted, or machine still unlocking:
3100 return setError(VBOX_E_INVALID_OBJECT_STATE,
3101 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3102 mUserData->s.strName.c_str());
3103 }
3104 else
3105 {
3106 // machine is not locked: then write-lock the machine (create the session machine)
3107
3108 // must not be busy
3109 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3110
3111 // get the caller's session PID
3112 RTPROCESS pid = NIL_RTPROCESS;
3113 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3114 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3115 Assert(pid != NIL_RTPROCESS);
3116
3117 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3118
3119 if (fLaunchingVMProcess)
3120 {
3121 if (mData->mSession.mPID == NIL_RTPROCESS)
3122 {
3123 // two or more clients racing for a lock, the one which set the
3124 // session state to Spawning will win, the others will get an
3125 // error as we can't decide here if waiting a little would help
3126 // (only for shared locks this would avoid an error)
3127 return setError(VBOX_E_INVALID_OBJECT_STATE,
3128 tr("The machine '%s' already has a lock request pending"),
3129 mUserData->s.strName.c_str());
3130 }
3131
3132 // this machine is awaiting for a spawning session to be opened:
3133 // then the calling process must be the one that got started by
3134 // LaunchVMProcess()
3135
3136 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3137 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3138
3139#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3140 /* Hardened windows builds spawns three processes when a VM is
3141 launched, the 3rd one is the one that will end up here. */
3142 RTPROCESS ppid;
3143 int rc = RTProcQueryParent(pid, &ppid);
3144 if (RT_SUCCESS(rc))
3145 rc = RTProcQueryParent(ppid, &ppid);
3146 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3147 || rc == VERR_ACCESS_DENIED)
3148 {
3149 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3150 mData->mSession.mPID = pid;
3151 }
3152#endif
3153
3154 if (mData->mSession.mPID != pid)
3155 return setError(E_ACCESSDENIED,
3156 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3157 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3158 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3159 }
3160
3161 // create the mutable SessionMachine from the current machine
3162 ComObjPtr<SessionMachine> sessionMachine;
3163 sessionMachine.createObject();
3164 rc = sessionMachine->init(this);
3165 AssertComRC(rc);
3166
3167 /* NOTE: doing return from this function after this point but
3168 * before the end is forbidden since it may call SessionMachine::uninit()
3169 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3170 * lock while still holding the Machine lock in alock so that a deadlock
3171 * is possible due to the wrong lock order. */
3172
3173 if (SUCCEEDED(rc))
3174 {
3175 /*
3176 * Set the session state to Spawning to protect against subsequent
3177 * attempts to open a session and to unregister the machine after
3178 * we release the lock.
3179 */
3180 SessionState_T origState = mData->mSession.mState;
3181 mData->mSession.mState = SessionState_Spawning;
3182
3183#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3184 /* Get the client token ID to be passed to the client process */
3185 Utf8Str strTokenId;
3186 sessionMachine->i_getTokenId(strTokenId);
3187 Assert(!strTokenId.isEmpty());
3188#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3189 /* Get the client token to be passed to the client process */
3190 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3191 /* The token is now "owned" by pToken, fix refcount */
3192 if (!pToken.isNull())
3193 pToken->Release();
3194#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3195
3196 /*
3197 * Release the lock before calling the client process -- it will call
3198 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3199 * because the state is Spawning, so that LaunchVMProcess() and
3200 * LockMachine() calls will fail. This method, called before we
3201 * acquire the lock again, will fail because of the wrong PID.
3202 *
3203 * Note that mData->mSession.mRemoteControls accessed outside
3204 * the lock may not be modified when state is Spawning, so it's safe.
3205 */
3206 alock.release();
3207
3208 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3209#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3210 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3211#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3212 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3213 /* Now the token is owned by the client process. */
3214 pToken.setNull();
3215#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3216 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3217
3218 /* The failure may occur w/o any error info (from RPC), so provide one */
3219 if (FAILED(rc))
3220 setError(VBOX_E_VM_ERROR,
3221 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3222
3223 // get session name, either to remember or to compare against
3224 // the already known session name.
3225 {
3226 Bstr bstrSessionName;
3227 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3228 if (SUCCEEDED(rc2))
3229 strSessionName = bstrSessionName;
3230 }
3231
3232 if ( SUCCEEDED(rc)
3233 && fLaunchingVMProcess
3234 )
3235 {
3236 /* complete the remote session initialization */
3237
3238 /* get the console from the direct session */
3239 ComPtr<IConsole> console;
3240 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3241 ComAssertComRC(rc);
3242
3243 if (SUCCEEDED(rc) && !console)
3244 {
3245 ComAssert(!!console);
3246 rc = E_FAIL;
3247 }
3248
3249 /* assign machine & console to the remote session */
3250 if (SUCCEEDED(rc))
3251 {
3252 /*
3253 * after LaunchVMProcess(), the first and the only
3254 * entry in remoteControls is that remote session
3255 */
3256 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3257 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3258 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3259
3260 /* The failure may occur w/o any error info (from RPC), so provide one */
3261 if (FAILED(rc))
3262 setError(VBOX_E_VM_ERROR,
3263 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3264 }
3265
3266 if (FAILED(rc))
3267 pSessionControl->Uninitialize();
3268 }
3269
3270 /* acquire the lock again */
3271 alock.acquire();
3272
3273 /* Restore the session state */
3274 mData->mSession.mState = origState;
3275 }
3276
3277 // finalize spawning anyway (this is why we don't return on errors above)
3278 if (fLaunchingVMProcess)
3279 {
3280 Assert(mData->mSession.mName == strSessionName || FAILED(rc));
3281 /* Note that the progress object is finalized later */
3282 /** @todo Consider checking mData->mSession.mProgress for cancellation
3283 * around here. */
3284
3285 /* We don't reset mSession.mPID here because it is necessary for
3286 * SessionMachine::uninit() to reap the child process later. */
3287
3288 if (FAILED(rc))
3289 {
3290 /* Close the remote session, remove the remote control from the list
3291 * and reset session state to Closed (@note keep the code in sync
3292 * with the relevant part in checkForSpawnFailure()). */
3293
3294 Assert(mData->mSession.mRemoteControls.size() == 1);
3295 if (mData->mSession.mRemoteControls.size() == 1)
3296 {
3297 ErrorInfoKeeper eik;
3298 mData->mSession.mRemoteControls.front()->Uninitialize();
3299 }
3300
3301 mData->mSession.mRemoteControls.clear();
3302 mData->mSession.mState = SessionState_Unlocked;
3303 }
3304 }
3305 else
3306 {
3307 /* memorize PID of the directly opened session */
3308 if (SUCCEEDED(rc))
3309 mData->mSession.mPID = pid;
3310 }
3311
3312 if (SUCCEEDED(rc))
3313 {
3314 mData->mSession.mLockType = aLockType;
3315 /* memorize the direct session control and cache IUnknown for it */
3316 mData->mSession.mDirectControl = pSessionControl;
3317 mData->mSession.mState = SessionState_Locked;
3318 if (!fLaunchingVMProcess)
3319 mData->mSession.mName = strSessionName;
3320 /* associate the SessionMachine with this Machine */
3321 mData->mSession.mMachine = sessionMachine;
3322
3323 /* request an IUnknown pointer early from the remote party for later
3324 * identity checks (it will be internally cached within mDirectControl
3325 * at least on XPCOM) */
3326 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3327 NOREF(unk);
3328 }
3329
3330 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3331 * would break the lock order */
3332 alock.release();
3333
3334 /* uninitialize the created session machine on failure */
3335 if (FAILED(rc))
3336 sessionMachine->uninit();
3337 }
3338
3339 if (SUCCEEDED(rc))
3340 {
3341 /*
3342 * tell the client watcher thread to update the set of
3343 * machines that have open sessions
3344 */
3345 mParent->i_updateClientWatcher();
3346
3347 if (oldState != SessionState_Locked)
3348 /* fire an event */
3349 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3350 }
3351
3352 return rc;
3353}
3354
3355/**
3356 * @note Locks objects!
3357 */
3358HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3359 const com::Utf8Str &aName,
3360 const std::vector<com::Utf8Str> &aEnvironmentChanges,
3361 ComPtr<IProgress> &aProgress)
3362{
3363 Utf8Str strFrontend(aName);
3364 /* "emergencystop" doesn't need the session, so skip the checks/interface
3365 * retrieval. This code doesn't quite fit in here, but introducing a
3366 * special API method would be even more effort, and would require explicit
3367 * support by every API client. It's better to hide the feature a bit. */
3368 if (strFrontend != "emergencystop")
3369 CheckComArgNotNull(aSession);
3370
3371 HRESULT rc = S_OK;
3372 if (strFrontend.isEmpty())
3373 {
3374 Bstr bstrFrontend;
3375 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3376 if (FAILED(rc))
3377 return rc;
3378 strFrontend = bstrFrontend;
3379 if (strFrontend.isEmpty())
3380 {
3381 ComPtr<ISystemProperties> systemProperties;
3382 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3383 if (FAILED(rc))
3384 return rc;
3385 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3386 if (FAILED(rc))
3387 return rc;
3388 strFrontend = bstrFrontend;
3389 }
3390 /* paranoia - emergencystop is not a valid default */
3391 if (strFrontend == "emergencystop")
3392 strFrontend = Utf8Str::Empty;
3393 }
3394 /* default frontend: Qt GUI */
3395 if (strFrontend.isEmpty())
3396 strFrontend = "GUI/Qt";
3397
3398 if (strFrontend != "emergencystop")
3399 {
3400 /* check the session state */
3401 SessionState_T state;
3402 rc = aSession->COMGETTER(State)(&state);
3403 if (FAILED(rc))
3404 return rc;
3405
3406 if (state != SessionState_Unlocked)
3407 return setError(VBOX_E_INVALID_OBJECT_STATE,
3408 tr("The given session is busy"));
3409
3410 /* get the IInternalSessionControl interface */
3411 ComPtr<IInternalSessionControl> control(aSession);
3412 ComAssertMsgRet(!control.isNull(),
3413 ("No IInternalSessionControl interface"),
3414 E_INVALIDARG);
3415
3416 /* get the teleporter enable state for the progress object init. */
3417 BOOL fTeleporterEnabled;
3418 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3419 if (FAILED(rc))
3420 return rc;
3421
3422 /* create a progress object */
3423 ComObjPtr<ProgressProxy> progress;
3424 progress.createObject();
3425 rc = progress->init(mParent,
3426 static_cast<IMachine*>(this),
3427 Bstr(tr("Starting VM")).raw(),
3428 TRUE /* aCancelable */,
3429 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3430 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3431 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3432 2 /* uFirstOperationWeight */,
3433 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3434
3435 if (SUCCEEDED(rc))
3436 {
3437 rc = i_launchVMProcess(control, strFrontend, aEnvironmentChanges, progress);
3438 if (SUCCEEDED(rc))
3439 {
3440 aProgress = progress;
3441
3442 /* signal the client watcher thread */
3443 mParent->i_updateClientWatcher();
3444
3445 /* fire an event */
3446 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3447 }
3448 }
3449 }
3450 else
3451 {
3452 /* no progress object - either instant success or failure */
3453 aProgress = NULL;
3454
3455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3456
3457 if (mData->mSession.mState != SessionState_Locked)
3458 return setError(VBOX_E_INVALID_OBJECT_STATE,
3459 tr("The machine '%s' is not locked by a session"),
3460 mUserData->s.strName.c_str());
3461
3462 /* must have a VM process associated - do not kill normal API clients
3463 * with an open session */
3464 if (!Global::IsOnline(mData->mMachineState))
3465 return setError(VBOX_E_INVALID_OBJECT_STATE,
3466 tr("The machine '%s' does not have a VM process"),
3467 mUserData->s.strName.c_str());
3468
3469 /* forcibly terminate the VM process */
3470 if (mData->mSession.mPID != NIL_RTPROCESS)
3471 RTProcTerminate(mData->mSession.mPID);
3472
3473 /* signal the client watcher thread, as most likely the client has
3474 * been terminated */
3475 mParent->i_updateClientWatcher();
3476 }
3477
3478 return rc;
3479}
3480
3481HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3482{
3483 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3484 return setError(E_INVALIDARG,
3485 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3486 aPosition, SchemaDefs::MaxBootPosition);
3487
3488 if (aDevice == DeviceType_USB)
3489 return setError(E_NOTIMPL,
3490 tr("Booting from USB device is currently not supported"));
3491
3492 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3493
3494 HRESULT rc = i_checkStateDependency(MutableStateDep);
3495 if (FAILED(rc)) return rc;
3496
3497 i_setModified(IsModified_MachineData);
3498 mHWData.backup();
3499 mHWData->mBootOrder[aPosition - 1] = aDevice;
3500
3501 return S_OK;
3502}
3503
3504HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3505{
3506 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3507 return setError(E_INVALIDARG,
3508 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3509 aPosition, SchemaDefs::MaxBootPosition);
3510
3511 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3512
3513 *aDevice = mHWData->mBootOrder[aPosition - 1];
3514
3515 return S_OK;
3516}
3517
3518HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3519 LONG aControllerPort,
3520 LONG aDevice,
3521 DeviceType_T aType,
3522 const ComPtr<IMedium> &aMedium)
3523{
3524 IMedium *aM = aMedium;
3525 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3526 aName.c_str(), aControllerPort, aDevice, aType, aM));
3527
3528 // request the host lock first, since might be calling Host methods for getting host drives;
3529 // next, protect the media tree all the while we're in here, as well as our member variables
3530 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3531 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3532
3533 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3534 if (FAILED(rc)) return rc;
3535
3536 /// @todo NEWMEDIA implicit machine registration
3537 if (!mData->mRegistered)
3538 return setError(VBOX_E_INVALID_OBJECT_STATE,
3539 tr("Cannot attach storage devices to an unregistered machine"));
3540
3541 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3542
3543 /* Check for an existing controller. */
3544 ComObjPtr<StorageController> ctl;
3545 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3546 if (FAILED(rc)) return rc;
3547
3548 StorageControllerType_T ctrlType;
3549 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3550 if (FAILED(rc))
3551 return setError(E_FAIL,
3552 tr("Could not get type of controller '%s'"),
3553 aName.c_str());
3554
3555 bool fSilent = false;
3556 Utf8Str strReconfig;
3557
3558 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3559 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3560 if ( mData->mMachineState == MachineState_Paused
3561 && strReconfig == "1")
3562 fSilent = true;
3563
3564 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3565 bool fHotplug = false;
3566 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3567 fHotplug = true;
3568
3569 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3570 return setError(VBOX_E_INVALID_VM_STATE,
3571 tr("Controller '%s' does not support hotplugging"),
3572 aName.c_str());
3573
3574 // check that the port and device are not out of range
3575 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3576 if (FAILED(rc)) return rc;
3577
3578 /* check if the device slot is already busy */
3579 MediumAttachment *pAttachTemp;
3580 if ((pAttachTemp = i_findAttachment(*mMediumAttachments.data(),
3581 aName,
3582 aControllerPort,
3583 aDevice)))
3584 {
3585 Medium *pMedium = pAttachTemp->i_getMedium();
3586 if (pMedium)
3587 {
3588 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3589 return setError(VBOX_E_OBJECT_IN_USE,
3590 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3591 pMedium->i_getLocationFull().c_str(),
3592 aControllerPort,
3593 aDevice,
3594 aName.c_str());
3595 }
3596 else
3597 return setError(VBOX_E_OBJECT_IN_USE,
3598 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3599 aControllerPort, aDevice, aName.c_str());
3600 }
3601
3602 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3603 if (aMedium && medium.isNull())
3604 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3605
3606 AutoCaller mediumCaller(medium);
3607 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3608
3609 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3610
3611 if ( (pAttachTemp = i_findAttachment(*mMediumAttachments.data(), medium))
3612 && !medium.isNull()
3613 )
3614 return setError(VBOX_E_OBJECT_IN_USE,
3615 tr("Medium '%s' is already attached to this virtual machine"),
3616 medium->i_getLocationFull().c_str());
3617
3618 if (!medium.isNull())
3619 {
3620 MediumType_T mtype = medium->i_getType();
3621 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3622 // For DVDs it's not written to the config file, so needs no global config
3623 // version bump. For floppies it's a new attribute "type", which is ignored
3624 // by older VirtualBox version, so needs no global config version bump either.
3625 // For hard disks this type is not accepted.
3626 if (mtype == MediumType_MultiAttach)
3627 {
3628 // This type is new with VirtualBox 4.0 and therefore requires settings
3629 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3630 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3631 // two reasons: The medium type is a property of the media registry tree, which
3632 // can reside in the global config file (for pre-4.0 media); we would therefore
3633 // possibly need to bump the global config version. We don't want to do that though
3634 // because that might make downgrading to pre-4.0 impossible.
3635 // As a result, we can only use these two new types if the medium is NOT in the
3636 // global registry:
3637 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3638 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3639 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3640 )
3641 return setError(VBOX_E_INVALID_OBJECT_STATE,
3642 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3643 "to machines that were created with VirtualBox 4.0 or later"),
3644 medium->i_getLocationFull().c_str());
3645 }
3646 }
3647
3648 bool fIndirect = false;
3649 if (!medium.isNull())
3650 fIndirect = medium->i_isReadOnly();
3651 bool associate = true;
3652
3653 do
3654 {
3655 if ( aType == DeviceType_HardDisk
3656 && mMediumAttachments.isBackedUp())
3657 {
3658 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3659
3660 /* check if the medium was attached to the VM before we started
3661 * changing attachments in which case the attachment just needs to
3662 * be restored */
3663 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3664 {
3665 AssertReturn(!fIndirect, E_FAIL);
3666
3667 /* see if it's the same bus/channel/device */
3668 if (pAttachTemp->i_matches(aName, aControllerPort, aDevice))
3669 {
3670 /* the simplest case: restore the whole attachment
3671 * and return, nothing else to do */
3672 mMediumAttachments->push_back(pAttachTemp);
3673
3674 /* Reattach the medium to the VM. */
3675 if (fHotplug || fSilent)
3676 {
3677 mediumLock.release();
3678 treeLock.release();
3679 alock.release();
3680
3681 MediumLockList *pMediumLockList(new MediumLockList());
3682
3683 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3684 medium /* pToLockWrite */,
3685 false /* fMediumLockWriteAll */,
3686 NULL,
3687 *pMediumLockList);
3688 alock.acquire();
3689 if (FAILED(rc))
3690 delete pMediumLockList;
3691 else
3692 {
3693 mData->mSession.mLockedMedia.Unlock();
3694 alock.release();
3695 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3696 mData->mSession.mLockedMedia.Lock();
3697 alock.acquire();
3698 }
3699 alock.release();
3700
3701 if (SUCCEEDED(rc))
3702 {
3703 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3704 /* Remove lock list in case of error. */
3705 if (FAILED(rc))
3706 {
3707 mData->mSession.mLockedMedia.Unlock();
3708 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3709 mData->mSession.mLockedMedia.Lock();
3710 }
3711 }
3712 }
3713
3714 return S_OK;
3715 }
3716
3717 /* bus/channel/device differ; we need a new attachment object,
3718 * but don't try to associate it again */
3719 associate = false;
3720 break;
3721 }
3722 }
3723
3724 /* go further only if the attachment is to be indirect */
3725 if (!fIndirect)
3726 break;
3727
3728 /* perform the so called smart attachment logic for indirect
3729 * attachments. Note that smart attachment is only applicable to base
3730 * hard disks. */
3731
3732 if (medium->i_getParent().isNull())
3733 {
3734 /* first, investigate the backup copy of the current hard disk
3735 * attachments to make it possible to re-attach existing diffs to
3736 * another device slot w/o losing their contents */
3737 if (mMediumAttachments.isBackedUp())
3738 {
3739 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
3740
3741 MediumAttachmentList::const_iterator foundIt = oldAtts.end();
3742 uint32_t foundLevel = 0;
3743
3744 for (MediumAttachmentList::const_iterator
3745 it = oldAtts.begin();
3746 it != oldAtts.end();
3747 ++it)
3748 {
3749 uint32_t level = 0;
3750 MediumAttachment *pAttach = *it;
3751 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3752 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3753 if (pMedium.isNull())
3754 continue;
3755
3756 if (pMedium->i_getBase(&level) == medium)
3757 {
3758 /* skip the hard disk if its currently attached (we
3759 * cannot attach the same hard disk twice) */
3760 if (i_findAttachment(*mMediumAttachments.data(),
3761 pMedium))
3762 continue;
3763
3764 /* matched device, channel and bus (i.e. attached to the
3765 * same place) will win and immediately stop the search;
3766 * otherwise the attachment that has the youngest
3767 * descendant of medium will be used
3768 */
3769 if (pAttach->i_matches(aName, aControllerPort, aDevice))
3770 {
3771 /* the simplest case: restore the whole attachment
3772 * and return, nothing else to do */
3773 mMediumAttachments->push_back(*it);
3774
3775 /* Reattach the medium to the VM. */
3776 if (fHotplug || fSilent)
3777 {
3778 mediumLock.release();
3779 treeLock.release();
3780 alock.release();
3781
3782 MediumLockList *pMediumLockList(new MediumLockList());
3783
3784 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3785 medium /* pToLockWrite */,
3786 false /* fMediumLockWriteAll */,
3787 NULL,
3788 *pMediumLockList);
3789 alock.acquire();
3790 if (FAILED(rc))
3791 delete pMediumLockList;
3792 else
3793 {
3794 mData->mSession.mLockedMedia.Unlock();
3795 alock.release();
3796 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3797 mData->mSession.mLockedMedia.Lock();
3798 alock.acquire();
3799 }
3800 alock.release();
3801
3802 if (SUCCEEDED(rc))
3803 {
3804 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3805 /* Remove lock list in case of error. */
3806 if (FAILED(rc))
3807 {
3808 mData->mSession.mLockedMedia.Unlock();
3809 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3810 mData->mSession.mLockedMedia.Lock();
3811 }
3812 }
3813 }
3814
3815 return S_OK;
3816 }
3817 else if ( foundIt == oldAtts.end()
3818 || level > foundLevel /* prefer younger */
3819 )
3820 {
3821 foundIt = it;
3822 foundLevel = level;
3823 }
3824 }
3825 }
3826
3827 if (foundIt != oldAtts.end())
3828 {
3829 /* use the previously attached hard disk */
3830 medium = (*foundIt)->i_getMedium();
3831 mediumCaller.attach(medium);
3832 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3833 mediumLock.attach(medium);
3834 /* not implicit, doesn't require association with this VM */
3835 fIndirect = false;
3836 associate = false;
3837 /* go right to the MediumAttachment creation */
3838 break;
3839 }
3840 }
3841
3842 /* must give up the medium lock and medium tree lock as below we
3843 * go over snapshots, which needs a lock with higher lock order. */
3844 mediumLock.release();
3845 treeLock.release();
3846
3847 /* then, search through snapshots for the best diff in the given
3848 * hard disk's chain to base the new diff on */
3849
3850 ComObjPtr<Medium> base;
3851 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
3852 while (snap)
3853 {
3854 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
3855
3856 const MediumAttachmentList &snapAtts = *snap->i_getSnapshotMachine()->mMediumAttachments.data();
3857
3858 MediumAttachment *pAttachFound = NULL;
3859 uint32_t foundLevel = 0;
3860
3861 for (MediumAttachmentList::const_iterator
3862 it = snapAtts.begin();
3863 it != snapAtts.end();
3864 ++it)
3865 {
3866 MediumAttachment *pAttach = *it;
3867 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3868 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3869 if (pMedium.isNull())
3870 continue;
3871
3872 uint32_t level = 0;
3873 if (pMedium->i_getBase(&level) == medium)
3874 {
3875 /* matched device, channel and bus (i.e. attached to the
3876 * same place) will win and immediately stop the search;
3877 * otherwise the attachment that has the youngest
3878 * descendant of medium will be used
3879 */
3880 if ( pAttach->i_getDevice() == aDevice
3881 && pAttach->i_getPort() == aControllerPort
3882 && pAttach->i_getControllerName() == aName
3883 )
3884 {
3885 pAttachFound = pAttach;
3886 break;
3887 }
3888 else if ( !pAttachFound
3889 || level > foundLevel /* prefer younger */
3890 )
3891 {
3892 pAttachFound = pAttach;
3893 foundLevel = level;
3894 }
3895 }
3896 }
3897
3898 if (pAttachFound)
3899 {
3900 base = pAttachFound->i_getMedium();
3901 break;
3902 }
3903
3904 snap = snap->i_getParent();
3905 }
3906
3907 /* re-lock medium tree and the medium, as we need it below */
3908 treeLock.acquire();
3909 mediumLock.acquire();
3910
3911 /* found a suitable diff, use it as a base */
3912 if (!base.isNull())
3913 {
3914 medium = base;
3915 mediumCaller.attach(medium);
3916 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3917 mediumLock.attach(medium);
3918 }
3919 }
3920
3921 Utf8Str strFullSnapshotFolder;
3922 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
3923
3924 ComObjPtr<Medium> diff;
3925 diff.createObject();
3926 // store this diff in the same registry as the parent
3927 Guid uuidRegistryParent;
3928 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
3929 {
3930 // parent image has no registry: this can happen if we're attaching a new immutable
3931 // image that has not yet been attached (medium then points to the base and we're
3932 // creating the diff image for the immutable, and the parent is not yet registered);
3933 // put the parent in the machine registry then
3934 mediumLock.release();
3935 treeLock.release();
3936 alock.release();
3937 i_addMediumToRegistry(medium);
3938 alock.acquire();
3939 treeLock.acquire();
3940 mediumLock.acquire();
3941 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
3942 }
3943 rc = diff->init(mParent,
3944 medium->i_getPreferredDiffFormat(),
3945 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
3946 uuidRegistryParent,
3947 DeviceType_HardDisk);
3948 if (FAILED(rc)) return rc;
3949
3950 /* Apply the normal locking logic to the entire chain. */
3951 MediumLockList *pMediumLockList(new MediumLockList());
3952 mediumLock.release();
3953 treeLock.release();
3954 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
3955 diff /* pToLockWrite */,
3956 false /* fMediumLockWriteAll */,
3957 medium,
3958 *pMediumLockList);
3959 treeLock.acquire();
3960 mediumLock.acquire();
3961 if (SUCCEEDED(rc))
3962 {
3963 mediumLock.release();
3964 treeLock.release();
3965 rc = pMediumLockList->Lock();
3966 treeLock.acquire();
3967 mediumLock.acquire();
3968 if (FAILED(rc))
3969 setError(rc,
3970 tr("Could not lock medium when creating diff '%s'"),
3971 diff->i_getLocationFull().c_str());
3972 else
3973 {
3974 /* will release the lock before the potentially lengthy
3975 * operation, so protect with the special state */
3976 MachineState_T oldState = mData->mMachineState;
3977 i_setMachineState(MachineState_SettingUp);
3978
3979 mediumLock.release();
3980 treeLock.release();
3981 alock.release();
3982
3983 rc = medium->i_createDiffStorage(diff,
3984 medium->i_getPreferredDiffVariant(),
3985 pMediumLockList,
3986 NULL /* aProgress */,
3987 true /* aWait */,
3988 false /* aNotify */);
3989
3990 alock.acquire();
3991 treeLock.acquire();
3992 mediumLock.acquire();
3993
3994 i_setMachineState(oldState);
3995 }
3996 }
3997
3998 /* Unlock the media and free the associated memory. */
3999 delete pMediumLockList;
4000
4001 if (FAILED(rc)) return rc;
4002
4003 /* use the created diff for the actual attachment */
4004 medium = diff;
4005 mediumCaller.attach(medium);
4006 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4007 mediumLock.attach(medium);
4008 }
4009 while (0);
4010
4011 ComObjPtr<MediumAttachment> attachment;
4012 attachment.createObject();
4013 rc = attachment->init(this,
4014 medium,
4015 aName,
4016 aControllerPort,
4017 aDevice,
4018 aType,
4019 fIndirect,
4020 false /* fPassthrough */,
4021 false /* fTempEject */,
4022 false /* fNonRotational */,
4023 false /* fDiscard */,
4024 fHotplug /* fHotPluggable */,
4025 Utf8Str::Empty);
4026 if (FAILED(rc)) return rc;
4027
4028 if (associate && !medium.isNull())
4029 {
4030 // as the last step, associate the medium to the VM
4031 rc = medium->i_addBackReference(mData->mUuid);
4032 // here we can fail because of Deleting, or being in process of creating a Diff
4033 if (FAILED(rc)) return rc;
4034
4035 mediumLock.release();
4036 treeLock.release();
4037 alock.release();
4038 i_addMediumToRegistry(medium);
4039 alock.acquire();
4040 treeLock.acquire();
4041 mediumLock.acquire();
4042 }
4043
4044 /* success: finally remember the attachment */
4045 i_setModified(IsModified_Storage);
4046 mMediumAttachments.backup();
4047 mMediumAttachments->push_back(attachment);
4048
4049 mediumLock.release();
4050 treeLock.release();
4051 alock.release();
4052
4053 if (fHotplug || fSilent)
4054 {
4055 if (!medium.isNull())
4056 {
4057 MediumLockList *pMediumLockList(new MediumLockList());
4058
4059 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4060 medium /* pToLockWrite */,
4061 false /* fMediumLockWriteAll */,
4062 NULL,
4063 *pMediumLockList);
4064 alock.acquire();
4065 if (FAILED(rc))
4066 delete pMediumLockList;
4067 else
4068 {
4069 mData->mSession.mLockedMedia.Unlock();
4070 alock.release();
4071 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4072 mData->mSession.mLockedMedia.Lock();
4073 alock.acquire();
4074 }
4075 alock.release();
4076 }
4077
4078 if (SUCCEEDED(rc))
4079 {
4080 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4081 /* Remove lock list in case of error. */
4082 if (FAILED(rc))
4083 {
4084 mData->mSession.mLockedMedia.Unlock();
4085 mData->mSession.mLockedMedia.Remove(attachment);
4086 mData->mSession.mLockedMedia.Lock();
4087 }
4088 }
4089 }
4090
4091 /* Save modified registries, but skip this machine as it's the caller's
4092 * job to save its settings like all other settings changes. */
4093 mParent->i_unmarkRegistryModified(i_getId());
4094 mParent->i_saveModifiedRegistries();
4095
4096 if (SUCCEEDED(rc))
4097 {
4098 if (fIndirect && medium != aM)
4099 mParent->i_onMediumConfigChanged(medium);
4100 mParent->i_onStorageDeviceChanged(attachment, FALSE, fSilent);
4101 }
4102
4103 return rc;
4104}
4105
4106HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4107 LONG aDevice)
4108{
4109 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4110 aName.c_str(), aControllerPort, aDevice));
4111
4112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4113
4114 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4115 if (FAILED(rc)) return rc;
4116
4117 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4118
4119 /* Check for an existing controller. */
4120 ComObjPtr<StorageController> ctl;
4121 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4122 if (FAILED(rc)) return rc;
4123
4124 StorageControllerType_T ctrlType;
4125 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4126 if (FAILED(rc))
4127 return setError(E_FAIL,
4128 tr("Could not get type of controller '%s'"),
4129 aName.c_str());
4130
4131 bool fSilent = false;
4132 Utf8Str strReconfig;
4133
4134 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4135 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4136 if ( mData->mMachineState == MachineState_Paused
4137 && strReconfig == "1")
4138 fSilent = true;
4139
4140 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4141 bool fHotplug = false;
4142 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4143 fHotplug = true;
4144
4145 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4146 return setError(VBOX_E_INVALID_VM_STATE,
4147 tr("Controller '%s' does not support hotplugging"),
4148 aName.c_str());
4149
4150 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4151 aName,
4152 aControllerPort,
4153 aDevice);
4154 if (!pAttach)
4155 return setError(VBOX_E_OBJECT_NOT_FOUND,
4156 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4157 aDevice, aControllerPort, aName.c_str());
4158
4159 if (fHotplug && !pAttach->i_getHotPluggable())
4160 return setError(VBOX_E_NOT_SUPPORTED,
4161 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4162 aDevice, aControllerPort, aName.c_str());
4163
4164 /*
4165 * The VM has to detach the device before we delete any implicit diffs.
4166 * If this fails we can roll back without loosing data.
4167 */
4168 if (fHotplug || fSilent)
4169 {
4170 alock.release();
4171 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4172 alock.acquire();
4173 }
4174 if (FAILED(rc)) return rc;
4175
4176 /* If we are here everything went well and we can delete the implicit now. */
4177 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4178
4179 alock.release();
4180
4181 /* Save modified registries, but skip this machine as it's the caller's
4182 * job to save its settings like all other settings changes. */
4183 mParent->i_unmarkRegistryModified(i_getId());
4184 mParent->i_saveModifiedRegistries();
4185
4186 if (SUCCEEDED(rc))
4187 mParent->i_onStorageDeviceChanged(pAttach, TRUE, fSilent);
4188
4189 return rc;
4190}
4191
4192HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4193 LONG aDevice, BOOL aPassthrough)
4194{
4195 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4196 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4197
4198 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4199
4200 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4201 if (FAILED(rc)) return rc;
4202
4203 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4204
4205 /* Check for an existing controller. */
4206 ComObjPtr<StorageController> ctl;
4207 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4208 if (FAILED(rc)) return rc;
4209
4210 StorageControllerType_T ctrlType;
4211 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4212 if (FAILED(rc))
4213 return setError(E_FAIL,
4214 tr("Could not get type of controller '%s'"),
4215 aName.c_str());
4216
4217 bool fSilent = false;
4218 Utf8Str strReconfig;
4219
4220 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4221 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4222 if ( mData->mMachineState == MachineState_Paused
4223 && strReconfig == "1")
4224 fSilent = true;
4225
4226 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4227 bool fHotplug = false;
4228 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4229 fHotplug = true;
4230
4231 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4232 return setError(VBOX_E_INVALID_VM_STATE,
4233 tr("Controller '%s' does not support hotplugging which is required to change the passthrough setting while the VM is running"),
4234 aName.c_str());
4235
4236 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4237 aName,
4238 aControllerPort,
4239 aDevice);
4240 if (!pAttach)
4241 return setError(VBOX_E_OBJECT_NOT_FOUND,
4242 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4243 aDevice, aControllerPort, aName.c_str());
4244
4245
4246 i_setModified(IsModified_Storage);
4247 mMediumAttachments.backup();
4248
4249 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4250
4251 if (pAttach->i_getType() != DeviceType_DVD)
4252 return setError(E_INVALIDARG,
4253 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4254 aDevice, aControllerPort, aName.c_str());
4255
4256 bool fValueChanged = pAttach->i_getPassthrough() != (aPassthrough != 0);
4257
4258 pAttach->i_updatePassthrough(!!aPassthrough);
4259
4260 attLock.release();
4261 alock.release();
4262 rc = i_onStorageDeviceChange(pAttach, FALSE /* aRemove */, FALSE /* aSilent */);
4263 if (SUCCEEDED(rc) && fValueChanged)
4264 mParent->i_onStorageDeviceChanged(pAttach, FALSE, FALSE);
4265
4266 return rc;
4267}
4268
4269HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4270 LONG aDevice, BOOL aTemporaryEject)
4271{
4272
4273 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4274 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4275
4276 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4277
4278 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4279 if (FAILED(rc)) return rc;
4280
4281 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4282 aName,
4283 aControllerPort,
4284 aDevice);
4285 if (!pAttach)
4286 return setError(VBOX_E_OBJECT_NOT_FOUND,
4287 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4288 aDevice, aControllerPort, aName.c_str());
4289
4290
4291 i_setModified(IsModified_Storage);
4292 mMediumAttachments.backup();
4293
4294 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4295
4296 if (pAttach->i_getType() != DeviceType_DVD)
4297 return setError(E_INVALIDARG,
4298 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4299 aDevice, aControllerPort, aName.c_str());
4300 pAttach->i_updateTempEject(!!aTemporaryEject);
4301
4302 return S_OK;
4303}
4304
4305HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4306 LONG aDevice, BOOL aNonRotational)
4307{
4308
4309 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4310 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4311
4312 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4313
4314 HRESULT rc = i_checkStateDependency(MutableStateDep);
4315 if (FAILED(rc)) return rc;
4316
4317 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4318
4319 if (Global::IsOnlineOrTransient(mData->mMachineState))
4320 return setError(VBOX_E_INVALID_VM_STATE,
4321 tr("Invalid machine state: %s"),
4322 Global::stringifyMachineState(mData->mMachineState));
4323
4324 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4325 aName,
4326 aControllerPort,
4327 aDevice);
4328 if (!pAttach)
4329 return setError(VBOX_E_OBJECT_NOT_FOUND,
4330 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4331 aDevice, aControllerPort, aName.c_str());
4332
4333
4334 i_setModified(IsModified_Storage);
4335 mMediumAttachments.backup();
4336
4337 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4338
4339 if (pAttach->i_getType() != DeviceType_HardDisk)
4340 return setError(E_INVALIDARG,
4341 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4342 aDevice, aControllerPort, aName.c_str());
4343 pAttach->i_updateNonRotational(!!aNonRotational);
4344
4345 return S_OK;
4346}
4347
4348HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4349 LONG aDevice, BOOL aDiscard)
4350{
4351
4352 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4353 aName.c_str(), aControllerPort, aDevice, aDiscard));
4354
4355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4356
4357 HRESULT rc = i_checkStateDependency(MutableStateDep);
4358 if (FAILED(rc)) return rc;
4359
4360 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4361
4362 if (Global::IsOnlineOrTransient(mData->mMachineState))
4363 return setError(VBOX_E_INVALID_VM_STATE,
4364 tr("Invalid machine state: %s"),
4365 Global::stringifyMachineState(mData->mMachineState));
4366
4367 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4368 aName,
4369 aControllerPort,
4370 aDevice);
4371 if (!pAttach)
4372 return setError(VBOX_E_OBJECT_NOT_FOUND,
4373 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4374 aDevice, aControllerPort, aName.c_str());
4375
4376
4377 i_setModified(IsModified_Storage);
4378 mMediumAttachments.backup();
4379
4380 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4381
4382 if (pAttach->i_getType() != DeviceType_HardDisk)
4383 return setError(E_INVALIDARG,
4384 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4385 aDevice, aControllerPort, aName.c_str());
4386 pAttach->i_updateDiscard(!!aDiscard);
4387
4388 return S_OK;
4389}
4390
4391HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4392 LONG aDevice, BOOL aHotPluggable)
4393{
4394 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4395 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4396
4397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4398
4399 HRESULT rc = i_checkStateDependency(MutableStateDep);
4400 if (FAILED(rc)) return rc;
4401
4402 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4403
4404 if (Global::IsOnlineOrTransient(mData->mMachineState))
4405 return setError(VBOX_E_INVALID_VM_STATE,
4406 tr("Invalid machine state: %s"),
4407 Global::stringifyMachineState(mData->mMachineState));
4408
4409 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4410 aName,
4411 aControllerPort,
4412 aDevice);
4413 if (!pAttach)
4414 return setError(VBOX_E_OBJECT_NOT_FOUND,
4415 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4416 aDevice, aControllerPort, aName.c_str());
4417
4418 /* Check for an existing controller. */
4419 ComObjPtr<StorageController> ctl;
4420 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4421 if (FAILED(rc)) return rc;
4422
4423 StorageControllerType_T ctrlType;
4424 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4425 if (FAILED(rc))
4426 return setError(E_FAIL,
4427 tr("Could not get type of controller '%s'"),
4428 aName.c_str());
4429
4430 if (!i_isControllerHotplugCapable(ctrlType))
4431 return setError(VBOX_E_NOT_SUPPORTED,
4432 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4433 aName.c_str());
4434
4435 i_setModified(IsModified_Storage);
4436 mMediumAttachments.backup();
4437
4438 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4439
4440 if (pAttach->i_getType() == DeviceType_Floppy)
4441 return setError(E_INVALIDARG,
4442 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4443 aDevice, aControllerPort, aName.c_str());
4444 pAttach->i_updateHotPluggable(!!aHotPluggable);
4445
4446 return S_OK;
4447}
4448
4449HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4450 LONG aDevice)
4451{
4452 int rc = S_OK;
4453 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4454 aName.c_str(), aControllerPort, aDevice));
4455
4456 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4457
4458 return rc;
4459}
4460
4461HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4462 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4463{
4464 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4465 aName.c_str(), aControllerPort, aDevice));
4466
4467 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4468
4469 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4470 if (FAILED(rc)) return rc;
4471
4472 if (Global::IsOnlineOrTransient(mData->mMachineState))
4473 return setError(VBOX_E_INVALID_VM_STATE,
4474 tr("Invalid machine state: %s"),
4475 Global::stringifyMachineState(mData->mMachineState));
4476
4477 MediumAttachment *pAttach = i_findAttachment(*mMediumAttachments.data(),
4478 aName,
4479 aControllerPort,
4480 aDevice);
4481 if (!pAttach)
4482 return setError(VBOX_E_OBJECT_NOT_FOUND,
4483 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4484 aDevice, aControllerPort, aName.c_str());
4485
4486
4487 i_setModified(IsModified_Storage);
4488 mMediumAttachments.backup();
4489
4490 IBandwidthGroup *iB = aBandwidthGroup;
4491 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4492 if (aBandwidthGroup && group.isNull())
4493 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4494
4495 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4496
4497 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4498 if (strBandwidthGroupOld.isNotEmpty())
4499 {
4500 /* Get the bandwidth group object and release it - this must not fail. */
4501 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4502 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4503 Assert(SUCCEEDED(rc));
4504
4505 pBandwidthGroupOld->i_release();
4506 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4507 }
4508
4509 if (!group.isNull())
4510 {
4511 group->i_reference();
4512 pAttach->i_updateBandwidthGroup(group->i_getName());
4513 }
4514
4515 return S_OK;
4516}
4517
4518HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4519 LONG aControllerPort,
4520 LONG aDevice,
4521 DeviceType_T aType)
4522{
4523 HRESULT rc = S_OK;
4524
4525 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d\n",
4526 aName.c_str(), aControllerPort, aDevice, aType));
4527
4528 rc = attachDevice(aName, aControllerPort, aDevice, aType, NULL);
4529
4530 return rc;
4531}
4532
4533
4534HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4535 LONG aControllerPort,
4536 LONG aDevice,
4537 BOOL aForce)
4538{
4539 int rc = S_OK;
4540 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4541 aName.c_str(), aControllerPort, aForce));
4542
4543 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4544
4545 return rc;
4546}
4547
4548HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4549 LONG aControllerPort,
4550 LONG aDevice,
4551 const ComPtr<IMedium> &aMedium,
4552 BOOL aForce)
4553{
4554 int rc = S_OK;
4555 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4556 aName.c_str(), aControllerPort, aDevice, aForce));
4557
4558 // request the host lock first, since might be calling Host methods for getting host drives;
4559 // next, protect the media tree all the while we're in here, as well as our member variables
4560 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4561 this->lockHandle(),
4562 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4563
4564 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4565 aName,
4566 aControllerPort,
4567 aDevice);
4568 if (pAttach.isNull())
4569 return setError(VBOX_E_OBJECT_NOT_FOUND,
4570 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4571 aDevice, aControllerPort, aName.c_str());
4572
4573 /* Remember previously mounted medium. The medium before taking the
4574 * backup is not necessarily the same thing. */
4575 ComObjPtr<Medium> oldmedium;
4576 oldmedium = pAttach->i_getMedium();
4577
4578 IMedium *iM = aMedium;
4579 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4580 if (aMedium && pMedium.isNull())
4581 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4582
4583 AutoCaller mediumCaller(pMedium);
4584 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4585
4586 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4587 if (pMedium)
4588 {
4589 DeviceType_T mediumType = pAttach->i_getType();
4590 switch (mediumType)
4591 {
4592 case DeviceType_DVD:
4593 case DeviceType_Floppy:
4594 break;
4595
4596 default:
4597 return setError(VBOX_E_INVALID_OBJECT_STATE,
4598 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4599 aControllerPort,
4600 aDevice,
4601 aName.c_str());
4602 }
4603 }
4604
4605 i_setModified(IsModified_Storage);
4606 mMediumAttachments.backup();
4607
4608 {
4609 // The backup operation makes the pAttach reference point to the
4610 // old settings. Re-get the correct reference.
4611 pAttach = i_findAttachment(*mMediumAttachments.data(),
4612 aName,
4613 aControllerPort,
4614 aDevice);
4615 if (!oldmedium.isNull())
4616 oldmedium->i_removeBackReference(mData->mUuid);
4617 if (!pMedium.isNull())
4618 {
4619 pMedium->i_addBackReference(mData->mUuid);
4620
4621 mediumLock.release();
4622 multiLock.release();
4623 i_addMediumToRegistry(pMedium);
4624 multiLock.acquire();
4625 mediumLock.acquire();
4626 }
4627
4628 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4629 pAttach->i_updateMedium(pMedium);
4630 }
4631
4632 i_setModified(IsModified_Storage);
4633
4634 mediumLock.release();
4635 multiLock.release();
4636 rc = i_onMediumChange(pAttach, aForce);
4637 multiLock.acquire();
4638 mediumLock.acquire();
4639
4640 /* On error roll back this change only. */
4641 if (FAILED(rc))
4642 {
4643 if (!pMedium.isNull())
4644 pMedium->i_removeBackReference(mData->mUuid);
4645 pAttach = i_findAttachment(*mMediumAttachments.data(),
4646 aName,
4647 aControllerPort,
4648 aDevice);
4649 /* If the attachment is gone in the meantime, bail out. */
4650 if (pAttach.isNull())
4651 return rc;
4652 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4653 if (!oldmedium.isNull())
4654 oldmedium->i_addBackReference(mData->mUuid);
4655 pAttach->i_updateMedium(oldmedium);
4656 }
4657
4658 mediumLock.release();
4659 multiLock.release();
4660
4661 /* Save modified registries, but skip this machine as it's the caller's
4662 * job to save its settings like all other settings changes. */
4663 mParent->i_unmarkRegistryModified(i_getId());
4664 mParent->i_saveModifiedRegistries();
4665
4666 return rc;
4667}
4668HRESULT Machine::getMedium(const com::Utf8Str &aName,
4669 LONG aControllerPort,
4670 LONG aDevice,
4671 ComPtr<IMedium> &aMedium)
4672{
4673 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4674 aName.c_str(), aControllerPort, aDevice));
4675
4676 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4677
4678 aMedium = NULL;
4679
4680 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
4681 aName,
4682 aControllerPort,
4683 aDevice);
4684 if (pAttach.isNull())
4685 return setError(VBOX_E_OBJECT_NOT_FOUND,
4686 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4687 aDevice, aControllerPort, aName.c_str());
4688
4689 aMedium = pAttach->i_getMedium();
4690
4691 return S_OK;
4692}
4693
4694HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4695{
4696
4697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4698
4699 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4700
4701 return S_OK;
4702}
4703
4704HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4705{
4706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4707
4708 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4709
4710 return S_OK;
4711}
4712
4713HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4714{
4715 /* Do not assert if slot is out of range, just return the advertised
4716 status. testdriver/vbox.py triggers this in logVmInfo. */
4717 if (aSlot >= mNetworkAdapters.size())
4718 return setError(E_INVALIDARG,
4719 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4720 aSlot, mNetworkAdapters.size());
4721
4722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4723
4724 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4725
4726 return S_OK;
4727}
4728
4729HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4730{
4731 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4732
4733 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4734 size_t i = 0;
4735 for (settings::StringsMap::const_iterator
4736 it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4737 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4738 ++it, ++i)
4739 aKeys[i] = it->first;
4740
4741 return S_OK;
4742}
4743
4744 /**
4745 * @note Locks this object for reading.
4746 */
4747HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4748 com::Utf8Str &aValue)
4749{
4750 /* start with nothing found */
4751 aValue = "";
4752
4753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4754
4755 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4756 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4757 // found:
4758 aValue = it->second; // source is a Utf8Str
4759
4760 /* return the result to caller (may be empty) */
4761 return S_OK;
4762}
4763
4764 /**
4765 * @note Locks mParent for writing + this object for writing.
4766 */
4767HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4768{
4769 /* Because control characters in aKey have caused problems in the settings
4770 * they are rejected unless the key should be deleted. */
4771 if (!aValue.isEmpty())
4772 {
4773 for (size_t i = 0; i < aKey.length(); ++i)
4774 {
4775 char ch = aKey[i];
4776 if (RTLocCIsCntrl(ch))
4777 return E_INVALIDARG;
4778 }
4779 }
4780
4781 Utf8Str strOldValue; // empty
4782
4783 // locking note: we only hold the read lock briefly to look up the old value,
4784 // then release it and call the onExtraCanChange callbacks. There is a small
4785 // chance of a race insofar as the callback might be called twice if two callers
4786 // change the same key at the same time, but that's a much better solution
4787 // than the deadlock we had here before. The actual changing of the extradata
4788 // is then performed under the write lock and race-free.
4789
4790 // look up the old value first; if nothing has changed then we need not do anything
4791 {
4792 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4793
4794 // For snapshots don't even think about allowing changes, extradata
4795 // is global for a machine, so there is nothing snapshot specific.
4796 if (i_isSnapshotMachine())
4797 return setError(VBOX_E_INVALID_VM_STATE,
4798 tr("Cannot set extradata for a snapshot"));
4799
4800 // check if the right IMachine instance is used
4801 if (mData->mRegistered && !i_isSessionMachine())
4802 return setError(VBOX_E_INVALID_VM_STATE,
4803 tr("Cannot set extradata for an immutable machine"));
4804
4805 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4806 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4807 strOldValue = it->second;
4808 }
4809
4810 bool fChanged;
4811 if ((fChanged = (strOldValue != aValue)))
4812 {
4813 // ask for permission from all listeners outside the locks;
4814 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4815 // lock to copy the list of callbacks to invoke
4816 Bstr error;
4817 Bstr bstrValue(aValue);
4818
4819 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4820 {
4821 const char *sep = error.isEmpty() ? "" : ": ";
4822 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, error.raw()));
4823 return setError(E_ACCESSDENIED,
4824 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4825 aKey.c_str(),
4826 aValue.c_str(),
4827 sep,
4828 error.raw());
4829 }
4830
4831 // data is changing and change not vetoed: then write it out under the lock
4832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4833
4834 if (aValue.isEmpty())
4835 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4836 else
4837 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4838 // creates a new key if needed
4839
4840 bool fNeedsGlobalSaveSettings = false;
4841 // This saving of settings is tricky: there is no "old state" for the
4842 // extradata items at all (unlike all other settings), so the old/new
4843 // settings comparison would give a wrong result!
4844 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4845
4846 if (fNeedsGlobalSaveSettings)
4847 {
4848 // save the global settings; for that we should hold only the VirtualBox lock
4849 alock.release();
4850 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4851 mParent->i_saveSettings();
4852 }
4853 }
4854
4855 // fire notification outside the lock
4856 if (fChanged)
4857 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4858
4859 return S_OK;
4860}
4861
4862HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4863{
4864 aProgress = NULL;
4865 NOREF(aSettingsFilePath);
4866 ReturnComNotImplemented();
4867}
4868
4869HRESULT Machine::saveSettings()
4870{
4871 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4872
4873 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4874 if (FAILED(rc)) return rc;
4875
4876 /* the settings file path may never be null */
4877 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
4878
4879 /* save all VM data excluding snapshots */
4880 bool fNeedsGlobalSaveSettings = false;
4881 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
4882 mlock.release();
4883
4884 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
4885 {
4886 // save the global settings; for that we should hold only the VirtualBox lock
4887 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
4888 rc = mParent->i_saveSettings();
4889 }
4890
4891 return rc;
4892}
4893
4894
4895HRESULT Machine::discardSettings()
4896{
4897 /*
4898 * We need to take the machine list lock here as well as the machine one
4899 * or we'll get into trouble should any media stuff require rolling back.
4900 *
4901 * Details:
4902 *
4903 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Wrong locking order! [uId=00007ff6853f6c34 thrd=ALIEN-1]
4904 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Lock: s 000000000259ef40 RTCritSectRw-3 srec=000000000259f150 cls=4-LISTOFMACHINES/any [s]
4905 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other lock: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x]
4906 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: class=0000000000d5eb10 4-LISTOFMACHINES created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4907 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 4 lookups
4908 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: #01: 3-HOSTOBJECT, manually , 0 lookups
4909 * 11:06:01.934284 00:00:05.805182 ALIEN-1 My class: Hash Stats: 3 hits, 1 misses
4910 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: class=0000000000d5ecd0 5-MACHINEOBJECT created={AutoLock.cpp(98) util::InitAutoLockSystem 00007ff6853f571f} sub-class=any
4911 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Prior: #00: 2-VIRTUALBOXOBJECT, manually , 2 lookups
4912 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #01: 3-HOSTOBJECT, manually , 6 lookups
4913 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: #02: 4-LISTOFMACHINES, manually , 5 lookups
4914 * 11:06:01.934284 00:00:05.805182 ALIEN-1 Other class: Hash Stats: 10 hits, 3 misses
4915 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- start of lock stack for 000000000259d2d0 ALIEN-1 - 2 entries ----
4916 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #00: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=2 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(11705) Machine::i_rollback 00007ff6853f6ce4} [x/r]
4917 * 11:06:01.934284 00:00:05.805182 ALIEN-1 #01: 00000000025ec710 RTCritSectRw-158 own=ALIEN-1 r=1 cls=5-MACHINEOBJECT/any pos={MachineImpl.cpp(5085) Machine::discardSettings 00007ff6853f6ce4} [x] (*)
4918 * 11:06:01.934284 00:00:05.805182 ALIEN-1 ---- end of lock stack ----
4919 * 0:005> k
4920 * # Child-SP RetAddr Call Site
4921 * 00 00000000`0287bc90 00007ffc`8c0bc8dc VBoxRT!rtLockValComplainPanic+0x23 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 807]
4922 * 01 00000000`0287bcc0 00007ffc`8c0bc083 VBoxRT!rtLockValidatorStackWrongOrder+0xac [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2149]
4923 * 02 00000000`0287bd10 00007ffc`8c0bbfc3 VBoxRT!rtLockValidatorStackCheckLockingOrder2+0x93 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2227]
4924 * 03 00000000`0287bdd0 00007ffc`8c0bf3c0 VBoxRT!rtLockValidatorStackCheckLockingOrder+0x523 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 2406]
4925 * 04 00000000`0287be40 00007ffc`8c180de4 VBoxRT!RTLockValidatorRecSharedCheckOrder+0x210 [e:\vbox\svn\trunk\src\vbox\runtime\common\misc\lockvalidator.cpp @ 3607]
4926 * 05 00000000`0287be90 00007ffc`8c1819b8 VBoxRT!rtCritSectRwEnterShared+0x1a4 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 222]
4927 * 06 00000000`0287bf60 00007ff6`853f5e78 VBoxRT!RTCritSectRwEnterSharedDebug+0x58 [e:\vbox\svn\trunk\src\vbox\runtime\generic\critsectrw-generic.cpp @ 428]
4928 * 07 00000000`0287bfb0 00007ff6`853f6c34 VBoxSVC!util::RWLockHandle::lockRead+0x58 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 245]
4929 * 08 00000000`0287c000 00007ff6`853f68a1 VBoxSVC!util::AutoReadLock::callLockImpl+0x64 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 552]
4930 * 09 00000000`0287c040 00007ff6`853f6a59 VBoxSVC!util::AutoLockBase::callLockOnAllHandles+0xa1 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 455]
4931 * 0a 00000000`0287c0a0 00007ff6`85038fdb VBoxSVC!util::AutoLockBase::acquire+0x89 [e:\vbox\svn\trunk\src\vbox\main\glue\autolock.cpp @ 500]
4932 * 0b 00000000`0287c0d0 00007ff6`85216dcf VBoxSVC!util::AutoReadLock::AutoReadLock+0x7b [e:\vbox\svn\trunk\include\vbox\com\autolock.h @ 370]
4933 * 0c 00000000`0287c120 00007ff6`8521cf08 VBoxSVC!VirtualBox::i_findMachine+0x14f [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 3216]
4934 * 0d 00000000`0287c260 00007ff6`8517a4b0 VBoxSVC!VirtualBox::i_markRegistryModified+0xa8 [e:\vbox\svn\trunk\src\vbox\main\src-server\virtualboximpl.cpp @ 4697]
4935 * 0e 00000000`0287c2f0 00007ff6`8517fac0 VBoxSVC!Medium::i_markRegistriesModified+0x170 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 4056]
4936 * 0f 00000000`0287c500 00007ff6`8511ca9d VBoxSVC!Medium::i_deleteStorage+0xb90 [e:\vbox\svn\trunk\src\vbox\main\src-server\mediumimpl.cpp @ 5114]
4937 * 10 00000000`0287cad0 00007ff6`8511ef0e VBoxSVC!Machine::i_deleteImplicitDiffs+0x11ed [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11117]
4938 * 11 00000000`0287d2e0 00007ff6`8511f896 VBoxSVC!Machine::i_rollbackMedia+0x42e [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11657]
4939 * 12 00000000`0287d3c0 00007ff6`850fd17a VBoxSVC!Machine::i_rollback+0x6a6 [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 11786]
4940 * 13 00000000`0287d710 00007ff6`85342dbe VBoxSVC!Machine::discardSettings+0x9a [e:\vbox\svn\trunk\src\vbox\main\src-server\machineimpl.cpp @ 5096]
4941 * 14 00000000`0287d790 00007ffc`c06813ff VBoxSVC!MachineWrap::DiscardSettings+0x16e [e:\vbox\svn\trunk\out\win.amd64\debug\obj\vboxapiwrap\machinewrap.cpp @ 9171]
4942 *
4943 */
4944 AutoReadLock alockMachines(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
4945 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4946
4947 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4948 if (FAILED(rc)) return rc;
4949
4950 /*
4951 * during this rollback, the session will be notified if data has
4952 * been actually changed
4953 */
4954 i_rollback(true /* aNotify */);
4955
4956 return S_OK;
4957}
4958
4959/** @note Locks objects! */
4960HRESULT Machine::unregister(AutoCaller &autoCaller,
4961 CleanupMode_T aCleanupMode,
4962 std::vector<ComPtr<IMedium> > &aMedia)
4963{
4964 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4965
4966 Guid id(i_getId());
4967
4968 if (mData->mSession.mState != SessionState_Unlocked)
4969 return setError(VBOX_E_INVALID_OBJECT_STATE,
4970 tr("Cannot unregister the machine '%s' while it is locked"),
4971 mUserData->s.strName.c_str());
4972
4973 // wait for state dependents to drop to zero
4974 i_ensureNoStateDependencies();
4975
4976 if (!mData->mAccessible)
4977 {
4978 // inaccessible maschines can only be unregistered; uninitialize ourselves
4979 // here because currently there may be no unregistered that are inaccessible
4980 // (this state combination is not supported). Note releasing the caller and
4981 // leaving the lock before calling uninit()
4982 alock.release();
4983 autoCaller.release();
4984
4985 uninit();
4986
4987 mParent->i_unregisterMachine(this, id);
4988 // calls VirtualBox::i_saveSettings()
4989
4990 return S_OK;
4991 }
4992
4993 HRESULT rc = S_OK;
4994
4995 /// @todo r=klaus this is stupid... why is the saved state always deleted?
4996 // discard saved state
4997 if (mData->mMachineState == MachineState_Saved)
4998 {
4999 // add the saved state file to the list of files the caller should delete
5000 Assert(!mSSData->strStateFilePath.isEmpty());
5001 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5002
5003 mSSData->strStateFilePath.setNull();
5004
5005 // unconditionally set the machine state to powered off, we now
5006 // know no session has locked the machine
5007 mData->mMachineState = MachineState_PoweredOff;
5008 }
5009
5010 size_t cSnapshots = 0;
5011 if (mData->mFirstSnapshot)
5012 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5013 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5014 // fail now before we start detaching media
5015 return setError(VBOX_E_INVALID_OBJECT_STATE,
5016 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5017 mUserData->s.strName.c_str(), cSnapshots);
5018
5019 // This list collects the medium objects from all medium attachments
5020 // which we will detach from the machine and its snapshots, in a specific
5021 // order which allows for closing all media without getting "media in use"
5022 // errors, simply by going through the list from the front to the back:
5023 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5024 // and must be closed before the parent media from the snapshots, or closing the parents
5025 // will fail because they still have children);
5026 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5027 // the root ("first") snapshot of the machine.
5028 MediaList llMedia;
5029
5030 if ( !mMediumAttachments.isNull() // can be NULL if machine is inaccessible
5031 && mMediumAttachments->size()
5032 )
5033 {
5034 // we have media attachments: detach them all and add the Medium objects to our list
5035 if (aCleanupMode != CleanupMode_UnregisterOnly)
5036 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5037 else
5038 return setError(VBOX_E_INVALID_OBJECT_STATE,
5039 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5040 mUserData->s.strName.c_str(), mMediumAttachments->size());
5041 }
5042
5043 if (cSnapshots)
5044 {
5045 // add the media from the medium attachments of the snapshots to llMedia
5046 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5047 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5048 // into the children first
5049
5050 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5051 MachineState_T oldState = mData->mMachineState;
5052 mData->mMachineState = MachineState_DeletingSnapshot;
5053
5054 // make a copy of the first snapshot so the refcount does not drop to 0
5055 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5056 // because of the AutoCaller voodoo)
5057 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5058
5059 // GO!
5060 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5061
5062 mData->mMachineState = oldState;
5063 }
5064
5065 if (FAILED(rc))
5066 {
5067 i_rollbackMedia();
5068 return rc;
5069 }
5070
5071 // commit all the media changes made above
5072 i_commitMedia();
5073
5074 mData->mRegistered = false;
5075
5076 // machine lock no longer needed
5077 alock.release();
5078
5079 // return media to caller
5080 aMedia.resize(llMedia.size());
5081 size_t i = 0;
5082 for (MediaList::const_iterator
5083 it = llMedia.begin();
5084 it != llMedia.end();
5085 ++it, ++i)
5086 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5087
5088 mParent->i_unregisterMachine(this, id);
5089 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5090
5091 return S_OK;
5092}
5093
5094/**
5095 * Task record for deleting a machine config.
5096 */
5097class Machine::DeleteConfigTask
5098 : public Machine::Task
5099{
5100public:
5101 DeleteConfigTask(Machine *m,
5102 Progress *p,
5103 const Utf8Str &t,
5104 const RTCList<ComPtr<IMedium> > &llMediums,
5105 const StringsList &llFilesToDelete)
5106 : Task(m, p, t),
5107 m_llMediums(llMediums),
5108 m_llFilesToDelete(llFilesToDelete)
5109 {}
5110
5111private:
5112 void handler()
5113 {
5114 try
5115 {
5116 m_pMachine->i_deleteConfigHandler(*this);
5117 }
5118 catch (...)
5119 {
5120 LogRel(("Some exception in the function Machine::i_deleteConfigHandler()\n"));
5121 }
5122 }
5123
5124 RTCList<ComPtr<IMedium> > m_llMediums;
5125 StringsList m_llFilesToDelete;
5126
5127 friend void Machine::i_deleteConfigHandler(DeleteConfigTask &task);
5128};
5129
5130/**
5131 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5132 * SessionMachine::taskHandler().
5133 *
5134 * @note Locks this object for writing.
5135 *
5136 * @param task
5137 * @return
5138 */
5139void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5140{
5141 LogFlowThisFuncEnter();
5142
5143 AutoCaller autoCaller(this);
5144 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5145 if (FAILED(autoCaller.rc()))
5146 {
5147 /* we might have been uninitialized because the session was accidentally
5148 * closed by the client, so don't assert */
5149 HRESULT rc = setError(E_FAIL,
5150 tr("The session has been accidentally closed"));
5151 task.m_pProgress->i_notifyComplete(rc);
5152 LogFlowThisFuncLeave();
5153 return;
5154 }
5155
5156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5157
5158 HRESULT rc = S_OK;
5159
5160 try
5161 {
5162 ULONG uLogHistoryCount = 3;
5163 ComPtr<ISystemProperties> systemProperties;
5164 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5165 if (FAILED(rc)) throw rc;
5166
5167 if (!systemProperties.isNull())
5168 {
5169 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5170 if (FAILED(rc)) throw rc;
5171 }
5172
5173 MachineState_T oldState = mData->mMachineState;
5174 i_setMachineState(MachineState_SettingUp);
5175 alock.release();
5176 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5177 {
5178 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5179 {
5180 AutoCaller mac(pMedium);
5181 if (FAILED(mac.rc())) throw mac.rc();
5182 Utf8Str strLocation = pMedium->i_getLocationFull();
5183 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5184 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5185 if (FAILED(rc)) throw rc;
5186 }
5187 if (pMedium->i_isMediumFormatFile())
5188 {
5189 ComPtr<IProgress> pProgress2;
5190 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5191 if (FAILED(rc)) throw rc;
5192 rc = task.m_pProgress->WaitForOtherProgressCompletion(pProgress2, 0 /* indefinite wait */);
5193 if (FAILED(rc)) throw rc;
5194 }
5195
5196 /* Close the medium, deliberately without checking the return
5197 * code, and without leaving any trace in the error info, as
5198 * a failure here is a very minor issue, which shouldn't happen
5199 * as above we even managed to delete the medium. */
5200 {
5201 ErrorInfoKeeper eik;
5202 pMedium->Close();
5203 }
5204 }
5205 i_setMachineState(oldState);
5206 alock.acquire();
5207
5208 // delete the files pushed on the task list by Machine::Delete()
5209 // (this includes saved states of the machine and snapshots and
5210 // medium storage files from the IMedium list passed in, and the
5211 // machine XML file)
5212 for (StringsList::const_iterator
5213 it = task.m_llFilesToDelete.begin();
5214 it != task.m_llFilesToDelete.end();
5215 ++it)
5216 {
5217 const Utf8Str &strFile = *it;
5218 LogFunc(("Deleting file %s\n", strFile.c_str()));
5219 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5220 if (FAILED(rc)) throw rc;
5221
5222 int vrc = RTFileDelete(strFile.c_str());
5223 if (RT_FAILURE(vrc))
5224 throw setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
5225 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5226 }
5227
5228 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5229 if (FAILED(rc)) throw rc;
5230
5231 /* delete the settings only when the file actually exists */
5232 if (mData->pMachineConfigFile->fileExists())
5233 {
5234 /* Delete any backup or uncommitted XML files. Ignore failures.
5235 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5236 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5237 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5238 RTFileDelete(otherXml.c_str());
5239 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5240 RTFileDelete(otherXml.c_str());
5241
5242 /* delete the Logs folder, nothing important should be left
5243 * there (we don't check for errors because the user might have
5244 * some private files there that we don't want to delete) */
5245 Utf8Str logFolder;
5246 getLogFolder(logFolder);
5247 Assert(logFolder.length());
5248 if (RTDirExists(logFolder.c_str()))
5249 {
5250 /* Delete all VBox.log[.N] files from the Logs folder
5251 * (this must be in sync with the rotation logic in
5252 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5253 * files that may have been created by the GUI. */
5254 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5255 logFolder.c_str(), RTPATH_DELIMITER);
5256 RTFileDelete(log.c_str());
5257 log = Utf8StrFmt("%s%cVBox.png",
5258 logFolder.c_str(), RTPATH_DELIMITER);
5259 RTFileDelete(log.c_str());
5260 for (int i = uLogHistoryCount; i > 0; i--)
5261 {
5262 log = Utf8StrFmt("%s%cVBox.log.%d",
5263 logFolder.c_str(), RTPATH_DELIMITER, i);
5264 RTFileDelete(log.c_str());
5265 log = Utf8StrFmt("%s%cVBox.png.%d",
5266 logFolder.c_str(), RTPATH_DELIMITER, i);
5267 RTFileDelete(log.c_str());
5268 }
5269#if defined(RT_OS_WINDOWS)
5270 log = Utf8StrFmt("%s%cVBoxStartup.log", logFolder.c_str(), RTPATH_DELIMITER);
5271 RTFileDelete(log.c_str());
5272 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5273 RTFileDelete(log.c_str());
5274#endif
5275
5276 RTDirRemove(logFolder.c_str());
5277 }
5278
5279 /* delete the Snapshots folder, nothing important should be left
5280 * there (we don't check for errors because the user might have
5281 * some private files there that we don't want to delete) */
5282 Utf8Str strFullSnapshotFolder;
5283 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5284 Assert(!strFullSnapshotFolder.isEmpty());
5285 if (RTDirExists(strFullSnapshotFolder.c_str()))
5286 RTDirRemove(strFullSnapshotFolder.c_str());
5287
5288 // delete the directory that contains the settings file, but only
5289 // if it matches the VM name
5290 Utf8Str settingsDir;
5291 if (i_isInOwnDir(&settingsDir))
5292 RTDirRemove(settingsDir.c_str());
5293 }
5294
5295 alock.release();
5296
5297 mParent->i_saveModifiedRegistries();
5298 }
5299 catch (HRESULT aRC) { rc = aRC; }
5300
5301 task.m_pProgress->i_notifyComplete(rc);
5302
5303 LogFlowThisFuncLeave();
5304}
5305
5306HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5307{
5308 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5309
5310 HRESULT rc = i_checkStateDependency(MutableStateDep);
5311 if (FAILED(rc)) return rc;
5312
5313 if (mData->mRegistered)
5314 return setError(VBOX_E_INVALID_VM_STATE,
5315 tr("Cannot delete settings of a registered machine"));
5316
5317 // collect files to delete
5318 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5319 if (mData->pMachineConfigFile->fileExists())
5320 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5321
5322 RTCList<ComPtr<IMedium> > llMediums;
5323 for (size_t i = 0; i < aMedia.size(); ++i)
5324 {
5325 IMedium *pIMedium(aMedia[i]);
5326 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5327 if (pMedium.isNull())
5328 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5329 SafeArray<BSTR> ids;
5330 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5331 if (FAILED(rc)) return rc;
5332 /* At this point the medium should not have any back references
5333 * anymore. If it has it is attached to another VM and *must* not
5334 * deleted. */
5335 if (ids.size() < 1)
5336 llMediums.append(pMedium);
5337 }
5338
5339 ComObjPtr<Progress> pProgress;
5340 pProgress.createObject();
5341 rc = pProgress->init(i_getVirtualBox(),
5342 static_cast<IMachine*>(this) /* aInitiator */,
5343 tr("Deleting files"),
5344 true /* fCancellable */,
5345 (ULONG)(1 + llMediums.size() + llFilesToDelete.size() + 1), // cOperations
5346 tr("Collecting file inventory"));
5347 if (FAILED(rc))
5348 return rc;
5349
5350 /* create and start the task on a separate thread (note that it will not
5351 * start working until we release alock) */
5352 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5353 rc = pTask->createThread();
5354 pTask = NULL;
5355 if (FAILED(rc))
5356 return rc;
5357
5358 pProgress.queryInterfaceTo(aProgress.asOutParam());
5359
5360 LogFlowFuncLeave();
5361
5362 return S_OK;
5363}
5364
5365HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5366{
5367 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5368
5369 ComObjPtr<Snapshot> pSnapshot;
5370 HRESULT rc;
5371
5372 if (aNameOrId.isEmpty())
5373 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5374 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5375 else
5376 {
5377 Guid uuid(aNameOrId);
5378 if (uuid.isValid())
5379 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5380 else
5381 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5382 }
5383 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5384
5385 return rc;
5386}
5387
5388HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable,
5389 BOOL aAutomount, const com::Utf8Str &aAutoMountPoint)
5390{
5391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5392
5393 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5394 if (FAILED(rc)) return rc;
5395
5396 ComObjPtr<SharedFolder> sharedFolder;
5397 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5398 if (SUCCEEDED(rc))
5399 return setError(VBOX_E_OBJECT_IN_USE,
5400 tr("Shared folder named '%s' already exists"),
5401 aName.c_str());
5402
5403 sharedFolder.createObject();
5404 rc = sharedFolder->init(i_getMachine(),
5405 aName,
5406 aHostPath,
5407 !!aWritable,
5408 !!aAutomount,
5409 aAutoMountPoint,
5410 true /* fFailOnError */);
5411 if (FAILED(rc)) return rc;
5412
5413 i_setModified(IsModified_SharedFolders);
5414 mHWData.backup();
5415 mHWData->mSharedFolders.push_back(sharedFolder);
5416
5417 /* inform the direct session if any */
5418 alock.release();
5419 i_onSharedFolderChange();
5420
5421 return S_OK;
5422}
5423
5424HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5425{
5426 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5427
5428 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5429 if (FAILED(rc)) return rc;
5430
5431 ComObjPtr<SharedFolder> sharedFolder;
5432 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5433 if (FAILED(rc)) return rc;
5434
5435 i_setModified(IsModified_SharedFolders);
5436 mHWData.backup();
5437 mHWData->mSharedFolders.remove(sharedFolder);
5438
5439 /* inform the direct session if any */
5440 alock.release();
5441 i_onSharedFolderChange();
5442
5443 return S_OK;
5444}
5445
5446HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5447{
5448 /* start with No */
5449 *aCanShow = FALSE;
5450
5451 ComPtr<IInternalSessionControl> directControl;
5452 {
5453 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5454
5455 if (mData->mSession.mState != SessionState_Locked)
5456 return setError(VBOX_E_INVALID_VM_STATE,
5457 tr("Machine is not locked for session (session state: %s)"),
5458 Global::stringifySessionState(mData->mSession.mState));
5459
5460 if (mData->mSession.mLockType == LockType_VM)
5461 directControl = mData->mSession.mDirectControl;
5462 }
5463
5464 /* ignore calls made after #OnSessionEnd() is called */
5465 if (!directControl)
5466 return S_OK;
5467
5468 LONG64 dummy;
5469 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5470}
5471
5472HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5473{
5474 ComPtr<IInternalSessionControl> directControl;
5475 {
5476 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5477
5478 if (mData->mSession.mState != SessionState_Locked)
5479 return setError(E_FAIL,
5480 tr("Machine is not locked for session (session state: %s)"),
5481 Global::stringifySessionState(mData->mSession.mState));
5482
5483 if (mData->mSession.mLockType == LockType_VM)
5484 directControl = mData->mSession.mDirectControl;
5485 }
5486
5487 /* ignore calls made after #OnSessionEnd() is called */
5488 if (!directControl)
5489 return S_OK;
5490
5491 BOOL dummy;
5492 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5493}
5494
5495#ifdef VBOX_WITH_GUEST_PROPS
5496/**
5497 * Look up a guest property in VBoxSVC's internal structures.
5498 */
5499HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5500 com::Utf8Str &aValue,
5501 LONG64 *aTimestamp,
5502 com::Utf8Str &aFlags) const
5503{
5504 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5505
5506 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5507 if (it != mHWData->mGuestProperties.end())
5508 {
5509 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5510 aValue = it->second.strValue;
5511 *aTimestamp = it->second.mTimestamp;
5512 GuestPropWriteFlags(it->second.mFlags, szFlags);
5513 aFlags = Utf8Str(szFlags);
5514 }
5515
5516 return S_OK;
5517}
5518
5519/**
5520 * Query the VM that a guest property belongs to for the property.
5521 * @returns E_ACCESSDENIED if the VM process is not available or not
5522 * currently handling queries and the lookup should then be done in
5523 * VBoxSVC.
5524 */
5525HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5526 com::Utf8Str &aValue,
5527 LONG64 *aTimestamp,
5528 com::Utf8Str &aFlags) const
5529{
5530 HRESULT rc = S_OK;
5531 Bstr bstrValue;
5532 Bstr bstrFlags;
5533
5534 ComPtr<IInternalSessionControl> directControl;
5535 {
5536 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5537 if (mData->mSession.mLockType == LockType_VM)
5538 directControl = mData->mSession.mDirectControl;
5539 }
5540
5541 /* ignore calls made after #OnSessionEnd() is called */
5542 if (!directControl)
5543 rc = E_ACCESSDENIED;
5544 else
5545 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr::Empty.raw(), Bstr::Empty.raw(),
5546 0 /* accessMode */,
5547 bstrValue.asOutParam(), aTimestamp, bstrFlags.asOutParam());
5548
5549 aValue = bstrValue;
5550 aFlags = bstrFlags;
5551
5552 return rc;
5553}
5554#endif // VBOX_WITH_GUEST_PROPS
5555
5556HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5557 com::Utf8Str &aValue,
5558 LONG64 *aTimestamp,
5559 com::Utf8Str &aFlags)
5560{
5561#ifndef VBOX_WITH_GUEST_PROPS
5562 ReturnComNotImplemented();
5563#else // VBOX_WITH_GUEST_PROPS
5564
5565 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5566
5567 if (rc == E_ACCESSDENIED)
5568 /* The VM is not running or the service is not (yet) accessible */
5569 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5570 return rc;
5571#endif // VBOX_WITH_GUEST_PROPS
5572}
5573
5574HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5575{
5576 LONG64 dummyTimestamp;
5577 com::Utf8Str dummyFlags;
5578 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5579 return rc;
5580
5581}
5582HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5583{
5584 com::Utf8Str dummyFlags;
5585 com::Utf8Str dummyValue;
5586 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5587 return rc;
5588}
5589
5590#ifdef VBOX_WITH_GUEST_PROPS
5591/**
5592 * Set a guest property in VBoxSVC's internal structures.
5593 */
5594HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5595 const com::Utf8Str &aFlags, bool fDelete)
5596{
5597 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5598 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
5599 if (FAILED(rc)) return rc;
5600
5601 try
5602 {
5603 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
5604 if (aFlags.length() && RT_FAILURE(GuestPropValidateFlags(aFlags.c_str(), &fFlags)))
5605 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5606
5607 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5608 if (it == mHWData->mGuestProperties.end())
5609 {
5610 if (!fDelete)
5611 {
5612 i_setModified(IsModified_MachineData);
5613 mHWData.backupEx();
5614
5615 RTTIMESPEC time;
5616 HWData::GuestProperty prop;
5617 prop.strValue = Bstr(aValue).raw();
5618 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5619 prop.mFlags = fFlags;
5620 mHWData->mGuestProperties[aName] = prop;
5621 }
5622 }
5623 else
5624 {
5625 if (it->second.mFlags & (GUEST_PROP_F_RDONLYHOST))
5626 {
5627 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5628 }
5629 else
5630 {
5631 i_setModified(IsModified_MachineData);
5632 mHWData.backupEx();
5633
5634 /* The backupEx() operation invalidates our iterator,
5635 * so get a new one. */
5636 it = mHWData->mGuestProperties.find(aName);
5637 Assert(it != mHWData->mGuestProperties.end());
5638
5639 if (!fDelete)
5640 {
5641 RTTIMESPEC time;
5642 it->second.strValue = aValue;
5643 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5644 it->second.mFlags = fFlags;
5645 }
5646 else
5647 mHWData->mGuestProperties.erase(it);
5648 }
5649 }
5650
5651 if (SUCCEEDED(rc))
5652 {
5653 alock.release();
5654
5655 mParent->i_onGuestPropertyChange(mData->mUuid,
5656 Bstr(aName).raw(),
5657 Bstr(aValue).raw(),
5658 Bstr(aFlags).raw());
5659 }
5660 }
5661 catch (std::bad_alloc &)
5662 {
5663 rc = E_OUTOFMEMORY;
5664 }
5665
5666 return rc;
5667}
5668
5669/**
5670 * Set a property on the VM that that property belongs to.
5671 * @returns E_ACCESSDENIED if the VM process is not available or not
5672 * currently handling queries and the setting should then be done in
5673 * VBoxSVC.
5674 */
5675HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5676 const com::Utf8Str &aFlags, bool fDelete)
5677{
5678 HRESULT rc;
5679
5680 try
5681 {
5682 ComPtr<IInternalSessionControl> directControl;
5683 {
5684 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5685 if (mData->mSession.mLockType == LockType_VM)
5686 directControl = mData->mSession.mDirectControl;
5687 }
5688
5689 Bstr dummy1; /* will not be changed (setter) */
5690 Bstr dummy2; /* will not be changed (setter) */
5691 LONG64 dummy64;
5692 if (!directControl)
5693 rc = E_ACCESSDENIED;
5694 else
5695 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5696 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5697 fDelete ? 2 : 1 /* accessMode */,
5698 dummy1.asOutParam(), &dummy64, dummy2.asOutParam());
5699 }
5700 catch (std::bad_alloc &)
5701 {
5702 rc = E_OUTOFMEMORY;
5703 }
5704
5705 return rc;
5706}
5707#endif // VBOX_WITH_GUEST_PROPS
5708
5709HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5710 const com::Utf8Str &aFlags)
5711{
5712#ifndef VBOX_WITH_GUEST_PROPS
5713 ReturnComNotImplemented();
5714#else // VBOX_WITH_GUEST_PROPS
5715 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5716 if (rc == E_ACCESSDENIED)
5717 /* The VM is not running or the service is not (yet) accessible */
5718 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5719 return rc;
5720#endif // VBOX_WITH_GUEST_PROPS
5721}
5722
5723HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5724{
5725 return setGuestProperty(aProperty, aValue, "");
5726}
5727
5728HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5729{
5730#ifndef VBOX_WITH_GUEST_PROPS
5731 ReturnComNotImplemented();
5732#else // VBOX_WITH_GUEST_PROPS
5733 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5734 if (rc == E_ACCESSDENIED)
5735 /* The VM is not running or the service is not (yet) accessible */
5736 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5737 return rc;
5738#endif // VBOX_WITH_GUEST_PROPS
5739}
5740
5741#ifdef VBOX_WITH_GUEST_PROPS
5742/**
5743 * Enumerate the guest properties in VBoxSVC's internal structures.
5744 */
5745HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5746 std::vector<com::Utf8Str> &aNames,
5747 std::vector<com::Utf8Str> &aValues,
5748 std::vector<LONG64> &aTimestamps,
5749 std::vector<com::Utf8Str> &aFlags)
5750{
5751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5752 Utf8Str strPatterns(aPatterns);
5753
5754 /*
5755 * Look for matching patterns and build up a list.
5756 */
5757 HWData::GuestPropertyMap propMap;
5758 for (HWData::GuestPropertyMap::const_iterator
5759 it = mHWData->mGuestProperties.begin();
5760 it != mHWData->mGuestProperties.end();
5761 ++it)
5762 {
5763 if ( strPatterns.isEmpty()
5764 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5765 RTSTR_MAX,
5766 it->first.c_str(),
5767 RTSTR_MAX,
5768 NULL)
5769 )
5770 propMap.insert(*it);
5771 }
5772
5773 alock.release();
5774
5775 /*
5776 * And build up the arrays for returning the property information.
5777 */
5778 size_t cEntries = propMap.size();
5779
5780 aNames.resize(cEntries);
5781 aValues.resize(cEntries);
5782 aTimestamps.resize(cEntries);
5783 aFlags.resize(cEntries);
5784
5785 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
5786 size_t i = 0;
5787 for (HWData::GuestPropertyMap::const_iterator
5788 it = propMap.begin();
5789 it != propMap.end();
5790 ++it, ++i)
5791 {
5792 aNames[i] = it->first;
5793 aValues[i] = it->second.strValue;
5794 aTimestamps[i] = it->second.mTimestamp;
5795 GuestPropWriteFlags(it->second.mFlags, szFlags);
5796 aFlags[i] = Utf8Str(szFlags);
5797 }
5798
5799 return S_OK;
5800}
5801
5802/**
5803 * Enumerate the properties managed by a VM.
5804 * @returns E_ACCESSDENIED if the VM process is not available or not
5805 * currently handling queries and the setting should then be done in
5806 * VBoxSVC.
5807 */
5808HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5809 std::vector<com::Utf8Str> &aNames,
5810 std::vector<com::Utf8Str> &aValues,
5811 std::vector<LONG64> &aTimestamps,
5812 std::vector<com::Utf8Str> &aFlags)
5813{
5814 HRESULT rc;
5815 ComPtr<IInternalSessionControl> directControl;
5816 {
5817 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5818 if (mData->mSession.mLockType == LockType_VM)
5819 directControl = mData->mSession.mDirectControl;
5820 }
5821
5822 com::SafeArray<BSTR> bNames;
5823 com::SafeArray<BSTR> bValues;
5824 com::SafeArray<LONG64> bTimestamps;
5825 com::SafeArray<BSTR> bFlags;
5826
5827 if (!directControl)
5828 rc = E_ACCESSDENIED;
5829 else
5830 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5831 ComSafeArrayAsOutParam(bNames),
5832 ComSafeArrayAsOutParam(bValues),
5833 ComSafeArrayAsOutParam(bTimestamps),
5834 ComSafeArrayAsOutParam(bFlags));
5835 size_t i;
5836 aNames.resize(bNames.size());
5837 for (i = 0; i < bNames.size(); ++i)
5838 aNames[i] = Utf8Str(bNames[i]);
5839 aValues.resize(bValues.size());
5840 for (i = 0; i < bValues.size(); ++i)
5841 aValues[i] = Utf8Str(bValues[i]);
5842 aTimestamps.resize(bTimestamps.size());
5843 for (i = 0; i < bTimestamps.size(); ++i)
5844 aTimestamps[i] = bTimestamps[i];
5845 aFlags.resize(bFlags.size());
5846 for (i = 0; i < bFlags.size(); ++i)
5847 aFlags[i] = Utf8Str(bFlags[i]);
5848
5849 return rc;
5850}
5851#endif // VBOX_WITH_GUEST_PROPS
5852HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5853 std::vector<com::Utf8Str> &aNames,
5854 std::vector<com::Utf8Str> &aValues,
5855 std::vector<LONG64> &aTimestamps,
5856 std::vector<com::Utf8Str> &aFlags)
5857{
5858#ifndef VBOX_WITH_GUEST_PROPS
5859 ReturnComNotImplemented();
5860#else // VBOX_WITH_GUEST_PROPS
5861
5862 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5863
5864 if (rc == E_ACCESSDENIED)
5865 /* The VM is not running or the service is not (yet) accessible */
5866 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5867 return rc;
5868#endif // VBOX_WITH_GUEST_PROPS
5869}
5870
5871HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5872 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5873{
5874 MediumAttachmentList atts;
5875
5876 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5877 if (FAILED(rc)) return rc;
5878
5879 aMediumAttachments.resize(atts.size());
5880 size_t i = 0;
5881 for (MediumAttachmentList::const_iterator
5882 it = atts.begin();
5883 it != atts.end();
5884 ++it, ++i)
5885 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5886
5887 return S_OK;
5888}
5889
5890HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5891 LONG aControllerPort,
5892 LONG aDevice,
5893 ComPtr<IMediumAttachment> &aAttachment)
5894{
5895 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5896 aName.c_str(), aControllerPort, aDevice));
5897
5898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5899
5900 aAttachment = NULL;
5901
5902 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(*mMediumAttachments.data(),
5903 aName,
5904 aControllerPort,
5905 aDevice);
5906 if (pAttach.isNull())
5907 return setError(VBOX_E_OBJECT_NOT_FOUND,
5908 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5909 aDevice, aControllerPort, aName.c_str());
5910
5911 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5912
5913 return S_OK;
5914}
5915
5916
5917HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5918 StorageBus_T aConnectionType,
5919 ComPtr<IStorageController> &aController)
5920{
5921 if ( (aConnectionType <= StorageBus_Null)
5922 || (aConnectionType > StorageBus_VirtioSCSI))
5923 return setError(E_INVALIDARG,
5924 tr("Invalid connection type: %d"),
5925 aConnectionType);
5926
5927 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5928
5929 HRESULT rc = i_checkStateDependency(MutableStateDep);
5930 if (FAILED(rc)) return rc;
5931
5932 /* try to find one with the name first. */
5933 ComObjPtr<StorageController> ctrl;
5934
5935 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
5936 if (SUCCEEDED(rc))
5937 return setError(VBOX_E_OBJECT_IN_USE,
5938 tr("Storage controller named '%s' already exists"),
5939 aName.c_str());
5940
5941 ctrl.createObject();
5942
5943 /* get a new instance number for the storage controller */
5944 ULONG ulInstance = 0;
5945 bool fBootable = true;
5946 for (StorageControllerList::const_iterator
5947 it = mStorageControllers->begin();
5948 it != mStorageControllers->end();
5949 ++it)
5950 {
5951 if ((*it)->i_getStorageBus() == aConnectionType)
5952 {
5953 ULONG ulCurInst = (*it)->i_getInstance();
5954
5955 if (ulCurInst >= ulInstance)
5956 ulInstance = ulCurInst + 1;
5957
5958 /* Only one controller of each type can be marked as bootable. */
5959 if ((*it)->i_getBootable())
5960 fBootable = false;
5961 }
5962 }
5963
5964 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
5965 if (FAILED(rc)) return rc;
5966
5967 i_setModified(IsModified_Storage);
5968 mStorageControllers.backup();
5969 mStorageControllers->push_back(ctrl);
5970
5971 ctrl.queryInterfaceTo(aController.asOutParam());
5972
5973 /* inform the direct session if any */
5974 alock.release();
5975 i_onStorageControllerChange(i_getId(), aName);
5976
5977 return S_OK;
5978}
5979
5980HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
5981 ComPtr<IStorageController> &aStorageController)
5982{
5983 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5984
5985 ComObjPtr<StorageController> ctrl;
5986
5987 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
5988 if (SUCCEEDED(rc))
5989 ctrl.queryInterfaceTo(aStorageController.asOutParam());
5990
5991 return rc;
5992}
5993
5994HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
5995 ULONG aInstance,
5996 ComPtr<IStorageController> &aStorageController)
5997{
5998 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5999
6000 for (StorageControllerList::const_iterator
6001 it = mStorageControllers->begin();
6002 it != mStorageControllers->end();
6003 ++it)
6004 {
6005 if ( (*it)->i_getStorageBus() == aConnectionType
6006 && (*it)->i_getInstance() == aInstance)
6007 {
6008 (*it).queryInterfaceTo(aStorageController.asOutParam());
6009 return S_OK;
6010 }
6011 }
6012
6013 return setError(VBOX_E_OBJECT_NOT_FOUND,
6014 tr("Could not find a storage controller with instance number '%lu'"),
6015 aInstance);
6016}
6017
6018HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6019{
6020 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6021
6022 HRESULT rc = i_checkStateDependency(MutableStateDep);
6023 if (FAILED(rc)) return rc;
6024
6025 ComObjPtr<StorageController> ctrl;
6026
6027 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6028 if (SUCCEEDED(rc))
6029 {
6030 /* Ensure that only one controller of each type is marked as bootable. */
6031 if (aBootable == TRUE)
6032 {
6033 for (StorageControllerList::const_iterator
6034 it = mStorageControllers->begin();
6035 it != mStorageControllers->end();
6036 ++it)
6037 {
6038 ComObjPtr<StorageController> aCtrl = (*it);
6039
6040 if ( (aCtrl->i_getName() != aName)
6041 && aCtrl->i_getBootable() == TRUE
6042 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6043 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6044 {
6045 aCtrl->i_setBootable(FALSE);
6046 break;
6047 }
6048 }
6049 }
6050
6051 if (SUCCEEDED(rc))
6052 {
6053 ctrl->i_setBootable(aBootable);
6054 i_setModified(IsModified_Storage);
6055 }
6056 }
6057
6058 if (SUCCEEDED(rc))
6059 {
6060 /* inform the direct session if any */
6061 alock.release();
6062 i_onStorageControllerChange(i_getId(), aName);
6063 }
6064
6065 return rc;
6066}
6067
6068HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6069{
6070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6071
6072 HRESULT rc = i_checkStateDependency(MutableStateDep);
6073 if (FAILED(rc)) return rc;
6074
6075 ComObjPtr<StorageController> ctrl;
6076 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6077 if (FAILED(rc)) return rc;
6078
6079 MediumAttachmentList llDetachedAttachments;
6080 {
6081 /* find all attached devices to the appropriate storage controller and detach them all */
6082 // make a temporary list because detachDevice invalidates iterators into
6083 // mMediumAttachments
6084 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
6085
6086 for (MediumAttachmentList::const_iterator
6087 it = llAttachments2.begin();
6088 it != llAttachments2.end();
6089 ++it)
6090 {
6091 MediumAttachment *pAttachTemp = *it;
6092
6093 AutoCaller localAutoCaller(pAttachTemp);
6094 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6095
6096 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6097
6098 if (pAttachTemp->i_getControllerName() == aName)
6099 {
6100 llDetachedAttachments.push_back(pAttachTemp);
6101 rc = i_detachDevice(pAttachTemp, alock, NULL);
6102 if (FAILED(rc)) return rc;
6103 }
6104 }
6105 }
6106
6107 /* send event about detached devices before removing parent controller */
6108 for (MediumAttachmentList::const_iterator
6109 it = llDetachedAttachments.begin();
6110 it != llDetachedAttachments.end();
6111 ++it)
6112 {
6113 mParent->i_onStorageDeviceChanged(*it, TRUE, FALSE);
6114 }
6115
6116 /* We can remove it now. */
6117 i_setModified(IsModified_Storage);
6118 mStorageControllers.backup();
6119
6120 ctrl->i_unshare();
6121
6122 mStorageControllers->remove(ctrl);
6123
6124 /* inform the direct session if any */
6125 alock.release();
6126 i_onStorageControllerChange(i_getId(), aName);
6127
6128 return S_OK;
6129}
6130
6131HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6132 ComPtr<IUSBController> &aController)
6133{
6134 if ( (aType <= USBControllerType_Null)
6135 || (aType >= USBControllerType_Last))
6136 return setError(E_INVALIDARG,
6137 tr("Invalid USB controller type: %d"),
6138 aType);
6139
6140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6141
6142 HRESULT rc = i_checkStateDependency(MutableStateDep);
6143 if (FAILED(rc)) return rc;
6144
6145 /* try to find one with the same type first. */
6146 ComObjPtr<USBController> ctrl;
6147
6148 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6149 if (SUCCEEDED(rc))
6150 return setError(VBOX_E_OBJECT_IN_USE,
6151 tr("USB controller named '%s' already exists"),
6152 aName.c_str());
6153
6154 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6155 ULONG maxInstances;
6156 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6157 if (FAILED(rc))
6158 return rc;
6159
6160 ULONG cInstances = i_getUSBControllerCountByType(aType);
6161 if (cInstances >= maxInstances)
6162 return setError(E_INVALIDARG,
6163 tr("Too many USB controllers of this type"));
6164
6165 ctrl.createObject();
6166
6167 rc = ctrl->init(this, aName, aType);
6168 if (FAILED(rc)) return rc;
6169
6170 i_setModified(IsModified_USB);
6171 mUSBControllers.backup();
6172 mUSBControllers->push_back(ctrl);
6173
6174 ctrl.queryInterfaceTo(aController.asOutParam());
6175
6176 /* inform the direct session if any */
6177 alock.release();
6178 i_onUSBControllerChange();
6179
6180 return S_OK;
6181}
6182
6183HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6184{
6185 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6186
6187 ComObjPtr<USBController> ctrl;
6188
6189 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6190 if (SUCCEEDED(rc))
6191 ctrl.queryInterfaceTo(aController.asOutParam());
6192
6193 return rc;
6194}
6195
6196HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6197 ULONG *aControllers)
6198{
6199 if ( (aType <= USBControllerType_Null)
6200 || (aType >= USBControllerType_Last))
6201 return setError(E_INVALIDARG,
6202 tr("Invalid USB controller type: %d"),
6203 aType);
6204
6205 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6206
6207 ComObjPtr<USBController> ctrl;
6208
6209 *aControllers = i_getUSBControllerCountByType(aType);
6210
6211 return S_OK;
6212}
6213
6214HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6215{
6216
6217 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6218
6219 HRESULT rc = i_checkStateDependency(MutableStateDep);
6220 if (FAILED(rc)) return rc;
6221
6222 ComObjPtr<USBController> ctrl;
6223 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6224 if (FAILED(rc)) return rc;
6225
6226 i_setModified(IsModified_USB);
6227 mUSBControllers.backup();
6228
6229 ctrl->i_unshare();
6230
6231 mUSBControllers->remove(ctrl);
6232
6233 /* inform the direct session if any */
6234 alock.release();
6235 i_onUSBControllerChange();
6236
6237 return S_OK;
6238}
6239
6240HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6241 ULONG *aOriginX,
6242 ULONG *aOriginY,
6243 ULONG *aWidth,
6244 ULONG *aHeight,
6245 BOOL *aEnabled)
6246{
6247 uint32_t u32OriginX= 0;
6248 uint32_t u32OriginY= 0;
6249 uint32_t u32Width = 0;
6250 uint32_t u32Height = 0;
6251 uint16_t u16Flags = 0;
6252
6253 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6254 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6255 if (RT_FAILURE(vrc))
6256 {
6257#ifdef RT_OS_WINDOWS
6258 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6259 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6260 * So just assign fEnable to TRUE again.
6261 * The right fix would be to change GUI API wrappers to make sure that parameters
6262 * are changed only if API succeeds.
6263 */
6264 *aEnabled = TRUE;
6265#endif
6266 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6267 tr("Saved guest size is not available (%Rrc)"),
6268 vrc);
6269 }
6270
6271 *aOriginX = u32OriginX;
6272 *aOriginY = u32OriginY;
6273 *aWidth = u32Width;
6274 *aHeight = u32Height;
6275 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6276
6277 return S_OK;
6278}
6279
6280HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6281 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6282{
6283 if (aScreenId != 0)
6284 return E_NOTIMPL;
6285
6286 if ( aBitmapFormat != BitmapFormat_BGR0
6287 && aBitmapFormat != BitmapFormat_BGRA
6288 && aBitmapFormat != BitmapFormat_RGBA
6289 && aBitmapFormat != BitmapFormat_PNG)
6290 return setError(E_NOTIMPL,
6291 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6292
6293 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6294
6295 uint8_t *pu8Data = NULL;
6296 uint32_t cbData = 0;
6297 uint32_t u32Width = 0;
6298 uint32_t u32Height = 0;
6299
6300 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6301
6302 if (RT_FAILURE(vrc))
6303 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6304 tr("Saved thumbnail data is not available (%Rrc)"),
6305 vrc);
6306
6307 HRESULT hr = S_OK;
6308
6309 *aWidth = u32Width;
6310 *aHeight = u32Height;
6311
6312 if (cbData > 0)
6313 {
6314 /* Convert pixels to the format expected by the API caller. */
6315 if (aBitmapFormat == BitmapFormat_BGR0)
6316 {
6317 /* [0] B, [1] G, [2] R, [3] 0. */
6318 aData.resize(cbData);
6319 memcpy(&aData.front(), pu8Data, cbData);
6320 }
6321 else if (aBitmapFormat == BitmapFormat_BGRA)
6322 {
6323 /* [0] B, [1] G, [2] R, [3] A. */
6324 aData.resize(cbData);
6325 for (uint32_t i = 0; i < cbData; i += 4)
6326 {
6327 aData[i] = pu8Data[i];
6328 aData[i + 1] = pu8Data[i + 1];
6329 aData[i + 2] = pu8Data[i + 2];
6330 aData[i + 3] = 0xff;
6331 }
6332 }
6333 else if (aBitmapFormat == BitmapFormat_RGBA)
6334 {
6335 /* [0] R, [1] G, [2] B, [3] A. */
6336 aData.resize(cbData);
6337 for (uint32_t i = 0; i < cbData; i += 4)
6338 {
6339 aData[i] = pu8Data[i + 2];
6340 aData[i + 1] = pu8Data[i + 1];
6341 aData[i + 2] = pu8Data[i];
6342 aData[i + 3] = 0xff;
6343 }
6344 }
6345 else if (aBitmapFormat == BitmapFormat_PNG)
6346 {
6347 uint8_t *pu8PNG = NULL;
6348 uint32_t cbPNG = 0;
6349 uint32_t cxPNG = 0;
6350 uint32_t cyPNG = 0;
6351
6352 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6353
6354 if (RT_SUCCESS(vrc))
6355 {
6356 aData.resize(cbPNG);
6357 if (cbPNG)
6358 memcpy(&aData.front(), pu8PNG, cbPNG);
6359 }
6360 else
6361 hr = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6362 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6363 vrc);
6364
6365 RTMemFree(pu8PNG);
6366 }
6367 }
6368
6369 freeSavedDisplayScreenshot(pu8Data);
6370
6371 return hr;
6372}
6373
6374HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6375 ULONG *aWidth,
6376 ULONG *aHeight,
6377 std::vector<BitmapFormat_T> &aBitmapFormats)
6378{
6379 if (aScreenId != 0)
6380 return E_NOTIMPL;
6381
6382 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6383
6384 uint8_t *pu8Data = NULL;
6385 uint32_t cbData = 0;
6386 uint32_t u32Width = 0;
6387 uint32_t u32Height = 0;
6388
6389 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6390
6391 if (RT_FAILURE(vrc))
6392 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6393 tr("Saved screenshot data is not available (%Rrc)"),
6394 vrc);
6395
6396 *aWidth = u32Width;
6397 *aHeight = u32Height;
6398 aBitmapFormats.resize(1);
6399 aBitmapFormats[0] = BitmapFormat_PNG;
6400
6401 freeSavedDisplayScreenshot(pu8Data);
6402
6403 return S_OK;
6404}
6405
6406HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6407 BitmapFormat_T aBitmapFormat,
6408 ULONG *aWidth,
6409 ULONG *aHeight,
6410 std::vector<BYTE> &aData)
6411{
6412 if (aScreenId != 0)
6413 return E_NOTIMPL;
6414
6415 if (aBitmapFormat != BitmapFormat_PNG)
6416 return E_NOTIMPL;
6417
6418 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6419
6420 uint8_t *pu8Data = NULL;
6421 uint32_t cbData = 0;
6422 uint32_t u32Width = 0;
6423 uint32_t u32Height = 0;
6424
6425 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6426
6427 if (RT_FAILURE(vrc))
6428 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6429 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6430 vrc);
6431
6432 *aWidth = u32Width;
6433 *aHeight = u32Height;
6434
6435 aData.resize(cbData);
6436 if (cbData)
6437 memcpy(&aData.front(), pu8Data, cbData);
6438
6439 freeSavedDisplayScreenshot(pu8Data);
6440
6441 return S_OK;
6442}
6443
6444HRESULT Machine::hotPlugCPU(ULONG aCpu)
6445{
6446 HRESULT rc = S_OK;
6447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6448
6449 if (!mHWData->mCPUHotPlugEnabled)
6450 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6451
6452 if (aCpu >= mHWData->mCPUCount)
6453 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6454
6455 if (mHWData->mCPUAttached[aCpu])
6456 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6457
6458 alock.release();
6459 rc = i_onCPUChange(aCpu, false);
6460 alock.acquire();
6461 if (FAILED(rc)) return rc;
6462
6463 i_setModified(IsModified_MachineData);
6464 mHWData.backup();
6465 mHWData->mCPUAttached[aCpu] = true;
6466
6467 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6468 if (Global::IsOnline(mData->mMachineState))
6469 i_saveSettings(NULL);
6470
6471 return S_OK;
6472}
6473
6474HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6475{
6476 HRESULT rc = S_OK;
6477
6478 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6479
6480 if (!mHWData->mCPUHotPlugEnabled)
6481 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6482
6483 if (aCpu >= SchemaDefs::MaxCPUCount)
6484 return setError(E_INVALIDARG,
6485 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6486 SchemaDefs::MaxCPUCount);
6487
6488 if (!mHWData->mCPUAttached[aCpu])
6489 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6490
6491 /* CPU 0 can't be detached */
6492 if (aCpu == 0)
6493 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6494
6495 alock.release();
6496 rc = i_onCPUChange(aCpu, true);
6497 alock.acquire();
6498 if (FAILED(rc)) return rc;
6499
6500 i_setModified(IsModified_MachineData);
6501 mHWData.backup();
6502 mHWData->mCPUAttached[aCpu] = false;
6503
6504 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6505 if (Global::IsOnline(mData->mMachineState))
6506 i_saveSettings(NULL);
6507
6508 return S_OK;
6509}
6510
6511HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6512{
6513 *aAttached = false;
6514
6515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6516
6517 /* If hotplug is enabled the CPU is always enabled. */
6518 if (!mHWData->mCPUHotPlugEnabled)
6519 {
6520 if (aCpu < mHWData->mCPUCount)
6521 *aAttached = true;
6522 }
6523 else
6524 {
6525 if (aCpu < SchemaDefs::MaxCPUCount)
6526 *aAttached = mHWData->mCPUAttached[aCpu];
6527 }
6528
6529 return S_OK;
6530}
6531
6532HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6533{
6534 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6535
6536 Utf8Str log = i_getLogFilename(aIdx);
6537 if (!RTFileExists(log.c_str()))
6538 log.setNull();
6539 aFilename = log;
6540
6541 return S_OK;
6542}
6543
6544HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6545{
6546 if (aSize < 0)
6547 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6548
6549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6550
6551 HRESULT rc = S_OK;
6552 Utf8Str log = i_getLogFilename(aIdx);
6553
6554 /* do not unnecessarily hold the lock while doing something which does
6555 * not need the lock and potentially takes a long time. */
6556 alock.release();
6557
6558 /* Limit the chunk size to 512K. Gives good performance over (XP)COM, and
6559 * keeps the SOAP reply size under 1M for the webservice (we're using
6560 * base64 encoded strings for binary data for years now, avoiding the
6561 * expansion of each byte array element to approx. 25 bytes of XML. */
6562 size_t cbData = (size_t)RT_MIN(aSize, _512K);
6563 aData.resize(cbData);
6564
6565 RTFILE LogFile;
6566 int vrc = RTFileOpen(&LogFile, log.c_str(),
6567 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6568 if (RT_SUCCESS(vrc))
6569 {
6570 vrc = RTFileReadAt(LogFile, aOffset, cbData ? &aData.front() : NULL, cbData, &cbData);
6571 if (RT_SUCCESS(vrc))
6572 aData.resize(cbData);
6573 else
6574 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6575 tr("Could not read log file '%s' (%Rrc)"),
6576 log.c_str(), vrc);
6577 RTFileClose(LogFile);
6578 }
6579 else
6580 rc = setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
6581 tr("Could not open log file '%s' (%Rrc)"),
6582 log.c_str(), vrc);
6583
6584 if (FAILED(rc))
6585 aData.resize(0);
6586
6587 return rc;
6588}
6589
6590
6591/**
6592 * Currently this method doesn't attach device to the running VM,
6593 * just makes sure it's plugged on next VM start.
6594 */
6595HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6596{
6597 // lock scope
6598 {
6599 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6600
6601 HRESULT rc = i_checkStateDependency(MutableStateDep);
6602 if (FAILED(rc)) return rc;
6603
6604 ChipsetType_T aChipset = ChipsetType_PIIX3;
6605 COMGETTER(ChipsetType)(&aChipset);
6606
6607 if (aChipset != ChipsetType_ICH9)
6608 {
6609 return setError(E_INVALIDARG,
6610 tr("Host PCI attachment only supported with ICH9 chipset"));
6611 }
6612
6613 // check if device with this host PCI address already attached
6614 for (HWData::PCIDeviceAssignmentList::const_iterator
6615 it = mHWData->mPCIDeviceAssignments.begin();
6616 it != mHWData->mPCIDeviceAssignments.end();
6617 ++it)
6618 {
6619 LONG iHostAddress = -1;
6620 ComPtr<PCIDeviceAttachment> pAttach;
6621 pAttach = *it;
6622 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6623 if (iHostAddress == aHostAddress)
6624 return setError(E_INVALIDARG,
6625 tr("Device with host PCI address already attached to this VM"));
6626 }
6627
6628 ComObjPtr<PCIDeviceAttachment> pda;
6629 char name[32];
6630
6631 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6632 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6633 pda.createObject();
6634 pda->init(this, name, aHostAddress, aDesiredGuestAddress, TRUE);
6635 i_setModified(IsModified_MachineData);
6636 mHWData.backup();
6637 mHWData->mPCIDeviceAssignments.push_back(pda);
6638 }
6639
6640 return S_OK;
6641}
6642
6643/**
6644 * Currently this method doesn't detach device from the running VM,
6645 * just makes sure it's not plugged on next VM start.
6646 */
6647HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6648{
6649 ComObjPtr<PCIDeviceAttachment> pAttach;
6650 bool fRemoved = false;
6651 HRESULT rc;
6652
6653 // lock scope
6654 {
6655 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6656
6657 rc = i_checkStateDependency(MutableStateDep);
6658 if (FAILED(rc)) return rc;
6659
6660 for (HWData::PCIDeviceAssignmentList::const_iterator
6661 it = mHWData->mPCIDeviceAssignments.begin();
6662 it != mHWData->mPCIDeviceAssignments.end();
6663 ++it)
6664 {
6665 LONG iHostAddress = -1;
6666 pAttach = *it;
6667 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6668 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6669 {
6670 i_setModified(IsModified_MachineData);
6671 mHWData.backup();
6672 mHWData->mPCIDeviceAssignments.remove(pAttach);
6673 fRemoved = true;
6674 break;
6675 }
6676 }
6677 }
6678
6679
6680 /* Fire event outside of the lock */
6681 if (fRemoved)
6682 {
6683 Assert(!pAttach.isNull());
6684 ComPtr<IEventSource> es;
6685 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6686 Assert(SUCCEEDED(rc));
6687 Bstr mid;
6688 rc = this->COMGETTER(Id)(mid.asOutParam());
6689 Assert(SUCCEEDED(rc));
6690 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6691 }
6692
6693 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6694 tr("No host PCI device %08x attached"),
6695 aHostAddress
6696 );
6697}
6698
6699HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6700{
6701 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6702
6703 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6704 size_t i = 0;
6705 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator
6706 it = mHWData->mPCIDeviceAssignments.begin();
6707 it != mHWData->mPCIDeviceAssignments.end();
6708 ++it, ++i)
6709 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6710
6711 return S_OK;
6712}
6713
6714HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6715{
6716 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6717
6718 return S_OK;
6719}
6720
6721HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6722{
6723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6724
6725 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6726
6727 return S_OK;
6728}
6729
6730HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6731{
6732 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6733 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6734 if (SUCCEEDED(hrc))
6735 {
6736 hrc = mHWData.backupEx();
6737 if (SUCCEEDED(hrc))
6738 {
6739 i_setModified(IsModified_MachineData);
6740 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6741 }
6742 }
6743 return hrc;
6744}
6745
6746HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6747{
6748 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6749 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6750 return S_OK;
6751}
6752
6753HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6754{
6755 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6756 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6757 if (SUCCEEDED(hrc))
6758 {
6759 hrc = mHWData.backupEx();
6760 if (SUCCEEDED(hrc))
6761 {
6762 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6763 if (SUCCEEDED(hrc))
6764 i_setModified(IsModified_MachineData);
6765 }
6766 }
6767 return hrc;
6768}
6769
6770HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6771{
6772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6773
6774 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6775
6776 return S_OK;
6777}
6778
6779HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6780{
6781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6782 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6783 if (SUCCEEDED(hrc))
6784 {
6785 hrc = mHWData.backupEx();
6786 if (SUCCEEDED(hrc))
6787 {
6788 i_setModified(IsModified_MachineData);
6789 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6790 }
6791 }
6792 return hrc;
6793}
6794
6795HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6796{
6797 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6798
6799 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6800
6801 return S_OK;
6802}
6803
6804HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807
6808 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6809 if ( SUCCEEDED(hrc)
6810 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6811 {
6812 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6813 int vrc;
6814
6815 if (aAutostartEnabled)
6816 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6817 else
6818 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6819
6820 if (RT_SUCCESS(vrc))
6821 {
6822 hrc = mHWData.backupEx();
6823 if (SUCCEEDED(hrc))
6824 {
6825 i_setModified(IsModified_MachineData);
6826 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6827 }
6828 }
6829 else if (vrc == VERR_NOT_SUPPORTED)
6830 hrc = setError(VBOX_E_NOT_SUPPORTED,
6831 tr("The VM autostart feature is not supported on this platform"));
6832 else if (vrc == VERR_PATH_NOT_FOUND)
6833 hrc = setError(E_FAIL,
6834 tr("The path to the autostart database is not set"));
6835 else
6836 hrc = setError(E_UNEXPECTED,
6837 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6838 aAutostartEnabled ? "Adding" : "Removing",
6839 mUserData->s.strName.c_str(), vrc);
6840 }
6841 return hrc;
6842}
6843
6844HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6845{
6846 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6847
6848 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6849
6850 return S_OK;
6851}
6852
6853HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6854{
6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6856 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6857 if (SUCCEEDED(hrc))
6858 {
6859 hrc = mHWData.backupEx();
6860 if (SUCCEEDED(hrc))
6861 {
6862 i_setModified(IsModified_MachineData);
6863 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6864 }
6865 }
6866 return hrc;
6867}
6868
6869HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6870{
6871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6872
6873 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6874
6875 return S_OK;
6876}
6877
6878HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6879{
6880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6881 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6882 if ( SUCCEEDED(hrc)
6883 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6884 {
6885 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6886 int vrc;
6887
6888 if (aAutostopType != AutostopType_Disabled)
6889 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6890 else
6891 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6892
6893 if (RT_SUCCESS(vrc))
6894 {
6895 hrc = mHWData.backupEx();
6896 if (SUCCEEDED(hrc))
6897 {
6898 i_setModified(IsModified_MachineData);
6899 mHWData->mAutostart.enmAutostopType = aAutostopType;
6900 }
6901 }
6902 else if (vrc == VERR_NOT_SUPPORTED)
6903 hrc = setError(VBOX_E_NOT_SUPPORTED,
6904 tr("The VM autostop feature is not supported on this platform"));
6905 else if (vrc == VERR_PATH_NOT_FOUND)
6906 hrc = setError(E_FAIL,
6907 tr("The path to the autostart database is not set"));
6908 else
6909 hrc = setError(E_UNEXPECTED,
6910 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6911 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6912 mUserData->s.strName.c_str(), vrc);
6913 }
6914 return hrc;
6915}
6916
6917HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6918{
6919 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6920
6921 aDefaultFrontend = mHWData->mDefaultFrontend;
6922
6923 return S_OK;
6924}
6925
6926HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6927{
6928 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6929 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6930 if (SUCCEEDED(hrc))
6931 {
6932 hrc = mHWData.backupEx();
6933 if (SUCCEEDED(hrc))
6934 {
6935 i_setModified(IsModified_MachineData);
6936 mHWData->mDefaultFrontend = aDefaultFrontend;
6937 }
6938 }
6939 return hrc;
6940}
6941
6942HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6943{
6944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6945 size_t cbIcon = mUserData->s.ovIcon.size();
6946 aIcon.resize(cbIcon);
6947 if (cbIcon)
6948 memcpy(&aIcon.front(), &mUserData->s.ovIcon[0], cbIcon);
6949 return S_OK;
6950}
6951
6952HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
6953{
6954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6955 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6956 if (SUCCEEDED(hrc))
6957 {
6958 i_setModified(IsModified_MachineData);
6959 mUserData.backup();
6960 size_t cbIcon = aIcon.size();
6961 mUserData->s.ovIcon.resize(cbIcon);
6962 if (cbIcon)
6963 memcpy(&mUserData->s.ovIcon[0], &aIcon.front(), cbIcon);
6964 }
6965 return hrc;
6966}
6967
6968HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
6969{
6970#ifdef VBOX_WITH_USB
6971 *aUSBProxyAvailable = true;
6972#else
6973 *aUSBProxyAvailable = false;
6974#endif
6975 return S_OK;
6976}
6977
6978HRESULT Machine::getVMProcessPriority(VMProcPriority_T *aVMProcessPriority)
6979{
6980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6981
6982 *aVMProcessPriority = mUserData->s.enmVMPriority;
6983
6984 return S_OK;
6985}
6986
6987HRESULT Machine::setVMProcessPriority(VMProcPriority_T aVMProcessPriority)
6988{
6989 RT_NOREF(aVMProcessPriority);
6990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6991 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6992 if (SUCCEEDED(hrc))
6993 {
6994 hrc = mUserData.backupEx();
6995 if (SUCCEEDED(hrc))
6996 {
6997 i_setModified(IsModified_MachineData);
6998 mUserData->s.enmVMPriority = aVMProcessPriority;
6999 }
7000 }
7001 alock.release();
7002 if (SUCCEEDED(hrc))
7003 hrc = i_onVMProcessPriorityChange(aVMProcessPriority);
7004 return hrc;
7005}
7006
7007HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7008 ComPtr<IProgress> &aProgress)
7009{
7010 ComObjPtr<Progress> pP;
7011 Progress *ppP = pP;
7012 IProgress *iP = static_cast<IProgress *>(ppP);
7013 IProgress **pProgress = &iP;
7014
7015 IMachine *pTarget = aTarget;
7016
7017 /* Convert the options. */
7018 RTCList<CloneOptions_T> optList;
7019 if (aOptions.size())
7020 for (size_t i = 0; i < aOptions.size(); ++i)
7021 optList.append(aOptions[i]);
7022
7023 if (optList.contains(CloneOptions_Link))
7024 {
7025 if (!i_isSnapshotMachine())
7026 return setError(E_INVALIDARG,
7027 tr("Linked clone can only be created from a snapshot"));
7028 if (aMode != CloneMode_MachineState)
7029 return setError(E_INVALIDARG,
7030 tr("Linked clone can only be created for a single machine state"));
7031 }
7032 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7033
7034 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7035
7036 HRESULT rc = pWorker->start(pProgress);
7037
7038 pP = static_cast<Progress *>(*pProgress);
7039 pP.queryInterfaceTo(aProgress.asOutParam());
7040
7041 return rc;
7042
7043}
7044
7045HRESULT Machine::moveTo(const com::Utf8Str &aTargetPath,
7046 const com::Utf8Str &aType,
7047 ComPtr<IProgress> &aProgress)
7048{
7049 LogFlowThisFuncEnter();
7050
7051 ComObjPtr<Progress> ptrProgress;
7052 HRESULT hrc = ptrProgress.createObject();
7053 if (SUCCEEDED(hrc))
7054 {
7055 /* Initialize our worker task */
7056 MachineMoveVM *pTask = NULL;
7057 try
7058 {
7059 pTask = new MachineMoveVM(this, aTargetPath, aType, ptrProgress);
7060 }
7061 catch (std::bad_alloc &)
7062 {
7063 return E_OUTOFMEMORY;
7064 }
7065
7066 hrc = pTask->init();//no exceptions are thrown
7067
7068 if (SUCCEEDED(hrc))
7069 {
7070 hrc = pTask->createThread();
7071 pTask = NULL; /* Consumed by createThread(). */
7072 if (SUCCEEDED(hrc))
7073 ptrProgress.queryInterfaceTo(aProgress.asOutParam());
7074 else
7075 setError(hrc, tr("Failed to create a worker thread for the MachineMoveVM task"));
7076 }
7077 else
7078 delete pTask;
7079 }
7080
7081 LogFlowThisFuncLeave();
7082 return hrc;
7083
7084}
7085
7086HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7087{
7088 NOREF(aProgress);
7089 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7090
7091 // This check should always fail.
7092 HRESULT rc = i_checkStateDependency(MutableStateDep);
7093 if (FAILED(rc)) return rc;
7094
7095 AssertFailedReturn(E_NOTIMPL);
7096}
7097
7098HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7099{
7100 NOREF(aSavedStateFile);
7101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7102
7103 // This check should always fail.
7104 HRESULT rc = i_checkStateDependency(MutableStateDep);
7105 if (FAILED(rc)) return rc;
7106
7107 AssertFailedReturn(E_NOTIMPL);
7108}
7109
7110HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7111{
7112 NOREF(aFRemoveFile);
7113 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7114
7115 // This check should always fail.
7116 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7117 if (FAILED(rc)) return rc;
7118
7119 AssertFailedReturn(E_NOTIMPL);
7120}
7121
7122// public methods for internal purposes
7123/////////////////////////////////////////////////////////////////////////////
7124
7125/**
7126 * Adds the given IsModified_* flag to the dirty flags of the machine.
7127 * This must be called either during i_loadSettings or under the machine write lock.
7128 * @param fl Flag
7129 * @param fAllowStateModification If state modifications are allowed.
7130 */
7131void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7132{
7133 mData->flModifications |= fl;
7134 if (fAllowStateModification && i_isStateModificationAllowed())
7135 mData->mCurrentStateModified = true;
7136}
7137
7138/**
7139 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7140 * care of the write locking.
7141 *
7142 * @param fModification The flag to add.
7143 * @param fAllowStateModification If state modifications are allowed.
7144 */
7145void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7146{
7147 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7148 i_setModified(fModification, fAllowStateModification);
7149}
7150
7151/**
7152 * Saves the registry entry of this machine to the given configuration node.
7153 *
7154 * @param data Machine registry data.
7155 *
7156 * @note locks this object for reading.
7157 */
7158HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7159{
7160 AutoLimitedCaller autoCaller(this);
7161 AssertComRCReturnRC(autoCaller.rc());
7162
7163 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7164
7165 data.uuid = mData->mUuid;
7166 data.strSettingsFile = mData->m_strConfigFile;
7167
7168 return S_OK;
7169}
7170
7171/**
7172 * Calculates the absolute path of the given path taking the directory of the
7173 * machine settings file as the current directory.
7174 *
7175 * @param strPath Path to calculate the absolute path for.
7176 * @param aResult Where to put the result (used only on success, can be the
7177 * same Utf8Str instance as passed in @a aPath).
7178 * @return IPRT result.
7179 *
7180 * @note Locks this object for reading.
7181 */
7182int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7183{
7184 AutoCaller autoCaller(this);
7185 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7186
7187 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7188
7189 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7190
7191 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7192
7193 strSettingsDir.stripFilename();
7194 char szFolder[RTPATH_MAX];
7195 size_t cbFolder = sizeof(szFolder);
7196 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), RTPATH_STR_F_STYLE_HOST, szFolder, &cbFolder);
7197 if (RT_SUCCESS(vrc))
7198 aResult = szFolder;
7199
7200 return vrc;
7201}
7202
7203/**
7204 * Copies strSource to strTarget, making it relative to the machine folder
7205 * if it is a subdirectory thereof, or simply copying it otherwise.
7206 *
7207 * @param strSource Path to evaluate and copy.
7208 * @param strTarget Buffer to receive target path.
7209 *
7210 * @note Locks this object for reading.
7211 */
7212void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7213 Utf8Str &strTarget)
7214{
7215 AutoCaller autoCaller(this);
7216 AssertComRCReturn(autoCaller.rc(), (void)0);
7217
7218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7219
7220 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7221 // use strTarget as a temporary buffer to hold the machine settings dir
7222 strTarget = mData->m_strConfigFileFull;
7223 strTarget.stripFilename();
7224 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7225 {
7226 // is relative: then append what's left
7227 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7228 // for empty paths (only possible for subdirs) use "." to avoid
7229 // triggering default settings for not present config attributes.
7230 if (strTarget.isEmpty())
7231 strTarget = ".";
7232 }
7233 else
7234 // is not relative: then overwrite
7235 strTarget = strSource;
7236}
7237
7238/**
7239 * Returns the full path to the machine's log folder in the
7240 * \a aLogFolder argument.
7241 */
7242void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7243{
7244 AutoCaller autoCaller(this);
7245 AssertComRCReturnVoid(autoCaller.rc());
7246
7247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7248
7249 char szTmp[RTPATH_MAX];
7250 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7251 if (RT_SUCCESS(vrc))
7252 {
7253 if (szTmp[0] && !mUserData.isNull())
7254 {
7255 char szTmp2[RTPATH_MAX];
7256 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7257 if (RT_SUCCESS(vrc))
7258 aLogFolder = Utf8StrFmt("%s%c%s",
7259 szTmp2,
7260 RTPATH_DELIMITER,
7261 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7262 }
7263 else
7264 vrc = VERR_PATH_IS_RELATIVE;
7265 }
7266
7267 if (RT_FAILURE(vrc))
7268 {
7269 // fallback if VBOX_USER_LOGHOME is not set or invalid
7270 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7271 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7272 aLogFolder.append(RTPATH_DELIMITER);
7273 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7274 }
7275}
7276
7277/**
7278 * Returns the full path to the machine's log file for an given index.
7279 */
7280Utf8Str Machine::i_getLogFilename(ULONG idx)
7281{
7282 Utf8Str logFolder;
7283 getLogFolder(logFolder);
7284 Assert(logFolder.length());
7285
7286 Utf8Str log;
7287 if (idx == 0)
7288 log = Utf8StrFmt("%s%cVBox.log", logFolder.c_str(), RTPATH_DELIMITER);
7289#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7290 else if (idx == 1)
7291 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
7292 else
7293 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx - 1);
7294#else
7295 else
7296 log = Utf8StrFmt("%s%cVBox.log.%u", logFolder.c_str(), RTPATH_DELIMITER, idx);
7297#endif
7298 return log;
7299}
7300
7301/**
7302 * Returns the full path to the machine's hardened log file.
7303 */
7304Utf8Str Machine::i_getHardeningLogFilename(void)
7305{
7306 Utf8Str strFilename;
7307 getLogFolder(strFilename);
7308 Assert(strFilename.length());
7309 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7310 return strFilename;
7311}
7312
7313
7314/**
7315 * Composes a unique saved state filename based on the current system time. The filename is
7316 * granular to the second so this will work so long as no more than one snapshot is taken on
7317 * a machine per second.
7318 *
7319 * Before version 4.1, we used this formula for saved state files:
7320 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7321 * which no longer works because saved state files can now be shared between the saved state of the
7322 * "saved" machine and an online snapshot, and the following would cause problems:
7323 * 1) save machine
7324 * 2) create online snapshot from that machine state --> reusing saved state file
7325 * 3) save machine again --> filename would be reused, breaking the online snapshot
7326 *
7327 * So instead we now use a timestamp.
7328 *
7329 * @param strStateFilePath
7330 */
7331
7332void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7333{
7334 AutoCaller autoCaller(this);
7335 AssertComRCReturnVoid(autoCaller.rc());
7336
7337 {
7338 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7339 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7340 }
7341
7342 RTTIMESPEC ts;
7343 RTTimeNow(&ts);
7344 RTTIME time;
7345 RTTimeExplode(&time, &ts);
7346
7347 strStateFilePath += RTPATH_DELIMITER;
7348 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7349 time.i32Year, time.u8Month, time.u8MonthDay,
7350 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7351}
7352
7353/**
7354 * Returns whether at least one USB controller is present for the VM.
7355 */
7356bool Machine::i_isUSBControllerPresent()
7357{
7358 AutoCaller autoCaller(this);
7359 AssertComRCReturn(autoCaller.rc(), false);
7360
7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7362
7363 return (mUSBControllers->size() > 0);
7364}
7365
7366/**
7367 * @note Locks this object for writing, calls the client process
7368 * (inside the lock).
7369 */
7370HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7371 const Utf8Str &strFrontend,
7372 const std::vector<com::Utf8Str> &aEnvironmentChanges,
7373 ProgressProxy *aProgress)
7374{
7375 LogFlowThisFuncEnter();
7376
7377 AssertReturn(aControl, E_FAIL);
7378 AssertReturn(aProgress, E_FAIL);
7379 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7380
7381 AutoCaller autoCaller(this);
7382 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7383
7384 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7385
7386 if (!mData->mRegistered)
7387 return setError(E_UNEXPECTED,
7388 tr("The machine '%s' is not registered"),
7389 mUserData->s.strName.c_str());
7390
7391 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7392
7393 /* The process started when launching a VM with separate UI/VM processes is always
7394 * the UI process, i.e. needs special handling as it won't claim the session. */
7395 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7396
7397 if (fSeparate)
7398 {
7399 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7400 return setError(VBOX_E_INVALID_OBJECT_STATE,
7401 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7402 mUserData->s.strName.c_str());
7403 }
7404 else
7405 {
7406 if ( mData->mSession.mState == SessionState_Locked
7407 || mData->mSession.mState == SessionState_Spawning
7408 || mData->mSession.mState == SessionState_Unlocking)
7409 return setError(VBOX_E_INVALID_OBJECT_STATE,
7410 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7411 mUserData->s.strName.c_str());
7412
7413 /* may not be busy */
7414 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7415 }
7416
7417 /* Hardening logging */
7418#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7419 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7420 {
7421 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7422 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7423 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7424 {
7425 Utf8Str strStartupLogDir = strHardeningLogFile;
7426 strStartupLogDir.stripFilename();
7427 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7428 file without stripping the file. */
7429 }
7430 strSupHardeningLogArg.append(strHardeningLogFile);
7431
7432 /* Remove legacy log filename to avoid confusion. */
7433 Utf8Str strOldStartupLogFile;
7434 getLogFolder(strOldStartupLogFile);
7435 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7436 RTFileDelete(strOldStartupLogFile.c_str());
7437 }
7438#else
7439 Utf8Str strSupHardeningLogArg;
7440#endif
7441
7442 Utf8Str strAppOverride;
7443#ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7444 strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7445#endif
7446
7447 bool fUseVBoxSDS = false;
7448 Utf8Str strCanonicalName;
7449 if (false)
7450 { }
7451#ifdef VBOX_WITH_QTGUI
7452 else if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7453 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7454 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7455 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7456 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7457 {
7458 strCanonicalName = "GUI/Qt";
7459 fUseVBoxSDS = true;
7460 }
7461#endif
7462#ifdef VBOX_WITH_VBOXSDL
7463 else if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7464 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7465 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7466 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7467 {
7468 strCanonicalName = "GUI/SDL";
7469 fUseVBoxSDS = true;
7470 }
7471#endif
7472#ifdef VBOX_WITH_HEADLESS
7473 else if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7474 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7475 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */)
7476 {
7477 strCanonicalName = "headless";
7478 }
7479#endif
7480 else
7481 return setError(E_INVALIDARG, tr("Invalid frontend name: '%s'"), strFrontend.c_str());
7482
7483 Utf8Str idStr = mData->mUuid.toString();
7484 Utf8Str const &strMachineName = mUserData->s.strName;
7485 RTPROCESS pid = NIL_RTPROCESS;
7486
7487#if !defined(VBOX_WITH_SDS) || !defined(RT_OS_WINDOWS)
7488 RT_NOREF(fUseVBoxSDS);
7489#else
7490 DWORD idCallerSession = ~(DWORD)0;
7491 if (fUseVBoxSDS)
7492 {
7493 /*
7494 * The VBoxSDS should be used for process launching the VM with
7495 * GUI only if the caller and the VBoxSDS are in different Windows
7496 * sessions and the caller in the interactive one.
7497 */
7498 fUseVBoxSDS = false;
7499
7500 /* Get windows session of the current process. The process token used
7501 due to several reasons:
7502 1. The token is absent for the current thread except someone set it
7503 for us.
7504 2. Needs to get the id of the session where the process is started.
7505 We only need to do this once, though. */
7506 static DWORD s_idCurrentSession = ~(DWORD)0;
7507 DWORD idCurrentSession = s_idCurrentSession;
7508 if (idCurrentSession == ~(DWORD)0)
7509 {
7510 HANDLE hCurrentProcessToken = NULL;
7511 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_READ, &hCurrentProcessToken))
7512 {
7513 DWORD cbIgn = 0;
7514 if (GetTokenInformation(hCurrentProcessToken, TokenSessionId, &idCurrentSession, sizeof(idCurrentSession), &cbIgn))
7515 s_idCurrentSession = idCurrentSession;
7516 else
7517 {
7518 idCurrentSession = ~(DWORD)0;
7519 LogRelFunc(("GetTokenInformation/TokenSessionId on self failed: %u\n", GetLastError()));
7520 }
7521 CloseHandle(hCurrentProcessToken);
7522 }
7523 else
7524 LogRelFunc(("OpenProcessToken/self failed: %u\n", GetLastError()));
7525 }
7526
7527 /* get the caller's session */
7528 HRESULT hrc = CoImpersonateClient();
7529 if (SUCCEEDED(hrc))
7530 {
7531 HANDLE hCallerThreadToken;
7532 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_READ,
7533 FALSE /* OpenAsSelf - for impersonation at SecurityIdentification level */,
7534 &hCallerThreadToken))
7535 {
7536 SetLastError(NO_ERROR);
7537 DWORD cbIgn = 0;
7538 if (GetTokenInformation(hCallerThreadToken, TokenSessionId, &idCallerSession, sizeof(DWORD), &cbIgn))
7539 {
7540 /* Only need to use SDS if the session ID differs: */
7541 if (idCurrentSession != idCallerSession)
7542 {
7543 fUseVBoxSDS = false;
7544
7545 /* Obtain the groups the access token belongs to so we can see if the session is interactive: */
7546 DWORD cbTokenGroups = 0;
7547 PTOKEN_GROUPS pTokenGroups = NULL;
7548 if ( !GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, 0, &cbTokenGroups)
7549 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
7550 pTokenGroups = (PTOKEN_GROUPS)RTMemTmpAllocZ(cbTokenGroups);
7551 if (GetTokenInformation(hCallerThreadToken, TokenGroups, pTokenGroups, cbTokenGroups, &cbTokenGroups))
7552 {
7553 /* Well-known interactive SID: SECURITY_INTERACTIVE_RID, S-1-5-4: */
7554 SID_IDENTIFIER_AUTHORITY sidIdNTAuthority = SECURITY_NT_AUTHORITY;
7555 PSID pInteractiveSid = NULL;
7556 if (AllocateAndInitializeSid(&sidIdNTAuthority, 1, 4, 0, 0, 0, 0, 0, 0, 0, &pInteractiveSid))
7557 {
7558 /* Iterate over the groups looking for the interactive SID: */
7559 fUseVBoxSDS = false;
7560 for (DWORD dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++)
7561 if (EqualSid(pTokenGroups->Groups[dwIndex].Sid, pInteractiveSid))
7562 {
7563 fUseVBoxSDS = true;
7564 break;
7565 }
7566 FreeSid(pInteractiveSid);
7567 }
7568 }
7569 else
7570 LogRelFunc(("GetTokenInformation/TokenGroups failed: %u\n", GetLastError()));
7571 RTMemTmpFree(pTokenGroups);
7572 }
7573 }
7574 else
7575 LogRelFunc(("GetTokenInformation/TokenSessionId failed: %u\n", GetLastError()));
7576 CloseHandle(hCallerThreadToken);
7577 }
7578 else
7579 LogRelFunc(("OpenThreadToken/client failed: %u\n", GetLastError()));
7580 CoRevertToSelf();
7581 }
7582 else
7583 LogRelFunc(("CoImpersonateClient failed: %Rhrc\n", hrc));
7584 }
7585 if (fUseVBoxSDS)
7586 {
7587 /* connect to VBoxSDS */
7588 ComPtr<IVirtualBoxSDS> pVBoxSDS;
7589 HRESULT rc = pVBoxSDS.createLocalObject(CLSID_VirtualBoxSDS);
7590 if (FAILED(rc))
7591 return setError(rc, tr("Failed to start the machine '%s'. A connection to VBoxSDS cannot be established"),
7592 strMachineName.c_str());
7593
7594 /* By default the RPC_C_IMP_LEVEL_IDENTIFY is used for impersonation the client. It allows
7595 ACL checking but restricts an access to system objects e.g. files. Call to CoSetProxyBlanket
7596 elevates the impersonation level up to RPC_C_IMP_LEVEL_IMPERSONATE allowing the VBoxSDS
7597 service to access the files. */
7598 rc = CoSetProxyBlanket(pVBoxSDS,
7599 RPC_C_AUTHN_DEFAULT,
7600 RPC_C_AUTHZ_DEFAULT,
7601 COLE_DEFAULT_PRINCIPAL,
7602 RPC_C_AUTHN_LEVEL_DEFAULT,
7603 RPC_C_IMP_LEVEL_IMPERSONATE,
7604 NULL,
7605 EOAC_DEFAULT);
7606 if (FAILED(rc))
7607 return setError(rc, tr("Failed to start the machine '%s'. CoSetProxyBlanket failed"), strMachineName.c_str());
7608
7609 size_t const cEnvVars = aEnvironmentChanges.size();
7610 com::SafeArray<IN_BSTR> aBstrEnvironmentChanges(cEnvVars);
7611 for (size_t i = 0; i < cEnvVars; i++)
7612 aBstrEnvironmentChanges[i] = Bstr(aEnvironmentChanges[i]).raw();
7613
7614 ULONG uPid = 0;
7615 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7616 ComSafeArrayAsInParam(aBstrEnvironmentChanges), Bstr(strSupHardeningLogArg).raw(),
7617 idCallerSession, &uPid);
7618 if (FAILED(rc))
7619 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7620 pid = (RTPROCESS)uPid;
7621 }
7622 else
7623#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7624 {
7625 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, aEnvironmentChanges, strSupHardeningLogArg,
7626 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7627 if (RT_FAILURE(vrc))
7628 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7629 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7630 }
7631
7632 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7633 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7634
7635 if (!fSeparate)
7636 {
7637 /*
7638 * Note that we don't release the lock here before calling the client,
7639 * because it doesn't need to call us back if called with a NULL argument.
7640 * Releasing the lock here is dangerous because we didn't prepare the
7641 * launch data yet, but the client we've just started may happen to be
7642 * too fast and call LockMachine() that will fail (because of PID, etc.),
7643 * so that the Machine will never get out of the Spawning session state.
7644 */
7645
7646 /* inform the session that it will be a remote one */
7647 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7648#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7649 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7650#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7651 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7652#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7653 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7654
7655 if (FAILED(rc))
7656 {
7657 /* restore the session state */
7658 mData->mSession.mState = SessionState_Unlocked;
7659 alock.release();
7660 mParent->i_addProcessToReap(pid);
7661 /* The failure may occur w/o any error info (from RPC), so provide one */
7662 return setError(VBOX_E_VM_ERROR,
7663 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7664 }
7665
7666 /* attach launch data to the machine */
7667 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7668 mData->mSession.mRemoteControls.push_back(aControl);
7669 mData->mSession.mProgress = aProgress;
7670 mData->mSession.mPID = pid;
7671 mData->mSession.mState = SessionState_Spawning;
7672 Assert(strCanonicalName.isNotEmpty());
7673 mData->mSession.mName = strCanonicalName;
7674 }
7675 else
7676 {
7677 /* For separate UI process we declare the launch as completed instantly, as the
7678 * actual headless VM start may or may not come. No point in remembering anything
7679 * yet, as what matters for us is when the headless VM gets started. */
7680 aProgress->i_notifyComplete(S_OK);
7681 }
7682
7683 alock.release();
7684 mParent->i_addProcessToReap(pid);
7685
7686 LogFlowThisFuncLeave();
7687 return S_OK;
7688}
7689
7690/**
7691 * Returns @c true if the given session machine instance has an open direct
7692 * session (and optionally also for direct sessions which are closing) and
7693 * returns the session control machine instance if so.
7694 *
7695 * Note that when the method returns @c false, the arguments remain unchanged.
7696 *
7697 * @param aMachine Session machine object.
7698 * @param aControl Direct session control object (optional).
7699 * @param aRequireVM If true then only allow VM sessions.
7700 * @param aAllowClosing If true then additionally a session which is currently
7701 * being closed will also be allowed.
7702 *
7703 * @note locks this object for reading.
7704 */
7705bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7706 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7707 bool aRequireVM /*= false*/,
7708 bool aAllowClosing /*= false*/)
7709{
7710 AutoLimitedCaller autoCaller(this);
7711 AssertComRCReturn(autoCaller.rc(), false);
7712
7713 /* just return false for inaccessible machines */
7714 if (getObjectState().getState() != ObjectState::Ready)
7715 return false;
7716
7717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7718
7719 if ( ( mData->mSession.mState == SessionState_Locked
7720 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7721 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7722 )
7723 {
7724 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7725
7726 aMachine = mData->mSession.mMachine;
7727
7728 if (aControl != NULL)
7729 *aControl = mData->mSession.mDirectControl;
7730
7731 return true;
7732 }
7733
7734 return false;
7735}
7736
7737/**
7738 * Returns @c true if the given machine has an spawning direct session.
7739 *
7740 * @note locks this object for reading.
7741 */
7742bool Machine::i_isSessionSpawning()
7743{
7744 AutoLimitedCaller autoCaller(this);
7745 AssertComRCReturn(autoCaller.rc(), false);
7746
7747 /* just return false for inaccessible machines */
7748 if (getObjectState().getState() != ObjectState::Ready)
7749 return false;
7750
7751 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7752
7753 if (mData->mSession.mState == SessionState_Spawning)
7754 return true;
7755
7756 return false;
7757}
7758
7759/**
7760 * Called from the client watcher thread to check for unexpected client process
7761 * death during Session_Spawning state (e.g. before it successfully opened a
7762 * direct session).
7763 *
7764 * On Win32 and on OS/2, this method is called only when we've got the
7765 * direct client's process termination notification, so it always returns @c
7766 * true.
7767 *
7768 * On other platforms, this method returns @c true if the client process is
7769 * terminated and @c false if it's still alive.
7770 *
7771 * @note Locks this object for writing.
7772 */
7773bool Machine::i_checkForSpawnFailure()
7774{
7775 AutoCaller autoCaller(this);
7776 if (!autoCaller.isOk())
7777 {
7778 /* nothing to do */
7779 LogFlowThisFunc(("Already uninitialized!\n"));
7780 return true;
7781 }
7782
7783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7784
7785 if (mData->mSession.mState != SessionState_Spawning)
7786 {
7787 /* nothing to do */
7788 LogFlowThisFunc(("Not spawning any more!\n"));
7789 return true;
7790 }
7791
7792 HRESULT rc = S_OK;
7793
7794 /* PID not yet initialized, skip check. */
7795 if (mData->mSession.mPID == NIL_RTPROCESS)
7796 return false;
7797
7798 RTPROCSTATUS status;
7799 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7800
7801 if (vrc != VERR_PROCESS_RUNNING)
7802 {
7803 Utf8Str strExtraInfo;
7804
7805#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7806 /* If the startup logfile exists and is of non-zero length, tell the
7807 user to look there for more details to encourage them to attach it
7808 when reporting startup issues. */
7809 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7810 uint64_t cbStartupLogFile = 0;
7811 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7812 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7813 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7814#endif
7815
7816 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7817 rc = setError(E_FAIL,
7818 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7819 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7820 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7821 rc = setError(E_FAIL,
7822 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7823 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7824 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7825 rc = setError(E_FAIL,
7826 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7827 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7828 else
7829 rc = setErrorBoth(E_FAIL, vrc,
7830 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7831 i_getName().c_str(), vrc, strExtraInfo.c_str());
7832 }
7833
7834 if (FAILED(rc))
7835 {
7836 /* Close the remote session, remove the remote control from the list
7837 * and reset session state to Closed (@note keep the code in sync with
7838 * the relevant part in LockMachine()). */
7839
7840 Assert(mData->mSession.mRemoteControls.size() == 1);
7841 if (mData->mSession.mRemoteControls.size() == 1)
7842 {
7843 ErrorInfoKeeper eik;
7844 mData->mSession.mRemoteControls.front()->Uninitialize();
7845 }
7846
7847 mData->mSession.mRemoteControls.clear();
7848 mData->mSession.mState = SessionState_Unlocked;
7849
7850 /* finalize the progress after setting the state */
7851 if (!mData->mSession.mProgress.isNull())
7852 {
7853 mData->mSession.mProgress->notifyComplete(rc);
7854 mData->mSession.mProgress.setNull();
7855 }
7856
7857 mData->mSession.mPID = NIL_RTPROCESS;
7858
7859 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7860 return true;
7861 }
7862
7863 return false;
7864}
7865
7866/**
7867 * Checks whether the machine can be registered. If so, commits and saves
7868 * all settings.
7869 *
7870 * @note Must be called from mParent's write lock. Locks this object and
7871 * children for writing.
7872 */
7873HRESULT Machine::i_prepareRegister()
7874{
7875 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7876
7877 AutoLimitedCaller autoCaller(this);
7878 AssertComRCReturnRC(autoCaller.rc());
7879
7880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7881
7882 /* wait for state dependents to drop to zero */
7883 i_ensureNoStateDependencies();
7884
7885 if (!mData->mAccessible)
7886 return setError(VBOX_E_INVALID_OBJECT_STATE,
7887 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7888 mUserData->s.strName.c_str(),
7889 mData->mUuid.toString().c_str());
7890
7891 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7892
7893 if (mData->mRegistered)
7894 return setError(VBOX_E_INVALID_OBJECT_STATE,
7895 tr("The machine '%s' with UUID {%s} is already registered"),
7896 mUserData->s.strName.c_str(),
7897 mData->mUuid.toString().c_str());
7898
7899 HRESULT rc = S_OK;
7900
7901 // Ensure the settings are saved. If we are going to be registered and
7902 // no config file exists yet, create it by calling i_saveSettings() too.
7903 if ( (mData->flModifications)
7904 || (!mData->pMachineConfigFile->fileExists())
7905 )
7906 {
7907 rc = i_saveSettings(NULL);
7908 // no need to check whether VirtualBox.xml needs saving too since
7909 // we can't have a machine XML file rename pending
7910 if (FAILED(rc)) return rc;
7911 }
7912
7913 /* more config checking goes here */
7914
7915 if (SUCCEEDED(rc))
7916 {
7917 /* we may have had implicit modifications we want to fix on success */
7918 i_commit();
7919
7920 mData->mRegistered = true;
7921 }
7922 else
7923 {
7924 /* we may have had implicit modifications we want to cancel on failure*/
7925 i_rollback(false /* aNotify */);
7926 }
7927
7928 return rc;
7929}
7930
7931/**
7932 * Increases the number of objects dependent on the machine state or on the
7933 * registered state. Guarantees that these two states will not change at least
7934 * until #i_releaseStateDependency() is called.
7935 *
7936 * Depending on the @a aDepType value, additional state checks may be made.
7937 * These checks will set extended error info on failure. See
7938 * #i_checkStateDependency() for more info.
7939 *
7940 * If this method returns a failure, the dependency is not added and the caller
7941 * is not allowed to rely on any particular machine state or registration state
7942 * value and may return the failed result code to the upper level.
7943 *
7944 * @param aDepType Dependency type to add.
7945 * @param aState Current machine state (NULL if not interested).
7946 * @param aRegistered Current registered state (NULL if not interested).
7947 *
7948 * @note Locks this object for writing.
7949 */
7950HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7951 MachineState_T *aState /* = NULL */,
7952 BOOL *aRegistered /* = NULL */)
7953{
7954 AutoCaller autoCaller(this);
7955 AssertComRCReturnRC(autoCaller.rc());
7956
7957 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7958
7959 HRESULT rc = i_checkStateDependency(aDepType);
7960 if (FAILED(rc)) return rc;
7961
7962 {
7963 if (mData->mMachineStateChangePending != 0)
7964 {
7965 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7966 * drop to zero so don't add more. It may make sense to wait a bit
7967 * and retry before reporting an error (since the pending state
7968 * transition should be really quick) but let's just assert for
7969 * now to see if it ever happens on practice. */
7970
7971 AssertFailed();
7972
7973 return setError(E_ACCESSDENIED,
7974 tr("Machine state change is in progress. Please retry the operation later."));
7975 }
7976
7977 ++mData->mMachineStateDeps;
7978 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7979 }
7980
7981 if (aState)
7982 *aState = mData->mMachineState;
7983 if (aRegistered)
7984 *aRegistered = mData->mRegistered;
7985
7986 return S_OK;
7987}
7988
7989/**
7990 * Decreases the number of objects dependent on the machine state.
7991 * Must always complete the #i_addStateDependency() call after the state
7992 * dependency is no more necessary.
7993 */
7994void Machine::i_releaseStateDependency()
7995{
7996 AutoCaller autoCaller(this);
7997 AssertComRCReturnVoid(autoCaller.rc());
7998
7999 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8000
8001 /* releaseStateDependency() w/o addStateDependency()? */
8002 AssertReturnVoid(mData->mMachineStateDeps != 0);
8003 -- mData->mMachineStateDeps;
8004
8005 if (mData->mMachineStateDeps == 0)
8006 {
8007 /* inform i_ensureNoStateDependencies() that there are no more deps */
8008 if (mData->mMachineStateChangePending != 0)
8009 {
8010 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8011 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8012 }
8013 }
8014}
8015
8016Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8017{
8018 /* start with nothing found */
8019 Utf8Str strResult("");
8020
8021 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8022
8023 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8024 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8025 // found:
8026 strResult = it->second; // source is a Utf8Str
8027
8028 return strResult;
8029}
8030
8031// protected methods
8032/////////////////////////////////////////////////////////////////////////////
8033
8034/**
8035 * Performs machine state checks based on the @a aDepType value. If a check
8036 * fails, this method will set extended error info, otherwise it will return
8037 * S_OK. It is supposed, that on failure, the caller will immediately return
8038 * the return value of this method to the upper level.
8039 *
8040 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8041 *
8042 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8043 * current state of this machine object allows to change settings of the
8044 * machine (i.e. the machine is not registered, or registered but not running
8045 * and not saved). It is useful to call this method from Machine setters
8046 * before performing any change.
8047 *
8048 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8049 * as for MutableStateDep except that if the machine is saved, S_OK is also
8050 * returned. This is useful in setters which allow changing machine
8051 * properties when it is in the saved state.
8052 *
8053 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8054 * if the current state of this machine object allows to change runtime
8055 * changeable settings of the machine (i.e. the machine is not registered, or
8056 * registered but either running or not running and not saved). It is useful
8057 * to call this method from Machine setters before performing any changes to
8058 * runtime changeable settings.
8059 *
8060 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8061 * the same as for MutableOrRunningStateDep except that if the machine is
8062 * saved, S_OK is also returned. This is useful in setters which allow
8063 * changing runtime and saved state changeable machine properties.
8064 *
8065 * @param aDepType Dependency type to check.
8066 *
8067 * @note Non Machine based classes should use #i_addStateDependency() and
8068 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8069 * template.
8070 *
8071 * @note This method must be called from under this object's read or write
8072 * lock.
8073 */
8074HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8075{
8076 switch (aDepType)
8077 {
8078 case AnyStateDep:
8079 {
8080 break;
8081 }
8082 case MutableStateDep:
8083 {
8084 if ( mData->mRegistered
8085 && ( !i_isSessionMachine()
8086 || ( mData->mMachineState != MachineState_Aborted
8087 && mData->mMachineState != MachineState_Teleported
8088 && mData->mMachineState != MachineState_PoweredOff
8089 )
8090 )
8091 )
8092 return setError(VBOX_E_INVALID_VM_STATE,
8093 tr("The machine is not mutable (state is %s)"),
8094 Global::stringifyMachineState(mData->mMachineState));
8095 break;
8096 }
8097 case MutableOrSavedStateDep:
8098 {
8099 if ( mData->mRegistered
8100 && ( !i_isSessionMachine()
8101 || ( mData->mMachineState != MachineState_Aborted
8102 && mData->mMachineState != MachineState_Teleported
8103 && mData->mMachineState != MachineState_Saved
8104 && mData->mMachineState != MachineState_PoweredOff
8105 )
8106 )
8107 )
8108 return setError(VBOX_E_INVALID_VM_STATE,
8109 tr("The machine is not mutable or saved (state is %s)"),
8110 Global::stringifyMachineState(mData->mMachineState));
8111 break;
8112 }
8113 case MutableOrRunningStateDep:
8114 {
8115 if ( mData->mRegistered
8116 && ( !i_isSessionMachine()
8117 || ( mData->mMachineState != MachineState_Aborted
8118 && mData->mMachineState != MachineState_Teleported
8119 && mData->mMachineState != MachineState_PoweredOff
8120 && !Global::IsOnline(mData->mMachineState)
8121 )
8122 )
8123 )
8124 return setError(VBOX_E_INVALID_VM_STATE,
8125 tr("The machine is not mutable or running (state is %s)"),
8126 Global::stringifyMachineState(mData->mMachineState));
8127 break;
8128 }
8129 case MutableOrSavedOrRunningStateDep:
8130 {
8131 if ( mData->mRegistered
8132 && ( !i_isSessionMachine()
8133 || ( mData->mMachineState != MachineState_Aborted
8134 && mData->mMachineState != MachineState_Teleported
8135 && mData->mMachineState != MachineState_Saved
8136 && mData->mMachineState != MachineState_PoweredOff
8137 && !Global::IsOnline(mData->mMachineState)
8138 )
8139 )
8140 )
8141 return setError(VBOX_E_INVALID_VM_STATE,
8142 tr("The machine is not mutable, saved or running (state is %s)"),
8143 Global::stringifyMachineState(mData->mMachineState));
8144 break;
8145 }
8146 }
8147
8148 return S_OK;
8149}
8150
8151/**
8152 * Helper to initialize all associated child objects and allocate data
8153 * structures.
8154 *
8155 * This method must be called as a part of the object's initialization procedure
8156 * (usually done in the #init() method).
8157 *
8158 * @note Must be called only from #init() or from #i_registeredInit().
8159 */
8160HRESULT Machine::initDataAndChildObjects()
8161{
8162 AutoCaller autoCaller(this);
8163 AssertComRCReturnRC(autoCaller.rc());
8164 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8165 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8166
8167 AssertReturn(!mData->mAccessible, E_FAIL);
8168
8169 /* allocate data structures */
8170 mSSData.allocate();
8171 mUserData.allocate();
8172 mHWData.allocate();
8173 mMediumAttachments.allocate();
8174 mStorageControllers.allocate();
8175 mUSBControllers.allocate();
8176
8177 /* initialize mOSTypeId */
8178 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8179
8180/** @todo r=bird: init() methods never fails, right? Why don't we make them
8181 * return void then! */
8182
8183 /* create associated BIOS settings object */
8184 unconst(mBIOSSettings).createObject();
8185 mBIOSSettings->init(this);
8186
8187 /* create associated record settings object */
8188 unconst(mRecordingSettings).createObject();
8189 mRecordingSettings->init(this);
8190
8191 /* create an associated VRDE object (default is disabled) */
8192 unconst(mVRDEServer).createObject();
8193 mVRDEServer->init(this);
8194
8195 /* create associated serial port objects */
8196 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8197 {
8198 unconst(mSerialPorts[slot]).createObject();
8199 mSerialPorts[slot]->init(this, slot);
8200 }
8201
8202 /* create associated parallel port objects */
8203 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8204 {
8205 unconst(mParallelPorts[slot]).createObject();
8206 mParallelPorts[slot]->init(this, slot);
8207 }
8208
8209 /* create the audio adapter object (always present, default is disabled) */
8210 unconst(mAudioAdapter).createObject();
8211 mAudioAdapter->init(this);
8212
8213 /* create the USB device filters object (always present) */
8214 unconst(mUSBDeviceFilters).createObject();
8215 mUSBDeviceFilters->init(this);
8216
8217 /* create associated network adapter objects */
8218 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8219 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8220 {
8221 unconst(mNetworkAdapters[slot]).createObject();
8222 mNetworkAdapters[slot]->init(this, slot);
8223 }
8224
8225 /* create the bandwidth control */
8226 unconst(mBandwidthControl).createObject();
8227 mBandwidthControl->init(this);
8228
8229 return S_OK;
8230}
8231
8232/**
8233 * Helper to uninitialize all associated child objects and to free all data
8234 * structures.
8235 *
8236 * This method must be called as a part of the object's uninitialization
8237 * procedure (usually done in the #uninit() method).
8238 *
8239 * @note Must be called only from #uninit() or from #i_registeredInit().
8240 */
8241void Machine::uninitDataAndChildObjects()
8242{
8243 AutoCaller autoCaller(this);
8244 AssertComRCReturnVoid(autoCaller.rc());
8245 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8246 || getObjectState().getState() == ObjectState::Limited);
8247
8248 /* tell all our other child objects we've been uninitialized */
8249 if (mBandwidthControl)
8250 {
8251 mBandwidthControl->uninit();
8252 unconst(mBandwidthControl).setNull();
8253 }
8254
8255 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8256 {
8257 if (mNetworkAdapters[slot])
8258 {
8259 mNetworkAdapters[slot]->uninit();
8260 unconst(mNetworkAdapters[slot]).setNull();
8261 }
8262 }
8263
8264 if (mUSBDeviceFilters)
8265 {
8266 mUSBDeviceFilters->uninit();
8267 unconst(mUSBDeviceFilters).setNull();
8268 }
8269
8270 if (mAudioAdapter)
8271 {
8272 mAudioAdapter->uninit();
8273 unconst(mAudioAdapter).setNull();
8274 }
8275
8276 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8277 {
8278 if (mParallelPorts[slot])
8279 {
8280 mParallelPorts[slot]->uninit();
8281 unconst(mParallelPorts[slot]).setNull();
8282 }
8283 }
8284
8285 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8286 {
8287 if (mSerialPorts[slot])
8288 {
8289 mSerialPorts[slot]->uninit();
8290 unconst(mSerialPorts[slot]).setNull();
8291 }
8292 }
8293
8294 if (mVRDEServer)
8295 {
8296 mVRDEServer->uninit();
8297 unconst(mVRDEServer).setNull();
8298 }
8299
8300 if (mBIOSSettings)
8301 {
8302 mBIOSSettings->uninit();
8303 unconst(mBIOSSettings).setNull();
8304 }
8305
8306 if (mRecordingSettings)
8307 {
8308 mRecordingSettings->uninit();
8309 unconst(mRecordingSettings).setNull();
8310 }
8311
8312 /* Deassociate media (only when a real Machine or a SnapshotMachine
8313 * instance is uninitialized; SessionMachine instances refer to real
8314 * Machine media). This is necessary for a clean re-initialization of
8315 * the VM after successfully re-checking the accessibility state. Note
8316 * that in case of normal Machine or SnapshotMachine uninitialization (as
8317 * a result of unregistering or deleting the snapshot), outdated media
8318 * attachments will already be uninitialized and deleted, so this
8319 * code will not affect them. */
8320 if ( !mMediumAttachments.isNull()
8321 && !i_isSessionMachine()
8322 )
8323 {
8324 for (MediumAttachmentList::const_iterator
8325 it = mMediumAttachments->begin();
8326 it != mMediumAttachments->end();
8327 ++it)
8328 {
8329 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8330 if (pMedium.isNull())
8331 continue;
8332 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8333 AssertComRC(rc);
8334 }
8335 }
8336
8337 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8338 {
8339 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8340 if (mData->mFirstSnapshot)
8341 {
8342 // snapshots tree is protected by machine write lock; strictly
8343 // this isn't necessary here since we're deleting the entire
8344 // machine, but otherwise we assert in Snapshot::uninit()
8345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8346 mData->mFirstSnapshot->uninit();
8347 mData->mFirstSnapshot.setNull();
8348 }
8349
8350 mData->mCurrentSnapshot.setNull();
8351 }
8352
8353 /* free data structures (the essential mData structure is not freed here
8354 * since it may be still in use) */
8355 mMediumAttachments.free();
8356 mStorageControllers.free();
8357 mUSBControllers.free();
8358 mHWData.free();
8359 mUserData.free();
8360 mSSData.free();
8361}
8362
8363/**
8364 * Returns a pointer to the Machine object for this machine that acts like a
8365 * parent for complex machine data objects such as shared folders, etc.
8366 *
8367 * For primary Machine objects and for SnapshotMachine objects, returns this
8368 * object's pointer itself. For SessionMachine objects, returns the peer
8369 * (primary) machine pointer.
8370 */
8371Machine *Machine::i_getMachine()
8372{
8373 if (i_isSessionMachine())
8374 return (Machine*)mPeer;
8375 return this;
8376}
8377
8378/**
8379 * Makes sure that there are no machine state dependents. If necessary, waits
8380 * for the number of dependents to drop to zero.
8381 *
8382 * Make sure this method is called from under this object's write lock to
8383 * guarantee that no new dependents may be added when this method returns
8384 * control to the caller.
8385 *
8386 * @note Locks this object for writing. The lock will be released while waiting
8387 * (if necessary).
8388 *
8389 * @warning To be used only in methods that change the machine state!
8390 */
8391void Machine::i_ensureNoStateDependencies()
8392{
8393 AssertReturnVoid(isWriteLockOnCurrentThread());
8394
8395 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8396
8397 /* Wait for all state dependents if necessary */
8398 if (mData->mMachineStateDeps != 0)
8399 {
8400 /* lazy semaphore creation */
8401 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8402 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8403
8404 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8405 mData->mMachineStateDeps));
8406
8407 ++mData->mMachineStateChangePending;
8408
8409 /* reset the semaphore before waiting, the last dependent will signal
8410 * it */
8411 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8412
8413 alock.release();
8414
8415 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8416
8417 alock.acquire();
8418
8419 -- mData->mMachineStateChangePending;
8420 }
8421}
8422
8423/**
8424 * Changes the machine state and informs callbacks.
8425 *
8426 * This method is not intended to fail so it either returns S_OK or asserts (and
8427 * returns a failure).
8428 *
8429 * @note Locks this object for writing.
8430 */
8431HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8432{
8433 LogFlowThisFuncEnter();
8434 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8435 Assert(aMachineState != MachineState_Null);
8436
8437 AutoCaller autoCaller(this);
8438 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8439
8440 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8441
8442 /* wait for state dependents to drop to zero */
8443 i_ensureNoStateDependencies();
8444
8445 MachineState_T const enmOldState = mData->mMachineState;
8446 if (enmOldState != aMachineState)
8447 {
8448 mData->mMachineState = aMachineState;
8449 RTTimeNow(&mData->mLastStateChange);
8450
8451#ifdef VBOX_WITH_DTRACE_R3_MAIN
8452 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8453#endif
8454 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8455 }
8456
8457 LogFlowThisFuncLeave();
8458 return S_OK;
8459}
8460
8461/**
8462 * Searches for a shared folder with the given logical name
8463 * in the collection of shared folders.
8464 *
8465 * @param aName logical name of the shared folder
8466 * @param aSharedFolder where to return the found object
8467 * @param aSetError whether to set the error info if the folder is
8468 * not found
8469 * @return
8470 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8471 *
8472 * @note
8473 * must be called from under the object's lock!
8474 */
8475HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8476 ComObjPtr<SharedFolder> &aSharedFolder,
8477 bool aSetError /* = false */)
8478{
8479 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8480 for (HWData::SharedFolderList::const_iterator
8481 it = mHWData->mSharedFolders.begin();
8482 it != mHWData->mSharedFolders.end();
8483 ++it)
8484 {
8485 SharedFolder *pSF = *it;
8486 AutoCaller autoCaller(pSF);
8487 if (pSF->i_getName() == aName)
8488 {
8489 aSharedFolder = pSF;
8490 rc = S_OK;
8491 break;
8492 }
8493 }
8494
8495 if (aSetError && FAILED(rc))
8496 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8497
8498 return rc;
8499}
8500
8501/**
8502 * Initializes all machine instance data from the given settings structures
8503 * from XML. The exception is the machine UUID which needs special handling
8504 * depending on the caller's use case, so the caller needs to set that herself.
8505 *
8506 * This gets called in several contexts during machine initialization:
8507 *
8508 * -- When machine XML exists on disk already and needs to be loaded into memory,
8509 * for example, from #i_registeredInit() to load all registered machines on
8510 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8511 * attached to the machine should be part of some media registry already.
8512 *
8513 * -- During OVF import, when a machine config has been constructed from an
8514 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8515 * ensure that the media listed as attachments in the config (which have
8516 * been imported from the OVF) receive the correct registry ID.
8517 *
8518 * -- During VM cloning.
8519 *
8520 * @param config Machine settings from XML.
8521 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8522 * for each attached medium in the config.
8523 * @return
8524 */
8525HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8526 const Guid *puuidRegistry)
8527{
8528 // copy name, description, OS type, teleporter, UTC etc.
8529 mUserData->s = config.machineUserData;
8530
8531 // look up the object by Id to check it is valid
8532 ComObjPtr<GuestOSType> pGuestOSType;
8533 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8534 if (!pGuestOSType.isNull())
8535 mUserData->s.strOsType = pGuestOSType->i_id();
8536
8537 // stateFile (optional)
8538 if (config.strStateFile.isEmpty())
8539 mSSData->strStateFilePath.setNull();
8540 else
8541 {
8542 Utf8Str stateFilePathFull(config.strStateFile);
8543 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8544 if (RT_FAILURE(vrc))
8545 return setErrorBoth(E_FAIL, vrc,
8546 tr("Invalid saved state file path '%s' (%Rrc)"),
8547 config.strStateFile.c_str(),
8548 vrc);
8549 mSSData->strStateFilePath = stateFilePathFull;
8550 }
8551
8552 // snapshot folder needs special processing so set it again
8553 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8554 if (FAILED(rc)) return rc;
8555
8556 /* Copy the extra data items (config may or may not be the same as
8557 * mData->pMachineConfigFile) if necessary. When loading the XML files
8558 * from disk they are the same, but not for OVF import. */
8559 if (mData->pMachineConfigFile != &config)
8560 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8561
8562 /* currentStateModified (optional, default is true) */
8563 mData->mCurrentStateModified = config.fCurrentStateModified;
8564
8565 mData->mLastStateChange = config.timeLastStateChange;
8566
8567 /*
8568 * note: all mUserData members must be assigned prior this point because
8569 * we need to commit changes in order to let mUserData be shared by all
8570 * snapshot machine instances.
8571 */
8572 mUserData.commitCopy();
8573
8574 // machine registry, if present (must be loaded before snapshots)
8575 if (config.canHaveOwnMediaRegistry())
8576 {
8577 // determine machine folder
8578 Utf8Str strMachineFolder = i_getSettingsFileFull();
8579 strMachineFolder.stripFilename();
8580 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8581 config.mediaRegistry,
8582 strMachineFolder);
8583 if (FAILED(rc)) return rc;
8584 }
8585
8586 /* Snapshot node (optional) */
8587 size_t cRootSnapshots;
8588 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8589 {
8590 // there must be only one root snapshot
8591 Assert(cRootSnapshots == 1);
8592
8593 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8594
8595 rc = i_loadSnapshot(snap,
8596 config.uuidCurrentSnapshot,
8597 NULL); // no parent == first snapshot
8598 if (FAILED(rc)) return rc;
8599 }
8600
8601 // hardware data
8602 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8603 if (FAILED(rc)) return rc;
8604
8605 /*
8606 * NOTE: the assignment below must be the last thing to do,
8607 * otherwise it will be not possible to change the settings
8608 * somewhere in the code above because all setters will be
8609 * blocked by i_checkStateDependency(MutableStateDep).
8610 */
8611
8612 /* set the machine state to Aborted or Saved when appropriate */
8613 if (config.fAborted)
8614 {
8615 mSSData->strStateFilePath.setNull();
8616
8617 /* no need to use i_setMachineState() during init() */
8618 mData->mMachineState = MachineState_Aborted;
8619 }
8620 else if (!mSSData->strStateFilePath.isEmpty())
8621 {
8622 /* no need to use i_setMachineState() during init() */
8623 mData->mMachineState = MachineState_Saved;
8624 }
8625
8626 // after loading settings, we are no longer different from the XML on disk
8627 mData->flModifications = 0;
8628
8629 return S_OK;
8630}
8631
8632/**
8633 * Recursively loads all snapshots starting from the given.
8634 *
8635 * @param data snapshot settings.
8636 * @param aCurSnapshotId Current snapshot ID from the settings file.
8637 * @param aParentSnapshot Parent snapshot.
8638 */
8639HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8640 const Guid &aCurSnapshotId,
8641 Snapshot *aParentSnapshot)
8642{
8643 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8644 AssertReturn(!i_isSessionMachine(), E_FAIL);
8645
8646 HRESULT rc = S_OK;
8647
8648 Utf8Str strStateFile;
8649 if (!data.strStateFile.isEmpty())
8650 {
8651 /* optional */
8652 strStateFile = data.strStateFile;
8653 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8654 if (RT_FAILURE(vrc))
8655 return setErrorBoth(E_FAIL, vrc,
8656 tr("Invalid saved state file path '%s' (%Rrc)"),
8657 strStateFile.c_str(),
8658 vrc);
8659 }
8660
8661 /* create a snapshot machine object */
8662 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8663 pSnapshotMachine.createObject();
8664 rc = pSnapshotMachine->initFromSettings(this,
8665 data.hardware,
8666 &data.debugging,
8667 &data.autostart,
8668 data.uuid.ref(),
8669 strStateFile);
8670 if (FAILED(rc)) return rc;
8671
8672 /* create a snapshot object */
8673 ComObjPtr<Snapshot> pSnapshot;
8674 pSnapshot.createObject();
8675 /* initialize the snapshot */
8676 rc = pSnapshot->init(mParent, // VirtualBox object
8677 data.uuid,
8678 data.strName,
8679 data.strDescription,
8680 data.timestamp,
8681 pSnapshotMachine,
8682 aParentSnapshot);
8683 if (FAILED(rc)) return rc;
8684
8685 /* memorize the first snapshot if necessary */
8686 if (!mData->mFirstSnapshot)
8687 mData->mFirstSnapshot = pSnapshot;
8688
8689 /* memorize the current snapshot when appropriate */
8690 if ( !mData->mCurrentSnapshot
8691 && pSnapshot->i_getId() == aCurSnapshotId
8692 )
8693 mData->mCurrentSnapshot = pSnapshot;
8694
8695 // now create the children
8696 for (settings::SnapshotsList::const_iterator
8697 it = data.llChildSnapshots.begin();
8698 it != data.llChildSnapshots.end();
8699 ++it)
8700 {
8701 const settings::Snapshot &childData = *it;
8702 // recurse
8703 rc = i_loadSnapshot(childData,
8704 aCurSnapshotId,
8705 pSnapshot); // parent = the one we created above
8706 if (FAILED(rc)) return rc;
8707 }
8708
8709 return rc;
8710}
8711
8712/**
8713 * Loads settings into mHWData.
8714 *
8715 * @param puuidRegistry Registry ID.
8716 * @param puuidSnapshot Snapshot ID
8717 * @param data Reference to the hardware settings.
8718 * @param pDbg Pointer to the debugging settings.
8719 * @param pAutostart Pointer to the autostart settings.
8720 */
8721HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8722 const Guid *puuidSnapshot,
8723 const settings::Hardware &data,
8724 const settings::Debugging *pDbg,
8725 const settings::Autostart *pAutostart)
8726{
8727 AssertReturn(!i_isSessionMachine(), E_FAIL);
8728
8729 HRESULT rc = S_OK;
8730
8731 try
8732 {
8733 ComObjPtr<GuestOSType> pGuestOSType;
8734 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8735
8736 /* The hardware version attribute (optional). */
8737 mHWData->mHWVersion = data.strVersion;
8738 mHWData->mHardwareUUID = data.uuid;
8739
8740 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8741 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8742 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8743 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8744 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8745 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8746 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8747 mHWData->mPAEEnabled = data.fPAE;
8748 mHWData->mLongMode = data.enmLongMode;
8749 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8750 mHWData->mAPIC = data.fAPIC;
8751 mHWData->mX2APIC = data.fX2APIC;
8752 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8753 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8754 mHWData->mSpecCtrl = data.fSpecCtrl;
8755 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8756 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8757 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8758 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8759 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8760 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8761 mHWData->mCPUCount = data.cCPUs;
8762 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8763 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8764 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8765 mHWData->mCpuProfile = data.strCpuProfile;
8766
8767 // cpu
8768 if (mHWData->mCPUHotPlugEnabled)
8769 {
8770 for (settings::CpuList::const_iterator
8771 it = data.llCpus.begin();
8772 it != data.llCpus.end();
8773 ++it)
8774 {
8775 const settings::Cpu &cpu = *it;
8776
8777 mHWData->mCPUAttached[cpu.ulId] = true;
8778 }
8779 }
8780
8781 // cpuid leafs
8782 for (settings::CpuIdLeafsList::const_iterator
8783 it = data.llCpuIdLeafs.begin();
8784 it != data.llCpuIdLeafs.end();
8785 ++it)
8786 {
8787 const settings::CpuIdLeaf &rLeaf= *it;
8788 if ( rLeaf.idx < UINT32_C(0x20)
8789 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8790 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8791 mHWData->mCpuIdLeafList.push_back(rLeaf);
8792 /* else: just ignore */
8793 }
8794
8795 mHWData->mMemorySize = data.ulMemorySizeMB;
8796 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8797
8798 // boot order
8799 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8800 {
8801 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8802 if (it == data.mapBootOrder.end())
8803 mHWData->mBootOrder[i] = DeviceType_Null;
8804 else
8805 mHWData->mBootOrder[i] = it->second;
8806 }
8807
8808 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8809 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8810 mHWData->mMonitorCount = data.cMonitors;
8811 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8812 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8813 mHWData->mFirmwareType = data.firmwareType;
8814 mHWData->mPointingHIDType = data.pointingHIDType;
8815 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8816 mHWData->mChipsetType = data.chipsetType;
8817 mHWData->mParavirtProvider = data.paravirtProvider;
8818 mHWData->mParavirtDebug = data.strParavirtDebug;
8819 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8820 mHWData->mHPETEnabled = data.fHPETEnabled;
8821
8822 /* VRDEServer */
8823 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8824 if (FAILED(rc)) return rc;
8825
8826 /* BIOS */
8827 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8828 if (FAILED(rc)) return rc;
8829
8830 /* Recording settings */
8831 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8832 if (FAILED(rc)) return rc;
8833
8834 // Bandwidth control (must come before network adapters)
8835 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8836 if (FAILED(rc)) return rc;
8837
8838 /* USB controllers */
8839 for (settings::USBControllerList::const_iterator
8840 it = data.usbSettings.llUSBControllers.begin();
8841 it != data.usbSettings.llUSBControllers.end();
8842 ++it)
8843 {
8844 const settings::USBController &settingsCtrl = *it;
8845 ComObjPtr<USBController> newCtrl;
8846
8847 newCtrl.createObject();
8848 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8849 mUSBControllers->push_back(newCtrl);
8850 }
8851
8852 /* USB device filters */
8853 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8854 if (FAILED(rc)) return rc;
8855
8856 // network adapters (establish array size first and apply defaults, to
8857 // ensure reading the same settings as we saved, since the list skips
8858 // adapters having defaults)
8859 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8860 size_t oldCount = mNetworkAdapters.size();
8861 if (newCount > oldCount)
8862 {
8863 mNetworkAdapters.resize(newCount);
8864 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8865 {
8866 unconst(mNetworkAdapters[slot]).createObject();
8867 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8868 }
8869 }
8870 else if (newCount < oldCount)
8871 mNetworkAdapters.resize(newCount);
8872 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8873 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8874 for (settings::NetworkAdaptersList::const_iterator
8875 it = data.llNetworkAdapters.begin();
8876 it != data.llNetworkAdapters.end();
8877 ++it)
8878 {
8879 const settings::NetworkAdapter &nic = *it;
8880
8881 /* slot uniqueness is guaranteed by XML Schema */
8882 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8883 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8884 if (FAILED(rc)) return rc;
8885 }
8886
8887 // serial ports (establish defaults first, to ensure reading the same
8888 // settings as we saved, since the list skips ports having defaults)
8889 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8890 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8891 for (settings::SerialPortsList::const_iterator
8892 it = data.llSerialPorts.begin();
8893 it != data.llSerialPorts.end();
8894 ++it)
8895 {
8896 const settings::SerialPort &s = *it;
8897
8898 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8899 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8900 if (FAILED(rc)) return rc;
8901 }
8902
8903 // parallel ports (establish defaults first, to ensure reading the same
8904 // settings as we saved, since the list skips ports having defaults)
8905 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8906 mParallelPorts[i]->i_applyDefaults();
8907 for (settings::ParallelPortsList::const_iterator
8908 it = data.llParallelPorts.begin();
8909 it != data.llParallelPorts.end();
8910 ++it)
8911 {
8912 const settings::ParallelPort &p = *it;
8913
8914 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8915 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8916 if (FAILED(rc)) return rc;
8917 }
8918
8919 /* AudioAdapter */
8920 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8921 if (FAILED(rc)) return rc;
8922
8923 /* storage controllers */
8924 rc = i_loadStorageControllers(data.storage,
8925 puuidRegistry,
8926 puuidSnapshot);
8927 if (FAILED(rc)) return rc;
8928
8929 /* Shared folders */
8930 for (settings::SharedFoldersList::const_iterator
8931 it = data.llSharedFolders.begin();
8932 it != data.llSharedFolders.end();
8933 ++it)
8934 {
8935 const settings::SharedFolder &sf = *it;
8936
8937 ComObjPtr<SharedFolder> sharedFolder;
8938 /* Check for double entries. Not allowed! */
8939 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8940 if (SUCCEEDED(rc))
8941 return setError(VBOX_E_OBJECT_IN_USE,
8942 tr("Shared folder named '%s' already exists"),
8943 sf.strName.c_str());
8944
8945 /* Create the new shared folder. Don't break on error. This will be
8946 * reported when the machine starts. */
8947 sharedFolder.createObject();
8948 rc = sharedFolder->init(i_getMachine(),
8949 sf.strName,
8950 sf.strHostPath,
8951 RT_BOOL(sf.fWritable),
8952 RT_BOOL(sf.fAutoMount),
8953 sf.strAutoMountPoint,
8954 false /* fFailOnError */);
8955 if (FAILED(rc)) return rc;
8956 mHWData->mSharedFolders.push_back(sharedFolder);
8957 }
8958
8959 // Clipboard
8960 mHWData->mClipboardMode = data.clipboardMode;
8961
8962 // drag'n'drop
8963 mHWData->mDnDMode = data.dndMode;
8964
8965 // guest settings
8966 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8967
8968 // IO settings
8969 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8970 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8971
8972 // Host PCI devices
8973 for (settings::HostPCIDeviceAttachmentList::const_iterator
8974 it = data.pciAttachments.begin();
8975 it != data.pciAttachments.end();
8976 ++it)
8977 {
8978 const settings::HostPCIDeviceAttachment &hpda = *it;
8979 ComObjPtr<PCIDeviceAttachment> pda;
8980
8981 pda.createObject();
8982 pda->i_loadSettings(this, hpda);
8983 mHWData->mPCIDeviceAssignments.push_back(pda);
8984 }
8985
8986 /*
8987 * (The following isn't really real hardware, but it lives in HWData
8988 * for reasons of convenience.)
8989 */
8990
8991#ifdef VBOX_WITH_GUEST_PROPS
8992 /* Guest properties (optional) */
8993
8994 /* Only load transient guest properties for configs which have saved
8995 * state, because there shouldn't be any for powered off VMs. The same
8996 * logic applies for snapshots, as offline snapshots shouldn't have
8997 * any such properties. They confuse the code in various places.
8998 * Note: can't rely on the machine state, as it isn't set yet. */
8999 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9000 /* apologies for the hacky unconst() usage, but this needs hacking
9001 * actually inconsistent settings into consistency, otherwise there
9002 * will be some corner cases where the inconsistency survives
9003 * surprisingly long without getting fixed, especially for snapshots
9004 * as there are no config changes. */
9005 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9006 for (settings::GuestPropertiesList::iterator
9007 it = llGuestProperties.begin();
9008 it != llGuestProperties.end();
9009 /*nothing*/)
9010 {
9011 const settings::GuestProperty &prop = *it;
9012 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9013 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9014 if ( fSkipTransientGuestProperties
9015 && ( fFlags & GUEST_PROP_F_TRANSIENT
9016 || fFlags & GUEST_PROP_F_TRANSRESET))
9017 {
9018 it = llGuestProperties.erase(it);
9019 continue;
9020 }
9021 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9022 mHWData->mGuestProperties[prop.strName] = property;
9023 ++it;
9024 }
9025#endif /* VBOX_WITH_GUEST_PROPS defined */
9026
9027 rc = i_loadDebugging(pDbg);
9028 if (FAILED(rc))
9029 return rc;
9030
9031 mHWData->mAutostart = *pAutostart;
9032
9033 /* default frontend */
9034 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9035 }
9036 catch (std::bad_alloc &)
9037 {
9038 return E_OUTOFMEMORY;
9039 }
9040
9041 AssertComRC(rc);
9042 return rc;
9043}
9044
9045/**
9046 * Called from i_loadHardware() to load the debugging settings of the
9047 * machine.
9048 *
9049 * @param pDbg Pointer to the settings.
9050 */
9051HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9052{
9053 mHWData->mDebugging = *pDbg;
9054 /* no more processing currently required, this will probably change. */
9055 return S_OK;
9056}
9057
9058/**
9059 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9060 *
9061 * @param data storage settings.
9062 * @param puuidRegistry media registry ID to set media to or NULL;
9063 * see Machine::i_loadMachineDataFromSettings()
9064 * @param puuidSnapshot snapshot ID
9065 * @return
9066 */
9067HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9068 const Guid *puuidRegistry,
9069 const Guid *puuidSnapshot)
9070{
9071 AssertReturn(!i_isSessionMachine(), E_FAIL);
9072
9073 HRESULT rc = S_OK;
9074
9075 for (settings::StorageControllersList::const_iterator
9076 it = data.llStorageControllers.begin();
9077 it != data.llStorageControllers.end();
9078 ++it)
9079 {
9080 const settings::StorageController &ctlData = *it;
9081
9082 ComObjPtr<StorageController> pCtl;
9083 /* Try to find one with the name first. */
9084 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9085 if (SUCCEEDED(rc))
9086 return setError(VBOX_E_OBJECT_IN_USE,
9087 tr("Storage controller named '%s' already exists"),
9088 ctlData.strName.c_str());
9089
9090 pCtl.createObject();
9091 rc = pCtl->init(this,
9092 ctlData.strName,
9093 ctlData.storageBus,
9094 ctlData.ulInstance,
9095 ctlData.fBootable);
9096 if (FAILED(rc)) return rc;
9097
9098 mStorageControllers->push_back(pCtl);
9099
9100 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9101 if (FAILED(rc)) return rc;
9102
9103 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9104 if (FAILED(rc)) return rc;
9105
9106 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9107 if (FAILED(rc)) return rc;
9108
9109 /* Load the attached devices now. */
9110 rc = i_loadStorageDevices(pCtl,
9111 ctlData,
9112 puuidRegistry,
9113 puuidSnapshot);
9114 if (FAILED(rc)) return rc;
9115 }
9116
9117 return S_OK;
9118}
9119
9120/**
9121 * Called from i_loadStorageControllers for a controller's devices.
9122 *
9123 * @param aStorageController
9124 * @param data
9125 * @param puuidRegistry media registry ID to set media to or NULL; see
9126 * Machine::i_loadMachineDataFromSettings()
9127 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9128 * @return
9129 */
9130HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9131 const settings::StorageController &data,
9132 const Guid *puuidRegistry,
9133 const Guid *puuidSnapshot)
9134{
9135 HRESULT rc = S_OK;
9136
9137 /* paranoia: detect duplicate attachments */
9138 for (settings::AttachedDevicesList::const_iterator
9139 it = data.llAttachedDevices.begin();
9140 it != data.llAttachedDevices.end();
9141 ++it)
9142 {
9143 const settings::AttachedDevice &ad = *it;
9144
9145 for (settings::AttachedDevicesList::const_iterator it2 = it;
9146 it2 != data.llAttachedDevices.end();
9147 ++it2)
9148 {
9149 if (it == it2)
9150 continue;
9151
9152 const settings::AttachedDevice &ad2 = *it2;
9153
9154 if ( ad.lPort == ad2.lPort
9155 && ad.lDevice == ad2.lDevice)
9156 {
9157 return setError(E_FAIL,
9158 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9159 aStorageController->i_getName().c_str(),
9160 ad.lPort,
9161 ad.lDevice,
9162 mUserData->s.strName.c_str());
9163 }
9164 }
9165 }
9166
9167 for (settings::AttachedDevicesList::const_iterator
9168 it = data.llAttachedDevices.begin();
9169 it != data.llAttachedDevices.end();
9170 ++it)
9171 {
9172 const settings::AttachedDevice &dev = *it;
9173 ComObjPtr<Medium> medium;
9174
9175 switch (dev.deviceType)
9176 {
9177 case DeviceType_Floppy:
9178 case DeviceType_DVD:
9179 if (dev.strHostDriveSrc.isNotEmpty())
9180 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9181 false /* fRefresh */, medium);
9182 else
9183 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9184 dev.uuid,
9185 false /* fRefresh */,
9186 false /* aSetError */,
9187 medium);
9188 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9189 // This is not an error. The host drive or UUID might have vanished, so just go
9190 // ahead without this removeable medium attachment
9191 rc = S_OK;
9192 break;
9193
9194 case DeviceType_HardDisk:
9195 {
9196 /* find a hard disk by UUID */
9197 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9198 if (FAILED(rc))
9199 {
9200 if (i_isSnapshotMachine())
9201 {
9202 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9203 // so the user knows that the bad disk is in a snapshot somewhere
9204 com::ErrorInfo info;
9205 return setError(E_FAIL,
9206 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9207 puuidSnapshot->raw(),
9208 info.getText().raw());
9209 }
9210 else
9211 return rc;
9212 }
9213
9214 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9215
9216 if (medium->i_getType() == MediumType_Immutable)
9217 {
9218 if (i_isSnapshotMachine())
9219 return setError(E_FAIL,
9220 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9221 "of the virtual machine '%s' ('%s')"),
9222 medium->i_getLocationFull().c_str(),
9223 dev.uuid.raw(),
9224 puuidSnapshot->raw(),
9225 mUserData->s.strName.c_str(),
9226 mData->m_strConfigFileFull.c_str());
9227
9228 return setError(E_FAIL,
9229 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9230 medium->i_getLocationFull().c_str(),
9231 dev.uuid.raw(),
9232 mUserData->s.strName.c_str(),
9233 mData->m_strConfigFileFull.c_str());
9234 }
9235
9236 if (medium->i_getType() == MediumType_MultiAttach)
9237 {
9238 if (i_isSnapshotMachine())
9239 return setError(E_FAIL,
9240 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9241 "of the virtual machine '%s' ('%s')"),
9242 medium->i_getLocationFull().c_str(),
9243 dev.uuid.raw(),
9244 puuidSnapshot->raw(),
9245 mUserData->s.strName.c_str(),
9246 mData->m_strConfigFileFull.c_str());
9247
9248 return setError(E_FAIL,
9249 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9250 medium->i_getLocationFull().c_str(),
9251 dev.uuid.raw(),
9252 mUserData->s.strName.c_str(),
9253 mData->m_strConfigFileFull.c_str());
9254 }
9255
9256 if ( !i_isSnapshotMachine()
9257 && medium->i_getChildren().size() != 0
9258 )
9259 return setError(E_FAIL,
9260 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9261 "because it has %d differencing child hard disks"),
9262 medium->i_getLocationFull().c_str(),
9263 dev.uuid.raw(),
9264 mUserData->s.strName.c_str(),
9265 mData->m_strConfigFileFull.c_str(),
9266 medium->i_getChildren().size());
9267
9268 if (i_findAttachment(*mMediumAttachments.data(),
9269 medium))
9270 return setError(E_FAIL,
9271 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9272 medium->i_getLocationFull().c_str(),
9273 dev.uuid.raw(),
9274 mUserData->s.strName.c_str(),
9275 mData->m_strConfigFileFull.c_str());
9276
9277 break;
9278 }
9279
9280 default:
9281 return setError(E_FAIL,
9282 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9283 medium->i_getLocationFull().c_str(),
9284 mUserData->s.strName.c_str(),
9285 mData->m_strConfigFileFull.c_str());
9286 }
9287
9288 if (FAILED(rc))
9289 break;
9290
9291 /* Bandwidth groups are loaded at this point. */
9292 ComObjPtr<BandwidthGroup> pBwGroup;
9293
9294 if (!dev.strBwGroup.isEmpty())
9295 {
9296 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9297 if (FAILED(rc))
9298 return setError(E_FAIL,
9299 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9300 medium->i_getLocationFull().c_str(),
9301 dev.strBwGroup.c_str(),
9302 mUserData->s.strName.c_str(),
9303 mData->m_strConfigFileFull.c_str());
9304 pBwGroup->i_reference();
9305 }
9306
9307 const Utf8Str controllerName = aStorageController->i_getName();
9308 ComObjPtr<MediumAttachment> pAttachment;
9309 pAttachment.createObject();
9310 rc = pAttachment->init(this,
9311 medium,
9312 controllerName,
9313 dev.lPort,
9314 dev.lDevice,
9315 dev.deviceType,
9316 false,
9317 dev.fPassThrough,
9318 dev.fTempEject,
9319 dev.fNonRotational,
9320 dev.fDiscard,
9321 dev.fHotPluggable,
9322 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9323 if (FAILED(rc)) break;
9324
9325 /* associate the medium with this machine and snapshot */
9326 if (!medium.isNull())
9327 {
9328 AutoCaller medCaller(medium);
9329 if (FAILED(medCaller.rc())) return medCaller.rc();
9330 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9331
9332 if (i_isSnapshotMachine())
9333 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9334 else
9335 rc = medium->i_addBackReference(mData->mUuid);
9336 /* If the medium->addBackReference fails it sets an appropriate
9337 * error message, so no need to do any guesswork here. */
9338
9339 if (puuidRegistry)
9340 // caller wants registry ID to be set on all attached media (OVF import case)
9341 medium->i_addRegistry(*puuidRegistry);
9342 }
9343
9344 if (FAILED(rc))
9345 break;
9346
9347 /* back up mMediumAttachments to let registeredInit() properly rollback
9348 * on failure (= limited accessibility) */
9349 i_setModified(IsModified_Storage);
9350 mMediumAttachments.backup();
9351 mMediumAttachments->push_back(pAttachment);
9352 }
9353
9354 return rc;
9355}
9356
9357/**
9358 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9359 *
9360 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9361 * @param aSnapshot where to return the found snapshot
9362 * @param aSetError true to set extended error info on failure
9363 */
9364HRESULT Machine::i_findSnapshotById(const Guid &aId,
9365 ComObjPtr<Snapshot> &aSnapshot,
9366 bool aSetError /* = false */)
9367{
9368 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9369
9370 if (!mData->mFirstSnapshot)
9371 {
9372 if (aSetError)
9373 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9374 return E_FAIL;
9375 }
9376
9377 if (aId.isZero())
9378 aSnapshot = mData->mFirstSnapshot;
9379 else
9380 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9381
9382 if (!aSnapshot)
9383 {
9384 if (aSetError)
9385 return setError(E_FAIL,
9386 tr("Could not find a snapshot with UUID {%s}"),
9387 aId.toString().c_str());
9388 return E_FAIL;
9389 }
9390
9391 return S_OK;
9392}
9393
9394/**
9395 * Returns the snapshot with the given name or fails of no such snapshot.
9396 *
9397 * @param strName snapshot name to find
9398 * @param aSnapshot where to return the found snapshot
9399 * @param aSetError true to set extended error info on failure
9400 */
9401HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9402 ComObjPtr<Snapshot> &aSnapshot,
9403 bool aSetError /* = false */)
9404{
9405 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9406
9407 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9408
9409 if (!mData->mFirstSnapshot)
9410 {
9411 if (aSetError)
9412 return setError(VBOX_E_OBJECT_NOT_FOUND,
9413 tr("This machine does not have any snapshots"));
9414 return VBOX_E_OBJECT_NOT_FOUND;
9415 }
9416
9417 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9418
9419 if (!aSnapshot)
9420 {
9421 if (aSetError)
9422 return setError(VBOX_E_OBJECT_NOT_FOUND,
9423 tr("Could not find a snapshot named '%s'"), strName.c_str());
9424 return VBOX_E_OBJECT_NOT_FOUND;
9425 }
9426
9427 return S_OK;
9428}
9429
9430/**
9431 * Returns a storage controller object with the given name.
9432 *
9433 * @param aName storage controller name to find
9434 * @param aStorageController where to return the found storage controller
9435 * @param aSetError true to set extended error info on failure
9436 */
9437HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9438 ComObjPtr<StorageController> &aStorageController,
9439 bool aSetError /* = false */)
9440{
9441 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9442
9443 for (StorageControllerList::const_iterator
9444 it = mStorageControllers->begin();
9445 it != mStorageControllers->end();
9446 ++it)
9447 {
9448 if ((*it)->i_getName() == aName)
9449 {
9450 aStorageController = (*it);
9451 return S_OK;
9452 }
9453 }
9454
9455 if (aSetError)
9456 return setError(VBOX_E_OBJECT_NOT_FOUND,
9457 tr("Could not find a storage controller named '%s'"),
9458 aName.c_str());
9459 return VBOX_E_OBJECT_NOT_FOUND;
9460}
9461
9462/**
9463 * Returns a USB controller object with the given name.
9464 *
9465 * @param aName USB controller name to find
9466 * @param aUSBController where to return the found USB controller
9467 * @param aSetError true to set extended error info on failure
9468 */
9469HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9470 ComObjPtr<USBController> &aUSBController,
9471 bool aSetError /* = false */)
9472{
9473 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9474
9475 for (USBControllerList::const_iterator
9476 it = mUSBControllers->begin();
9477 it != mUSBControllers->end();
9478 ++it)
9479 {
9480 if ((*it)->i_getName() == aName)
9481 {
9482 aUSBController = (*it);
9483 return S_OK;
9484 }
9485 }
9486
9487 if (aSetError)
9488 return setError(VBOX_E_OBJECT_NOT_FOUND,
9489 tr("Could not find a storage controller named '%s'"),
9490 aName.c_str());
9491 return VBOX_E_OBJECT_NOT_FOUND;
9492}
9493
9494/**
9495 * Returns the number of USB controller instance of the given type.
9496 *
9497 * @param enmType USB controller type.
9498 */
9499ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9500{
9501 ULONG cCtrls = 0;
9502
9503 for (USBControllerList::const_iterator
9504 it = mUSBControllers->begin();
9505 it != mUSBControllers->end();
9506 ++it)
9507 {
9508 if ((*it)->i_getControllerType() == enmType)
9509 cCtrls++;
9510 }
9511
9512 return cCtrls;
9513}
9514
9515HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9516 MediumAttachmentList &atts)
9517{
9518 AutoCaller autoCaller(this);
9519 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9520
9521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9522
9523 for (MediumAttachmentList::const_iterator
9524 it = mMediumAttachments->begin();
9525 it != mMediumAttachments->end();
9526 ++it)
9527 {
9528 const ComObjPtr<MediumAttachment> &pAtt = *it;
9529 // should never happen, but deal with NULL pointers in the list.
9530 AssertContinue(!pAtt.isNull());
9531
9532 // getControllerName() needs caller+read lock
9533 AutoCaller autoAttCaller(pAtt);
9534 if (FAILED(autoAttCaller.rc()))
9535 {
9536 atts.clear();
9537 return autoAttCaller.rc();
9538 }
9539 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9540
9541 if (pAtt->i_getControllerName() == aName)
9542 atts.push_back(pAtt);
9543 }
9544
9545 return S_OK;
9546}
9547
9548
9549/**
9550 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9551 * file if the machine name was changed and about creating a new settings file
9552 * if this is a new machine.
9553 *
9554 * @note Must be never called directly but only from #saveSettings().
9555 */
9556HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9557{
9558 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9559
9560 HRESULT rc = S_OK;
9561
9562 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9563
9564 /// @todo need to handle primary group change, too
9565
9566 /* attempt to rename the settings file if machine name is changed */
9567 if ( mUserData->s.fNameSync
9568 && mUserData.isBackedUp()
9569 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9570 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9571 )
9572 {
9573 bool dirRenamed = false;
9574 bool fileRenamed = false;
9575
9576 Utf8Str configFile, newConfigFile;
9577 Utf8Str configFilePrev, newConfigFilePrev;
9578 Utf8Str configDir, newConfigDir;
9579
9580 do
9581 {
9582 int vrc = VINF_SUCCESS;
9583
9584 Utf8Str name = mUserData.backedUpData()->s.strName;
9585 Utf8Str newName = mUserData->s.strName;
9586 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9587 if (group == "/")
9588 group.setNull();
9589 Utf8Str newGroup = mUserData->s.llGroups.front();
9590 if (newGroup == "/")
9591 newGroup.setNull();
9592
9593 configFile = mData->m_strConfigFileFull;
9594
9595 /* first, rename the directory if it matches the group and machine name */
9596 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9597 group.c_str(), RTPATH_DELIMITER, name.c_str());
9598 /** @todo hack, make somehow use of ComposeMachineFilename */
9599 if (mUserData->s.fDirectoryIncludesUUID)
9600 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9601 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9602 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9603 /** @todo hack, make somehow use of ComposeMachineFilename */
9604 if (mUserData->s.fDirectoryIncludesUUID)
9605 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9606 configDir = configFile;
9607 configDir.stripFilename();
9608 newConfigDir = configDir;
9609 if ( configDir.length() >= groupPlusName.length()
9610 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9611 groupPlusName.c_str()))
9612 {
9613 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9614 Utf8Str newConfigBaseDir(newConfigDir);
9615 newConfigDir.append(newGroupPlusName);
9616 /* consistency: use \ if appropriate on the platform */
9617 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9618 /* new dir and old dir cannot be equal here because of 'if'
9619 * above and because name != newName */
9620 Assert(configDir != newConfigDir);
9621 if (!fSettingsFileIsNew)
9622 {
9623 /* perform real rename only if the machine is not new */
9624 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9625 if ( vrc == VERR_FILE_NOT_FOUND
9626 || vrc == VERR_PATH_NOT_FOUND)
9627 {
9628 /* create the parent directory, then retry renaming */
9629 Utf8Str parent(newConfigDir);
9630 parent.stripFilename();
9631 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9632 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9633 }
9634 if (RT_FAILURE(vrc))
9635 {
9636 rc = setErrorBoth(E_FAIL, vrc,
9637 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9638 configDir.c_str(),
9639 newConfigDir.c_str(),
9640 vrc);
9641 break;
9642 }
9643 /* delete subdirectories which are no longer needed */
9644 Utf8Str dir(configDir);
9645 dir.stripFilename();
9646 while (dir != newConfigBaseDir && dir != ".")
9647 {
9648 vrc = RTDirRemove(dir.c_str());
9649 if (RT_FAILURE(vrc))
9650 break;
9651 dir.stripFilename();
9652 }
9653 dirRenamed = true;
9654 }
9655 }
9656
9657 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9658 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9659
9660 /* then try to rename the settings file itself */
9661 if (newConfigFile != configFile)
9662 {
9663 /* get the path to old settings file in renamed directory */
9664 configFile = Utf8StrFmt("%s%c%s",
9665 newConfigDir.c_str(),
9666 RTPATH_DELIMITER,
9667 RTPathFilename(configFile.c_str()));
9668 if (!fSettingsFileIsNew)
9669 {
9670 /* perform real rename only if the machine is not new */
9671 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9672 if (RT_FAILURE(vrc))
9673 {
9674 rc = setErrorBoth(E_FAIL, vrc,
9675 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9676 configFile.c_str(),
9677 newConfigFile.c_str(),
9678 vrc);
9679 break;
9680 }
9681 fileRenamed = true;
9682 configFilePrev = configFile;
9683 configFilePrev += "-prev";
9684 newConfigFilePrev = newConfigFile;
9685 newConfigFilePrev += "-prev";
9686 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9687 }
9688 }
9689
9690 // update m_strConfigFileFull amd mConfigFile
9691 mData->m_strConfigFileFull = newConfigFile;
9692 // compute the relative path too
9693 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9694
9695 // store the old and new so that VirtualBox::i_saveSettings() can update
9696 // the media registry
9697 if ( mData->mRegistered
9698 && (configDir != newConfigDir || configFile != newConfigFile))
9699 {
9700 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9701
9702 if (pfNeedsGlobalSaveSettings)
9703 *pfNeedsGlobalSaveSettings = true;
9704 }
9705
9706 // in the saved state file path, replace the old directory with the new directory
9707 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9708 {
9709 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9710 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9711 }
9712
9713 // and do the same thing for the saved state file paths of all the online snapshots
9714 if (mData->mFirstSnapshot)
9715 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9716 newConfigDir.c_str());
9717 }
9718 while (0);
9719
9720 if (FAILED(rc))
9721 {
9722 /* silently try to rename everything back */
9723 if (fileRenamed)
9724 {
9725 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9726 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9727 }
9728 if (dirRenamed)
9729 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9730 }
9731
9732 if (FAILED(rc)) return rc;
9733 }
9734
9735 if (fSettingsFileIsNew)
9736 {
9737 /* create a virgin config file */
9738 int vrc = VINF_SUCCESS;
9739
9740 /* ensure the settings directory exists */
9741 Utf8Str path(mData->m_strConfigFileFull);
9742 path.stripFilename();
9743 if (!RTDirExists(path.c_str()))
9744 {
9745 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9746 if (RT_FAILURE(vrc))
9747 {
9748 return setErrorBoth(E_FAIL, vrc,
9749 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9750 path.c_str(),
9751 vrc);
9752 }
9753 }
9754
9755 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9756 path = Utf8Str(mData->m_strConfigFileFull);
9757 RTFILE f = NIL_RTFILE;
9758 vrc = RTFileOpen(&f, path.c_str(),
9759 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9760 if (RT_FAILURE(vrc))
9761 return setErrorBoth(E_FAIL, vrc,
9762 tr("Could not create the settings file '%s' (%Rrc)"),
9763 path.c_str(),
9764 vrc);
9765 RTFileClose(f);
9766 }
9767
9768 return rc;
9769}
9770
9771/**
9772 * Saves and commits machine data, user data and hardware data.
9773 *
9774 * Note that on failure, the data remains uncommitted.
9775 *
9776 * @a aFlags may combine the following flags:
9777 *
9778 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9779 * Used when saving settings after an operation that makes them 100%
9780 * correspond to the settings from the current snapshot.
9781 * - SaveS_Force: settings will be saved without doing a deep compare of the
9782 * settings structures. This is used when this is called because snapshots
9783 * have changed to avoid the overhead of the deep compare.
9784 *
9785 * @note Must be called from under this object's write lock. Locks children for
9786 * writing.
9787 *
9788 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9789 * initialized to false and that will be set to true by this function if
9790 * the caller must invoke VirtualBox::i_saveSettings() because the global
9791 * settings have changed. This will happen if a machine rename has been
9792 * saved and the global machine and media registries will therefore need
9793 * updating.
9794 * @param aFlags Flags.
9795 */
9796HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9797 int aFlags /*= 0*/)
9798{
9799 LogFlowThisFuncEnter();
9800
9801 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9802
9803 /* make sure child objects are unable to modify the settings while we are
9804 * saving them */
9805 i_ensureNoStateDependencies();
9806
9807 AssertReturn(!i_isSnapshotMachine(),
9808 E_FAIL);
9809
9810 if (!mData->mAccessible)
9811 return setError(VBOX_E_INVALID_VM_STATE,
9812 tr("The machine is not accessible, so cannot save settings"));
9813
9814 HRESULT rc = S_OK;
9815 bool fNeedsWrite = false;
9816
9817 /* First, prepare to save settings. It will care about renaming the
9818 * settings directory and file if the machine name was changed and about
9819 * creating a new settings file if this is a new machine. */
9820 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9821 if (FAILED(rc)) return rc;
9822
9823 // keep a pointer to the current settings structures
9824 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9825 settings::MachineConfigFile *pNewConfig = NULL;
9826
9827 try
9828 {
9829 // make a fresh one to have everyone write stuff into
9830 pNewConfig = new settings::MachineConfigFile(NULL);
9831 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9832
9833 // now go and copy all the settings data from COM to the settings structures
9834 // (this calls i_saveSettings() on all the COM objects in the machine)
9835 i_copyMachineDataToSettings(*pNewConfig);
9836
9837 if (aFlags & SaveS_ResetCurStateModified)
9838 {
9839 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9840 mData->mCurrentStateModified = FALSE;
9841 fNeedsWrite = true; // always, no need to compare
9842 }
9843 else if (aFlags & SaveS_Force)
9844 {
9845 fNeedsWrite = true; // always, no need to compare
9846 }
9847 else
9848 {
9849 if (!mData->mCurrentStateModified)
9850 {
9851 // do a deep compare of the settings that we just saved with the settings
9852 // previously stored in the config file; this invokes MachineConfigFile::operator==
9853 // which does a deep compare of all the settings, which is expensive but less expensive
9854 // than writing out XML in vain
9855 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9856
9857 // could still be modified if any settings changed
9858 mData->mCurrentStateModified = fAnySettingsChanged;
9859
9860 fNeedsWrite = fAnySettingsChanged;
9861 }
9862 else
9863 fNeedsWrite = true;
9864 }
9865
9866 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9867
9868 if (fNeedsWrite)
9869 // now spit it all out!
9870 pNewConfig->write(mData->m_strConfigFileFull);
9871
9872 mData->pMachineConfigFile = pNewConfig;
9873 delete pOldConfig;
9874 i_commit();
9875
9876 // after saving settings, we are no longer different from the XML on disk
9877 mData->flModifications = 0;
9878 }
9879 catch (HRESULT err)
9880 {
9881 // we assume that error info is set by the thrower
9882 rc = err;
9883
9884 // restore old config
9885 delete pNewConfig;
9886 mData->pMachineConfigFile = pOldConfig;
9887 }
9888 catch (...)
9889 {
9890 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9891 }
9892
9893 if (fNeedsWrite)
9894 {
9895 /* Fire the data change event, even on failure (since we've already
9896 * committed all data). This is done only for SessionMachines because
9897 * mutable Machine instances are always not registered (i.e. private
9898 * to the client process that creates them) and thus don't need to
9899 * inform callbacks. */
9900 if (i_isSessionMachine())
9901 mParent->i_onMachineDataChange(mData->mUuid);
9902 }
9903
9904 LogFlowThisFunc(("rc=%08X\n", rc));
9905 LogFlowThisFuncLeave();
9906 return rc;
9907}
9908
9909/**
9910 * Implementation for saving the machine settings into the given
9911 * settings::MachineConfigFile instance. This copies machine extradata
9912 * from the previous machine config file in the instance data, if any.
9913 *
9914 * This gets called from two locations:
9915 *
9916 * -- Machine::i_saveSettings(), during the regular XML writing;
9917 *
9918 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9919 * exported to OVF and we write the VirtualBox proprietary XML
9920 * into a <vbox:Machine> tag.
9921 *
9922 * This routine fills all the fields in there, including snapshots, *except*
9923 * for the following:
9924 *
9925 * -- fCurrentStateModified. There is some special logic associated with that.
9926 *
9927 * The caller can then call MachineConfigFile::write() or do something else
9928 * with it.
9929 *
9930 * Caller must hold the machine lock!
9931 *
9932 * This throws XML errors and HRESULT, so the caller must have a catch block!
9933 */
9934void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9935{
9936 // deep copy extradata, being extra careful with self assignment (the STL
9937 // map assignment on Mac OS X clang based Xcode isn't checking)
9938 if (&config != mData->pMachineConfigFile)
9939 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9940
9941 config.uuid = mData->mUuid;
9942
9943 // copy name, description, OS type, teleport, UTC etc.
9944 config.machineUserData = mUserData->s;
9945
9946 if ( mData->mMachineState == MachineState_Saved
9947 || mData->mMachineState == MachineState_Restoring
9948 // when doing certain snapshot operations we may or may not have
9949 // a saved state in the current state, so keep everything as is
9950 || ( ( mData->mMachineState == MachineState_Snapshotting
9951 || mData->mMachineState == MachineState_DeletingSnapshot
9952 || mData->mMachineState == MachineState_RestoringSnapshot)
9953 && (!mSSData->strStateFilePath.isEmpty())
9954 )
9955 )
9956 {
9957 Assert(!mSSData->strStateFilePath.isEmpty());
9958 /* try to make the file name relative to the settings file dir */
9959 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9960 }
9961 else
9962 {
9963 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9964 config.strStateFile.setNull();
9965 }
9966
9967 if (mData->mCurrentSnapshot)
9968 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9969 else
9970 config.uuidCurrentSnapshot.clear();
9971
9972 config.timeLastStateChange = mData->mLastStateChange;
9973 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9974 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9975
9976 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9977 if (FAILED(rc)) throw rc;
9978
9979 // save machine's media registry if this is VirtualBox 4.0 or later
9980 if (config.canHaveOwnMediaRegistry())
9981 {
9982 // determine machine folder
9983 Utf8Str strMachineFolder = i_getSettingsFileFull();
9984 strMachineFolder.stripFilename();
9985 mParent->i_saveMediaRegistry(config.mediaRegistry,
9986 i_getId(), // only media with registry ID == machine UUID
9987 strMachineFolder);
9988 // this throws HRESULT
9989 }
9990
9991 // save snapshots
9992 rc = i_saveAllSnapshots(config);
9993 if (FAILED(rc)) throw rc;
9994}
9995
9996/**
9997 * Saves all snapshots of the machine into the given machine config file. Called
9998 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9999 * @param config
10000 * @return
10001 */
10002HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10003{
10004 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10005
10006 HRESULT rc = S_OK;
10007
10008 try
10009 {
10010 config.llFirstSnapshot.clear();
10011
10012 if (mData->mFirstSnapshot)
10013 {
10014 // the settings use a list for "the first snapshot"
10015 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10016
10017 // get reference to the snapshot on the list and work on that
10018 // element straight in the list to avoid excessive copying later
10019 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10020 if (FAILED(rc)) throw rc;
10021 }
10022
10023// if (mType == IsSessionMachine)
10024// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10025
10026 }
10027 catch (HRESULT err)
10028 {
10029 /* we assume that error info is set by the thrower */
10030 rc = err;
10031 }
10032 catch (...)
10033 {
10034 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10035 }
10036
10037 return rc;
10038}
10039
10040/**
10041 * Saves the VM hardware configuration. It is assumed that the
10042 * given node is empty.
10043 *
10044 * @param data Reference to the settings object for the hardware config.
10045 * @param pDbg Pointer to the settings object for the debugging config
10046 * which happens to live in mHWData.
10047 * @param pAutostart Pointer to the settings object for the autostart config
10048 * which happens to live in mHWData.
10049 */
10050HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10051 settings::Autostart *pAutostart)
10052{
10053 HRESULT rc = S_OK;
10054
10055 try
10056 {
10057 /* The hardware version attribute (optional).
10058 Automatically upgrade from 1 to current default hardware version
10059 when there is no saved state. (ugly!) */
10060 if ( mHWData->mHWVersion == "1"
10061 && mSSData->strStateFilePath.isEmpty()
10062 )
10063 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10064
10065 data.strVersion = mHWData->mHWVersion;
10066 data.uuid = mHWData->mHardwareUUID;
10067
10068 // CPU
10069 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10070 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10071 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10072 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10073 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10074 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10075 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10076 data.fPAE = !!mHWData->mPAEEnabled;
10077 data.enmLongMode = mHWData->mLongMode;
10078 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10079 data.fAPIC = !!mHWData->mAPIC;
10080 data.fX2APIC = !!mHWData->mX2APIC;
10081 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10082 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10083 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10084 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10085 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10086 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10087 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10088 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10089 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10090 data.cCPUs = mHWData->mCPUCount;
10091 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10092 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10093 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10094 data.strCpuProfile = mHWData->mCpuProfile;
10095
10096 data.llCpus.clear();
10097 if (data.fCpuHotPlug)
10098 {
10099 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10100 {
10101 if (mHWData->mCPUAttached[idx])
10102 {
10103 settings::Cpu cpu;
10104 cpu.ulId = idx;
10105 data.llCpus.push_back(cpu);
10106 }
10107 }
10108 }
10109
10110 /* Standard and Extended CPUID leafs. */
10111 data.llCpuIdLeafs.clear();
10112 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10113
10114 // memory
10115 data.ulMemorySizeMB = mHWData->mMemorySize;
10116 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10117
10118 // firmware
10119 data.firmwareType = mHWData->mFirmwareType;
10120
10121 // HID
10122 data.pointingHIDType = mHWData->mPointingHIDType;
10123 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10124
10125 // chipset
10126 data.chipsetType = mHWData->mChipsetType;
10127
10128 // paravirt
10129 data.paravirtProvider = mHWData->mParavirtProvider;
10130 data.strParavirtDebug = mHWData->mParavirtDebug;
10131
10132 // emulated USB card reader
10133 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10134
10135 // HPET
10136 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10137
10138 // boot order
10139 data.mapBootOrder.clear();
10140 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10141 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10142
10143 // display
10144 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10145 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10146 data.cMonitors = mHWData->mMonitorCount;
10147 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10148 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10149
10150 /* VRDEServer settings (optional) */
10151 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10152 if (FAILED(rc)) throw rc;
10153
10154 /* BIOS settings (required) */
10155 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10156 if (FAILED(rc)) throw rc;
10157
10158 /* Recording settings (required) */
10159 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10160 if (FAILED(rc)) throw rc;
10161
10162 /* USB Controller (required) */
10163 data.usbSettings.llUSBControllers.clear();
10164 for (USBControllerList::const_iterator
10165 it = mUSBControllers->begin();
10166 it != mUSBControllers->end();
10167 ++it)
10168 {
10169 ComObjPtr<USBController> ctrl = *it;
10170 settings::USBController settingsCtrl;
10171
10172 settingsCtrl.strName = ctrl->i_getName();
10173 settingsCtrl.enmType = ctrl->i_getControllerType();
10174
10175 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10176 }
10177
10178 /* USB device filters (required) */
10179 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10180 if (FAILED(rc)) throw rc;
10181
10182 /* Network adapters (required) */
10183 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10184 data.llNetworkAdapters.clear();
10185 /* Write out only the nominal number of network adapters for this
10186 * chipset type. Since Machine::commit() hasn't been called there
10187 * may be extra NIC settings in the vector. */
10188 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10189 {
10190 settings::NetworkAdapter nic;
10191 nic.ulSlot = (uint32_t)slot;
10192 /* paranoia check... must not be NULL, but must not crash either. */
10193 if (mNetworkAdapters[slot])
10194 {
10195 if (mNetworkAdapters[slot]->i_hasDefaults())
10196 continue;
10197
10198 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10199 if (FAILED(rc)) throw rc;
10200
10201 data.llNetworkAdapters.push_back(nic);
10202 }
10203 }
10204
10205 /* Serial ports */
10206 data.llSerialPorts.clear();
10207 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10208 {
10209 if (mSerialPorts[slot]->i_hasDefaults())
10210 continue;
10211
10212 settings::SerialPort s;
10213 s.ulSlot = slot;
10214 rc = mSerialPorts[slot]->i_saveSettings(s);
10215 if (FAILED(rc)) return rc;
10216
10217 data.llSerialPorts.push_back(s);
10218 }
10219
10220 /* Parallel ports */
10221 data.llParallelPorts.clear();
10222 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10223 {
10224 if (mParallelPorts[slot]->i_hasDefaults())
10225 continue;
10226
10227 settings::ParallelPort p;
10228 p.ulSlot = slot;
10229 rc = mParallelPorts[slot]->i_saveSettings(p);
10230 if (FAILED(rc)) return rc;
10231
10232 data.llParallelPorts.push_back(p);
10233 }
10234
10235 /* Audio adapter */
10236 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10237 if (FAILED(rc)) return rc;
10238
10239 rc = i_saveStorageControllers(data.storage);
10240 if (FAILED(rc)) return rc;
10241
10242 /* Shared folders */
10243 data.llSharedFolders.clear();
10244 for (HWData::SharedFolderList::const_iterator
10245 it = mHWData->mSharedFolders.begin();
10246 it != mHWData->mSharedFolders.end();
10247 ++it)
10248 {
10249 SharedFolder *pSF = *it;
10250 AutoCaller sfCaller(pSF);
10251 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10252 settings::SharedFolder sf;
10253 sf.strName = pSF->i_getName();
10254 sf.strHostPath = pSF->i_getHostPath();
10255 sf.fWritable = !!pSF->i_isWritable();
10256 sf.fAutoMount = !!pSF->i_isAutoMounted();
10257 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10258
10259 data.llSharedFolders.push_back(sf);
10260 }
10261
10262 // clipboard
10263 data.clipboardMode = mHWData->mClipboardMode;
10264
10265 // drag'n'drop
10266 data.dndMode = mHWData->mDnDMode;
10267
10268 /* Guest */
10269 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10270
10271 // IO settings
10272 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10273 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10274
10275 /* BandwidthControl (required) */
10276 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10277 if (FAILED(rc)) throw rc;
10278
10279 /* Host PCI devices */
10280 data.pciAttachments.clear();
10281 for (HWData::PCIDeviceAssignmentList::const_iterator
10282 it = mHWData->mPCIDeviceAssignments.begin();
10283 it != mHWData->mPCIDeviceAssignments.end();
10284 ++it)
10285 {
10286 ComObjPtr<PCIDeviceAttachment> pda = *it;
10287 settings::HostPCIDeviceAttachment hpda;
10288
10289 rc = pda->i_saveSettings(hpda);
10290 if (FAILED(rc)) throw rc;
10291
10292 data.pciAttachments.push_back(hpda);
10293 }
10294
10295 // guest properties
10296 data.llGuestProperties.clear();
10297#ifdef VBOX_WITH_GUEST_PROPS
10298 for (HWData::GuestPropertyMap::const_iterator
10299 it = mHWData->mGuestProperties.begin();
10300 it != mHWData->mGuestProperties.end();
10301 ++it)
10302 {
10303 HWData::GuestProperty property = it->second;
10304
10305 /* Remove transient guest properties at shutdown unless we
10306 * are saving state. Note that restoring snapshot intentionally
10307 * keeps them, they will be removed if appropriate once the final
10308 * machine state is set (as crashes etc. need to work). */
10309 if ( ( mData->mMachineState == MachineState_PoweredOff
10310 || mData->mMachineState == MachineState_Aborted
10311 || mData->mMachineState == MachineState_Teleported)
10312 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10313 continue;
10314 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10315 prop.strName = it->first;
10316 prop.strValue = property.strValue;
10317 prop.timestamp = property.mTimestamp;
10318 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10319 GuestPropWriteFlags(property.mFlags, szFlags);
10320 prop.strFlags = szFlags;
10321
10322 data.llGuestProperties.push_back(prop);
10323 }
10324
10325 /* I presume this doesn't require a backup(). */
10326 mData->mGuestPropertiesModified = FALSE;
10327#endif /* VBOX_WITH_GUEST_PROPS defined */
10328
10329 *pDbg = mHWData->mDebugging;
10330 *pAutostart = mHWData->mAutostart;
10331
10332 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10333 }
10334 catch (std::bad_alloc &)
10335 {
10336 return E_OUTOFMEMORY;
10337 }
10338
10339 AssertComRC(rc);
10340 return rc;
10341}
10342
10343/**
10344 * Saves the storage controller configuration.
10345 *
10346 * @param data storage settings.
10347 */
10348HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10349{
10350 data.llStorageControllers.clear();
10351
10352 for (StorageControllerList::const_iterator
10353 it = mStorageControllers->begin();
10354 it != mStorageControllers->end();
10355 ++it)
10356 {
10357 HRESULT rc;
10358 ComObjPtr<StorageController> pCtl = *it;
10359
10360 settings::StorageController ctl;
10361 ctl.strName = pCtl->i_getName();
10362 ctl.controllerType = pCtl->i_getControllerType();
10363 ctl.storageBus = pCtl->i_getStorageBus();
10364 ctl.ulInstance = pCtl->i_getInstance();
10365 ctl.fBootable = pCtl->i_getBootable();
10366
10367 /* Save the port count. */
10368 ULONG portCount;
10369 rc = pCtl->COMGETTER(PortCount)(&portCount);
10370 ComAssertComRCRet(rc, rc);
10371 ctl.ulPortCount = portCount;
10372
10373 /* Save fUseHostIOCache */
10374 BOOL fUseHostIOCache;
10375 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10376 ComAssertComRCRet(rc, rc);
10377 ctl.fUseHostIOCache = !!fUseHostIOCache;
10378
10379 /* save the devices now. */
10380 rc = i_saveStorageDevices(pCtl, ctl);
10381 ComAssertComRCRet(rc, rc);
10382
10383 data.llStorageControllers.push_back(ctl);
10384 }
10385
10386 return S_OK;
10387}
10388
10389/**
10390 * Saves the hard disk configuration.
10391 */
10392HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10393 settings::StorageController &data)
10394{
10395 MediumAttachmentList atts;
10396
10397 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10398 if (FAILED(rc)) return rc;
10399
10400 data.llAttachedDevices.clear();
10401 for (MediumAttachmentList::const_iterator
10402 it = atts.begin();
10403 it != atts.end();
10404 ++it)
10405 {
10406 settings::AttachedDevice dev;
10407 IMediumAttachment *iA = *it;
10408 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10409 Medium *pMedium = pAttach->i_getMedium();
10410
10411 dev.deviceType = pAttach->i_getType();
10412 dev.lPort = pAttach->i_getPort();
10413 dev.lDevice = pAttach->i_getDevice();
10414 dev.fPassThrough = pAttach->i_getPassthrough();
10415 dev.fHotPluggable = pAttach->i_getHotPluggable();
10416 if (pMedium)
10417 {
10418 if (pMedium->i_isHostDrive())
10419 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10420 else
10421 dev.uuid = pMedium->i_getId();
10422 dev.fTempEject = pAttach->i_getTempEject();
10423 dev.fNonRotational = pAttach->i_getNonRotational();
10424 dev.fDiscard = pAttach->i_getDiscard();
10425 }
10426
10427 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10428
10429 data.llAttachedDevices.push_back(dev);
10430 }
10431
10432 return S_OK;
10433}
10434
10435/**
10436 * Saves machine state settings as defined by aFlags
10437 * (SaveSTS_* values).
10438 *
10439 * @param aFlags Combination of SaveSTS_* flags.
10440 *
10441 * @note Locks objects for writing.
10442 */
10443HRESULT Machine::i_saveStateSettings(int aFlags)
10444{
10445 if (aFlags == 0)
10446 return S_OK;
10447
10448 AutoCaller autoCaller(this);
10449 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10450
10451 /* This object's write lock is also necessary to serialize file access
10452 * (prevent concurrent reads and writes) */
10453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10454
10455 HRESULT rc = S_OK;
10456
10457 Assert(mData->pMachineConfigFile);
10458
10459 try
10460 {
10461 if (aFlags & SaveSTS_CurStateModified)
10462 mData->pMachineConfigFile->fCurrentStateModified = true;
10463
10464 if (aFlags & SaveSTS_StateFilePath)
10465 {
10466 if (!mSSData->strStateFilePath.isEmpty())
10467 /* try to make the file name relative to the settings file dir */
10468 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10469 else
10470 mData->pMachineConfigFile->strStateFile.setNull();
10471 }
10472
10473 if (aFlags & SaveSTS_StateTimeStamp)
10474 {
10475 Assert( mData->mMachineState != MachineState_Aborted
10476 || mSSData->strStateFilePath.isEmpty());
10477
10478 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10479
10480 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10481/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10482 }
10483
10484 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10485 }
10486 catch (...)
10487 {
10488 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10489 }
10490
10491 return rc;
10492}
10493
10494/**
10495 * Ensures that the given medium is added to a media registry. If this machine
10496 * was created with 4.0 or later, then the machine registry is used. Otherwise
10497 * the global VirtualBox media registry is used.
10498 *
10499 * Caller must NOT hold machine lock, media tree or any medium locks!
10500 *
10501 * @param pMedium
10502 */
10503void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10504{
10505 /* Paranoia checks: do not hold machine or media tree locks. */
10506 AssertReturnVoid(!isWriteLockOnCurrentThread());
10507 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10508
10509 ComObjPtr<Medium> pBase;
10510 {
10511 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10512 pBase = pMedium->i_getBase();
10513 }
10514
10515 /* Paranoia checks: do not hold medium locks. */
10516 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10517 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10518
10519 // decide which medium registry to use now that the medium is attached:
10520 Guid uuid;
10521 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10522 if (fCanHaveOwnMediaRegistry)
10523 // machine XML is VirtualBox 4.0 or higher:
10524 uuid = i_getId(); // machine UUID
10525 else
10526 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10527
10528 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10529 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10530 if (pMedium->i_addRegistry(uuid))
10531 mParent->i_markRegistryModified(uuid);
10532
10533 /* For more complex hard disk structures it can happen that the base
10534 * medium isn't yet associated with any medium registry. Do that now. */
10535 if (pMedium != pBase)
10536 {
10537 /* Tree lock needed by Medium::addRegistry when recursing. */
10538 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10539 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10540 {
10541 treeLock.release();
10542 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10543 treeLock.acquire();
10544 }
10545 if (pBase->i_addRegistryRecursive(uuid))
10546 {
10547 treeLock.release();
10548 mParent->i_markRegistryModified(uuid);
10549 }
10550 }
10551}
10552
10553/**
10554 * Creates differencing hard disks for all normal hard disks attached to this
10555 * machine and a new set of attachments to refer to created disks.
10556 *
10557 * Used when taking a snapshot or when deleting the current state. Gets called
10558 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10559 *
10560 * This method assumes that mMediumAttachments contains the original hard disk
10561 * attachments it needs to create diffs for. On success, these attachments will
10562 * be replaced with the created diffs.
10563 *
10564 * Attachments with non-normal hard disks are left as is.
10565 *
10566 * If @a aOnline is @c false then the original hard disks that require implicit
10567 * diffs will be locked for reading. Otherwise it is assumed that they are
10568 * already locked for writing (when the VM was started). Note that in the latter
10569 * case it is responsibility of the caller to lock the newly created diffs for
10570 * writing if this method succeeds.
10571 *
10572 * @param aProgress Progress object to run (must contain at least as
10573 * many operations left as the number of hard disks
10574 * attached).
10575 * @param aWeight Weight of this operation.
10576 * @param aOnline Whether the VM was online prior to this operation.
10577 *
10578 * @note The progress object is not marked as completed, neither on success nor
10579 * on failure. This is a responsibility of the caller.
10580 *
10581 * @note Locks this object and the media tree for writing.
10582 */
10583HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10584 ULONG aWeight,
10585 bool aOnline)
10586{
10587 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10588
10589 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10590 AssertReturn(!!pProgressControl, E_INVALIDARG);
10591
10592 AutoCaller autoCaller(this);
10593 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10594
10595 AutoMultiWriteLock2 alock(this->lockHandle(),
10596 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10597
10598 /* must be in a protective state because we release the lock below */
10599 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10600 || mData->mMachineState == MachineState_OnlineSnapshotting
10601 || mData->mMachineState == MachineState_LiveSnapshotting
10602 || mData->mMachineState == MachineState_RestoringSnapshot
10603 || mData->mMachineState == MachineState_DeletingSnapshot
10604 , E_FAIL);
10605
10606 HRESULT rc = S_OK;
10607
10608 // use appropriate locked media map (online or offline)
10609 MediumLockListMap lockedMediaOffline;
10610 MediumLockListMap *lockedMediaMap;
10611 if (aOnline)
10612 lockedMediaMap = &mData->mSession.mLockedMedia;
10613 else
10614 lockedMediaMap = &lockedMediaOffline;
10615
10616 try
10617 {
10618 if (!aOnline)
10619 {
10620 /* lock all attached hard disks early to detect "in use"
10621 * situations before creating actual diffs */
10622 for (MediumAttachmentList::const_iterator
10623 it = mMediumAttachments->begin();
10624 it != mMediumAttachments->end();
10625 ++it)
10626 {
10627 MediumAttachment *pAtt = *it;
10628 if (pAtt->i_getType() == DeviceType_HardDisk)
10629 {
10630 Medium *pMedium = pAtt->i_getMedium();
10631 Assert(pMedium);
10632
10633 MediumLockList *pMediumLockList(new MediumLockList());
10634 alock.release();
10635 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10636 NULL /* pToLockWrite */,
10637 false /* fMediumLockWriteAll */,
10638 NULL,
10639 *pMediumLockList);
10640 alock.acquire();
10641 if (FAILED(rc))
10642 {
10643 delete pMediumLockList;
10644 throw rc;
10645 }
10646 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10647 if (FAILED(rc))
10648 {
10649 throw setError(rc,
10650 tr("Collecting locking information for all attached media failed"));
10651 }
10652 }
10653 }
10654
10655 /* Now lock all media. If this fails, nothing is locked. */
10656 alock.release();
10657 rc = lockedMediaMap->Lock();
10658 alock.acquire();
10659 if (FAILED(rc))
10660 {
10661 throw setError(rc,
10662 tr("Locking of attached media failed"));
10663 }
10664 }
10665
10666 /* remember the current list (note that we don't use backup() since
10667 * mMediumAttachments may be already backed up) */
10668 MediumAttachmentList atts = *mMediumAttachments.data();
10669
10670 /* start from scratch */
10671 mMediumAttachments->clear();
10672
10673 /* go through remembered attachments and create diffs for normal hard
10674 * disks and attach them */
10675 for (MediumAttachmentList::const_iterator
10676 it = atts.begin();
10677 it != atts.end();
10678 ++it)
10679 {
10680 MediumAttachment *pAtt = *it;
10681
10682 DeviceType_T devType = pAtt->i_getType();
10683 Medium *pMedium = pAtt->i_getMedium();
10684
10685 if ( devType != DeviceType_HardDisk
10686 || pMedium == NULL
10687 || pMedium->i_getType() != MediumType_Normal)
10688 {
10689 /* copy the attachment as is */
10690
10691 /** @todo the progress object created in SessionMachine::TakeSnaphot
10692 * only expects operations for hard disks. Later other
10693 * device types need to show up in the progress as well. */
10694 if (devType == DeviceType_HardDisk)
10695 {
10696 if (pMedium == NULL)
10697 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10698 aWeight); // weight
10699 else
10700 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10701 pMedium->i_getBase()->i_getName().c_str()).raw(),
10702 aWeight); // weight
10703 }
10704
10705 mMediumAttachments->push_back(pAtt);
10706 continue;
10707 }
10708
10709 /* need a diff */
10710 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10711 pMedium->i_getBase()->i_getName().c_str()).raw(),
10712 aWeight); // weight
10713
10714 Utf8Str strFullSnapshotFolder;
10715 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10716
10717 ComObjPtr<Medium> diff;
10718 diff.createObject();
10719 // store the diff in the same registry as the parent
10720 // (this cannot fail here because we can't create implicit diffs for
10721 // unregistered images)
10722 Guid uuidRegistryParent;
10723 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10724 Assert(fInRegistry); NOREF(fInRegistry);
10725 rc = diff->init(mParent,
10726 pMedium->i_getPreferredDiffFormat(),
10727 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10728 uuidRegistryParent,
10729 DeviceType_HardDisk);
10730 if (FAILED(rc)) throw rc;
10731
10732 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10733 * the push_back? Looks like we're going to release medium with the
10734 * wrong kind of lock (general issue with if we fail anywhere at all)
10735 * and an orphaned VDI in the snapshots folder. */
10736
10737 /* update the appropriate lock list */
10738 MediumLockList *pMediumLockList;
10739 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10740 AssertComRCThrowRC(rc);
10741 if (aOnline)
10742 {
10743 alock.release();
10744 /* The currently attached medium will be read-only, change
10745 * the lock type to read. */
10746 rc = pMediumLockList->Update(pMedium, false);
10747 alock.acquire();
10748 AssertComRCThrowRC(rc);
10749 }
10750
10751 /* release the locks before the potentially lengthy operation */
10752 alock.release();
10753 rc = pMedium->i_createDiffStorage(diff,
10754 pMedium->i_getPreferredDiffVariant(),
10755 pMediumLockList,
10756 NULL /* aProgress */,
10757 true /* aWait */,
10758 false /* aNotify */);
10759 alock.acquire();
10760 if (FAILED(rc)) throw rc;
10761
10762 /* actual lock list update is done in Machine::i_commitMedia */
10763
10764 rc = diff->i_addBackReference(mData->mUuid);
10765 AssertComRCThrowRC(rc);
10766
10767 /* add a new attachment */
10768 ComObjPtr<MediumAttachment> attachment;
10769 attachment.createObject();
10770 rc = attachment->init(this,
10771 diff,
10772 pAtt->i_getControllerName(),
10773 pAtt->i_getPort(),
10774 pAtt->i_getDevice(),
10775 DeviceType_HardDisk,
10776 true /* aImplicit */,
10777 false /* aPassthrough */,
10778 false /* aTempEject */,
10779 pAtt->i_getNonRotational(),
10780 pAtt->i_getDiscard(),
10781 pAtt->i_getHotPluggable(),
10782 pAtt->i_getBandwidthGroup());
10783 if (FAILED(rc)) throw rc;
10784
10785 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10786 AssertComRCThrowRC(rc);
10787 mMediumAttachments->push_back(attachment);
10788 }
10789 }
10790 catch (HRESULT aRC) { rc = aRC; }
10791
10792 /* unlock all hard disks we locked when there is no VM */
10793 if (!aOnline)
10794 {
10795 ErrorInfoKeeper eik;
10796
10797 HRESULT rc1 = lockedMediaMap->Clear();
10798 AssertComRC(rc1);
10799 }
10800
10801 return rc;
10802}
10803
10804/**
10805 * Deletes implicit differencing hard disks created either by
10806 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10807 * mMediumAttachments.
10808 *
10809 * Note that to delete hard disks created by #attachDevice() this method is
10810 * called from #i_rollbackMedia() when the changes are rolled back.
10811 *
10812 * @note Locks this object and the media tree for writing.
10813 */
10814HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10815{
10816 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10817
10818 AutoCaller autoCaller(this);
10819 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10820
10821 AutoMultiWriteLock2 alock(this->lockHandle(),
10822 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10823
10824 /* We absolutely must have backed up state. */
10825 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10826
10827 /* Check if there are any implicitly created diff images. */
10828 bool fImplicitDiffs = false;
10829 for (MediumAttachmentList::const_iterator
10830 it = mMediumAttachments->begin();
10831 it != mMediumAttachments->end();
10832 ++it)
10833 {
10834 const ComObjPtr<MediumAttachment> &pAtt = *it;
10835 if (pAtt->i_isImplicit())
10836 {
10837 fImplicitDiffs = true;
10838 break;
10839 }
10840 }
10841 /* If there is nothing to do, leave early. This saves lots of image locking
10842 * effort. It also avoids a MachineStateChanged event without real reason.
10843 * This is important e.g. when loading a VM config, because there should be
10844 * no events. Otherwise API clients can become thoroughly confused for
10845 * inaccessible VMs (the code for loading VM configs uses this method for
10846 * cleanup if the config makes no sense), as they take such events as an
10847 * indication that the VM is alive, and they would force the VM config to
10848 * be reread, leading to an endless loop. */
10849 if (!fImplicitDiffs)
10850 return S_OK;
10851
10852 HRESULT rc = S_OK;
10853 MachineState_T oldState = mData->mMachineState;
10854
10855 /* will release the lock before the potentially lengthy operation,
10856 * so protect with the special state (unless already protected) */
10857 if ( oldState != MachineState_Snapshotting
10858 && oldState != MachineState_OnlineSnapshotting
10859 && oldState != MachineState_LiveSnapshotting
10860 && oldState != MachineState_RestoringSnapshot
10861 && oldState != MachineState_DeletingSnapshot
10862 && oldState != MachineState_DeletingSnapshotOnline
10863 && oldState != MachineState_DeletingSnapshotPaused
10864 )
10865 i_setMachineState(MachineState_SettingUp);
10866
10867 // use appropriate locked media map (online or offline)
10868 MediumLockListMap lockedMediaOffline;
10869 MediumLockListMap *lockedMediaMap;
10870 if (aOnline)
10871 lockedMediaMap = &mData->mSession.mLockedMedia;
10872 else
10873 lockedMediaMap = &lockedMediaOffline;
10874
10875 try
10876 {
10877 if (!aOnline)
10878 {
10879 /* lock all attached hard disks early to detect "in use"
10880 * situations before deleting actual diffs */
10881 for (MediumAttachmentList::const_iterator
10882 it = mMediumAttachments->begin();
10883 it != mMediumAttachments->end();
10884 ++it)
10885 {
10886 MediumAttachment *pAtt = *it;
10887 if (pAtt->i_getType() == DeviceType_HardDisk)
10888 {
10889 Medium *pMedium = pAtt->i_getMedium();
10890 Assert(pMedium);
10891
10892 MediumLockList *pMediumLockList(new MediumLockList());
10893 alock.release();
10894 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10895 NULL /* pToLockWrite */,
10896 false /* fMediumLockWriteAll */,
10897 NULL,
10898 *pMediumLockList);
10899 alock.acquire();
10900
10901 if (FAILED(rc))
10902 {
10903 delete pMediumLockList;
10904 throw rc;
10905 }
10906
10907 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10908 if (FAILED(rc))
10909 throw rc;
10910 }
10911 }
10912
10913 if (FAILED(rc))
10914 throw rc;
10915 } // end of offline
10916
10917 /* Lock lists are now up to date and include implicitly created media */
10918
10919 /* Go through remembered attachments and delete all implicitly created
10920 * diffs and fix up the attachment information */
10921 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10922 MediumAttachmentList implicitAtts;
10923 for (MediumAttachmentList::const_iterator
10924 it = mMediumAttachments->begin();
10925 it != mMediumAttachments->end();
10926 ++it)
10927 {
10928 ComObjPtr<MediumAttachment> pAtt = *it;
10929 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10930 if (pMedium.isNull())
10931 continue;
10932
10933 // Implicit attachments go on the list for deletion and back references are removed.
10934 if (pAtt->i_isImplicit())
10935 {
10936 /* Deassociate and mark for deletion */
10937 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10938 rc = pMedium->i_removeBackReference(mData->mUuid);
10939 if (FAILED(rc))
10940 throw rc;
10941 implicitAtts.push_back(pAtt);
10942 continue;
10943 }
10944
10945 /* Was this medium attached before? */
10946 if (!i_findAttachment(oldAtts, pMedium))
10947 {
10948 /* no: de-associate */
10949 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10950 rc = pMedium->i_removeBackReference(mData->mUuid);
10951 if (FAILED(rc))
10952 throw rc;
10953 continue;
10954 }
10955 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10956 }
10957
10958 /* If there are implicit attachments to delete, throw away the lock
10959 * map contents (which will unlock all media) since the medium
10960 * attachments will be rolled back. Below we need to completely
10961 * recreate the lock map anyway since it is infinitely complex to
10962 * do this incrementally (would need reconstructing each attachment
10963 * change, which would be extremely hairy). */
10964 if (implicitAtts.size() != 0)
10965 {
10966 ErrorInfoKeeper eik;
10967
10968 HRESULT rc1 = lockedMediaMap->Clear();
10969 AssertComRC(rc1);
10970 }
10971
10972 /* rollback hard disk changes */
10973 mMediumAttachments.rollback();
10974
10975 MultiResult mrc(S_OK);
10976
10977 // Delete unused implicit diffs.
10978 if (implicitAtts.size() != 0)
10979 {
10980 alock.release();
10981
10982 for (MediumAttachmentList::const_iterator
10983 it = implicitAtts.begin();
10984 it != implicitAtts.end();
10985 ++it)
10986 {
10987 // Remove medium associated with this attachment.
10988 ComObjPtr<MediumAttachment> pAtt = *it;
10989 Assert(pAtt);
10990 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10991 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10992 Assert(pMedium);
10993
10994 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10995 // continue on delete failure, just collect error messages
10996 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10997 pMedium->i_getLocationFull().c_str() ));
10998 mrc = rc;
10999 }
11000 // Clear the list of deleted implicit attachments now, while not
11001 // holding the lock, as it will ultimately trigger Medium::uninit()
11002 // calls which assume that the media tree lock isn't held.
11003 implicitAtts.clear();
11004
11005 alock.acquire();
11006
11007 /* if there is a VM recreate media lock map as mentioned above,
11008 * otherwise it is a waste of time and we leave things unlocked */
11009 if (aOnline)
11010 {
11011 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11012 /* must never be NULL, but better safe than sorry */
11013 if (!pMachine.isNull())
11014 {
11015 alock.release();
11016 rc = mData->mSession.mMachine->i_lockMedia();
11017 alock.acquire();
11018 if (FAILED(rc))
11019 throw rc;
11020 }
11021 }
11022 }
11023 }
11024 catch (HRESULT aRC) {rc = aRC;}
11025
11026 if (mData->mMachineState == MachineState_SettingUp)
11027 i_setMachineState(oldState);
11028
11029 /* unlock all hard disks we locked when there is no VM */
11030 if (!aOnline)
11031 {
11032 ErrorInfoKeeper eik;
11033
11034 HRESULT rc1 = lockedMediaMap->Clear();
11035 AssertComRC(rc1);
11036 }
11037
11038 return rc;
11039}
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 aControllerName
11049 * @param aControllerPort
11050 * @param aDevice
11051 * @return
11052 */
11053MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11054 const Utf8Str &aControllerName,
11055 LONG aControllerPort,
11056 LONG aDevice)
11057{
11058 for (MediumAttachmentList::const_iterator
11059 it = ll.begin();
11060 it != ll.end();
11061 ++it)
11062 {
11063 MediumAttachment *pAttach = *it;
11064 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11065 return pAttach;
11066 }
11067
11068 return NULL;
11069}
11070
11071/**
11072 * Looks through the given list of media attachments for one with the given parameters
11073 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11074 * can be searched as well if needed.
11075 *
11076 * @param ll
11077 * @param pMedium
11078 * @return
11079 */
11080MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11081 ComObjPtr<Medium> pMedium)
11082{
11083 for (MediumAttachmentList::const_iterator
11084 it = ll.begin();
11085 it != ll.end();
11086 ++it)
11087 {
11088 MediumAttachment *pAttach = *it;
11089 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11090 if (pMediumThis == pMedium)
11091 return pAttach;
11092 }
11093
11094 return NULL;
11095}
11096
11097/**
11098 * Looks through the given list of media attachments for one with the given parameters
11099 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11100 * can be searched as well if needed.
11101 *
11102 * @param ll
11103 * @param id
11104 * @return
11105 */
11106MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11107 Guid &id)
11108{
11109 for (MediumAttachmentList::const_iterator
11110 it = ll.begin();
11111 it != ll.end();
11112 ++it)
11113 {
11114 MediumAttachment *pAttach = *it;
11115 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11116 if (pMediumThis->i_getId() == id)
11117 return pAttach;
11118 }
11119
11120 return NULL;
11121}
11122
11123/**
11124 * Main implementation for Machine::DetachDevice. This also gets called
11125 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11126 *
11127 * @param pAttach Medium attachment to detach.
11128 * @param writeLock Machine write lock which the caller must have locked once.
11129 * This may be released temporarily in here.
11130 * @param pSnapshot If NULL, then the detachment is for the current machine.
11131 * Otherwise this is for a SnapshotMachine, and this must be
11132 * its snapshot.
11133 * @return
11134 */
11135HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11136 AutoWriteLock &writeLock,
11137 Snapshot *pSnapshot)
11138{
11139 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11140 DeviceType_T mediumType = pAttach->i_getType();
11141
11142 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11143
11144 if (pAttach->i_isImplicit())
11145 {
11146 /* attempt to implicitly delete the implicitly created diff */
11147
11148 /// @todo move the implicit flag from MediumAttachment to Medium
11149 /// and forbid any hard disk operation when it is implicit. Or maybe
11150 /// a special media state for it to make it even more simple.
11151
11152 Assert(mMediumAttachments.isBackedUp());
11153
11154 /* will release the lock before the potentially lengthy operation, so
11155 * protect with the special state */
11156 MachineState_T oldState = mData->mMachineState;
11157 i_setMachineState(MachineState_SettingUp);
11158
11159 writeLock.release();
11160
11161 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11162 true /*aWait*/,
11163 false /*aNotify*/);
11164
11165 writeLock.acquire();
11166
11167 i_setMachineState(oldState);
11168
11169 if (FAILED(rc)) return rc;
11170 }
11171
11172 i_setModified(IsModified_Storage);
11173 mMediumAttachments.backup();
11174 mMediumAttachments->remove(pAttach);
11175
11176 if (!oldmedium.isNull())
11177 {
11178 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11179 if (pSnapshot)
11180 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11181 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11182 else if (mediumType != DeviceType_HardDisk)
11183 oldmedium->i_removeBackReference(mData->mUuid);
11184 }
11185
11186 return S_OK;
11187}
11188
11189/**
11190 * Goes thru all media of the given list and
11191 *
11192 * 1) calls i_detachDevice() on each of them for this machine and
11193 * 2) adds all Medium objects found in the process to the given list,
11194 * depending on cleanupMode.
11195 *
11196 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11197 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11198 * media to the list.
11199 *
11200 * This gets called from Machine::Unregister, both for the actual Machine and
11201 * the SnapshotMachine objects that might be found in the snapshots.
11202 *
11203 * Requires caller and locking. The machine lock must be passed in because it
11204 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11205 *
11206 * @param writeLock Machine lock from top-level caller; this gets passed to
11207 * i_detachDevice.
11208 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11209 * object if called for a SnapshotMachine.
11210 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11211 * added to llMedia; if Full, then all media get added;
11212 * otherwise no media get added.
11213 * @param llMedia Caller's list to receive Medium objects which got detached so
11214 * caller can close() them, depending on cleanupMode.
11215 * @return
11216 */
11217HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11218 Snapshot *pSnapshot,
11219 CleanupMode_T cleanupMode,
11220 MediaList &llMedia)
11221{
11222 Assert(isWriteLockOnCurrentThread());
11223
11224 HRESULT rc;
11225
11226 // make a temporary list because i_detachDevice invalidates iterators into
11227 // mMediumAttachments
11228 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11229
11230 for (MediumAttachmentList::iterator
11231 it = llAttachments2.begin();
11232 it != llAttachments2.end();
11233 ++it)
11234 {
11235 ComObjPtr<MediumAttachment> &pAttach = *it;
11236 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11237
11238 if (!pMedium.isNull())
11239 {
11240 AutoCaller mac(pMedium);
11241 if (FAILED(mac.rc())) return mac.rc();
11242 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11243 DeviceType_T devType = pMedium->i_getDeviceType();
11244 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11245 && devType == DeviceType_HardDisk)
11246 || (cleanupMode == CleanupMode_Full)
11247 )
11248 {
11249 llMedia.push_back(pMedium);
11250 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11251 /* Not allowed to keep this lock as below we need the parent
11252 * medium lock, and the lock order is parent to child. */
11253 lock.release();
11254 /*
11255 * Search for medias which are not attached to any machine, but
11256 * in the chain to an attached disk. Mediums are only consided
11257 * if they are:
11258 * - have only one child
11259 * - no references to any machines
11260 * - are of normal medium type
11261 */
11262 while (!pParent.isNull())
11263 {
11264 AutoCaller mac1(pParent);
11265 if (FAILED(mac1.rc())) return mac1.rc();
11266 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11267 if (pParent->i_getChildren().size() == 1)
11268 {
11269 if ( pParent->i_getMachineBackRefCount() == 0
11270 && pParent->i_getType() == MediumType_Normal
11271 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11272 llMedia.push_back(pParent);
11273 }
11274 else
11275 break;
11276 pParent = pParent->i_getParent();
11277 }
11278 }
11279 }
11280
11281 // real machine: then we need to use the proper method
11282 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11283
11284 if (FAILED(rc))
11285 return rc;
11286 }
11287
11288 return S_OK;
11289}
11290
11291/**
11292 * Perform deferred hard disk detachments.
11293 *
11294 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11295 * changed (not backed up).
11296 *
11297 * If @a aOnline is @c true then this method will also unlock the old hard
11298 * disks for which the new implicit diffs were created and will lock these new
11299 * diffs for writing.
11300 *
11301 * @param aOnline Whether the VM was online prior to this operation.
11302 *
11303 * @note Locks this object for writing!
11304 */
11305void Machine::i_commitMedia(bool aOnline /*= false*/)
11306{
11307 AutoCaller autoCaller(this);
11308 AssertComRCReturnVoid(autoCaller.rc());
11309
11310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11311
11312 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11313
11314 HRESULT rc = S_OK;
11315
11316 /* no attach/detach operations -- nothing to do */
11317 if (!mMediumAttachments.isBackedUp())
11318 return;
11319
11320 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11321 bool fMediaNeedsLocking = false;
11322
11323 /* enumerate new attachments */
11324 for (MediumAttachmentList::const_iterator
11325 it = mMediumAttachments->begin();
11326 it != mMediumAttachments->end();
11327 ++it)
11328 {
11329 MediumAttachment *pAttach = *it;
11330
11331 pAttach->i_commit();
11332
11333 Medium *pMedium = pAttach->i_getMedium();
11334 bool fImplicit = pAttach->i_isImplicit();
11335
11336 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11337 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11338 fImplicit));
11339
11340 /** @todo convert all this Machine-based voodoo to MediumAttachment
11341 * based commit logic. */
11342 if (fImplicit)
11343 {
11344 /* convert implicit attachment to normal */
11345 pAttach->i_setImplicit(false);
11346
11347 if ( aOnline
11348 && pMedium
11349 && pAttach->i_getType() == DeviceType_HardDisk
11350 )
11351 {
11352 /* update the appropriate lock list */
11353 MediumLockList *pMediumLockList;
11354 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11355 AssertComRC(rc);
11356 if (pMediumLockList)
11357 {
11358 /* unlock if there's a need to change the locking */
11359 if (!fMediaNeedsLocking)
11360 {
11361 rc = mData->mSession.mLockedMedia.Unlock();
11362 AssertComRC(rc);
11363 fMediaNeedsLocking = true;
11364 }
11365 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11366 AssertComRC(rc);
11367 rc = pMediumLockList->Append(pMedium, true);
11368 AssertComRC(rc);
11369 }
11370 }
11371
11372 continue;
11373 }
11374
11375 if (pMedium)
11376 {
11377 /* was this medium attached before? */
11378 for (MediumAttachmentList::iterator
11379 oldIt = oldAtts.begin();
11380 oldIt != oldAtts.end();
11381 ++oldIt)
11382 {
11383 MediumAttachment *pOldAttach = *oldIt;
11384 if (pOldAttach->i_getMedium() == pMedium)
11385 {
11386 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11387
11388 /* yes: remove from old to avoid de-association */
11389 oldAtts.erase(oldIt);
11390 break;
11391 }
11392 }
11393 }
11394 }
11395
11396 /* enumerate remaining old attachments and de-associate from the
11397 * current machine state */
11398 for (MediumAttachmentList::const_iterator
11399 it = oldAtts.begin();
11400 it != oldAtts.end();
11401 ++it)
11402 {
11403 MediumAttachment *pAttach = *it;
11404 Medium *pMedium = pAttach->i_getMedium();
11405
11406 /* Detach only hard disks, since DVD/floppy media is detached
11407 * instantly in MountMedium. */
11408 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11409 {
11410 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11411
11412 /* now de-associate from the current machine state */
11413 rc = pMedium->i_removeBackReference(mData->mUuid);
11414 AssertComRC(rc);
11415
11416 if (aOnline)
11417 {
11418 /* unlock since medium is not used anymore */
11419 MediumLockList *pMediumLockList;
11420 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11421 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11422 {
11423 /* this happens for online snapshots, there the attachment
11424 * is changing, but only to a diff image created under
11425 * the old one, so there is no separate lock list */
11426 Assert(!pMediumLockList);
11427 }
11428 else
11429 {
11430 AssertComRC(rc);
11431 if (pMediumLockList)
11432 {
11433 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11434 AssertComRC(rc);
11435 }
11436 }
11437 }
11438 }
11439 }
11440
11441 /* take media locks again so that the locking state is consistent */
11442 if (fMediaNeedsLocking)
11443 {
11444 Assert(aOnline);
11445 rc = mData->mSession.mLockedMedia.Lock();
11446 AssertComRC(rc);
11447 }
11448
11449 /* commit the hard disk changes */
11450 mMediumAttachments.commit();
11451
11452 if (i_isSessionMachine())
11453 {
11454 /*
11455 * Update the parent machine to point to the new owner.
11456 * This is necessary because the stored parent will point to the
11457 * session machine otherwise and cause crashes or errors later
11458 * when the session machine gets invalid.
11459 */
11460 /** @todo Change the MediumAttachment class to behave like any other
11461 * class in this regard by creating peer MediumAttachment
11462 * objects for session machines and share the data with the peer
11463 * machine.
11464 */
11465 for (MediumAttachmentList::const_iterator
11466 it = mMediumAttachments->begin();
11467 it != mMediumAttachments->end();
11468 ++it)
11469 (*it)->i_updateParentMachine(mPeer);
11470
11471 /* attach new data to the primary machine and reshare it */
11472 mPeer->mMediumAttachments.attach(mMediumAttachments);
11473 }
11474
11475 return;
11476}
11477
11478/**
11479 * Perform deferred deletion of implicitly created diffs.
11480 *
11481 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11482 * changed (not backed up).
11483 *
11484 * @note Locks this object for writing!
11485 */
11486void Machine::i_rollbackMedia()
11487{
11488 AutoCaller autoCaller(this);
11489 AssertComRCReturnVoid(autoCaller.rc());
11490
11491 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11492 LogFlowThisFunc(("Entering rollbackMedia\n"));
11493
11494 HRESULT rc = S_OK;
11495
11496 /* no attach/detach operations -- nothing to do */
11497 if (!mMediumAttachments.isBackedUp())
11498 return;
11499
11500 /* enumerate new attachments */
11501 for (MediumAttachmentList::const_iterator
11502 it = mMediumAttachments->begin();
11503 it != mMediumAttachments->end();
11504 ++it)
11505 {
11506 MediumAttachment *pAttach = *it;
11507 /* Fix up the backrefs for DVD/floppy media. */
11508 if (pAttach->i_getType() != DeviceType_HardDisk)
11509 {
11510 Medium *pMedium = pAttach->i_getMedium();
11511 if (pMedium)
11512 {
11513 rc = pMedium->i_removeBackReference(mData->mUuid);
11514 AssertComRC(rc);
11515 }
11516 }
11517
11518 (*it)->i_rollback();
11519
11520 pAttach = *it;
11521 /* Fix up the backrefs for DVD/floppy media. */
11522 if (pAttach->i_getType() != DeviceType_HardDisk)
11523 {
11524 Medium *pMedium = pAttach->i_getMedium();
11525 if (pMedium)
11526 {
11527 rc = pMedium->i_addBackReference(mData->mUuid);
11528 AssertComRC(rc);
11529 }
11530 }
11531 }
11532
11533 /** @todo convert all this Machine-based voodoo to MediumAttachment
11534 * based rollback logic. */
11535 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11536
11537 return;
11538}
11539
11540/**
11541 * Returns true if the settings file is located in the directory named exactly
11542 * as the machine; this means, among other things, that the machine directory
11543 * should be auto-renamed.
11544 *
11545 * @param aSettingsDir if not NULL, the full machine settings file directory
11546 * name will be assigned there.
11547 *
11548 * @note Doesn't lock anything.
11549 * @note Not thread safe (must be called from this object's lock).
11550 */
11551bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11552{
11553 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11554 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11555 if (aSettingsDir)
11556 *aSettingsDir = strMachineDirName;
11557 strMachineDirName.stripPath(); // vmname
11558 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11559 strConfigFileOnly.stripPath() // vmname.vbox
11560 .stripSuffix(); // vmname
11561 /** @todo hack, make somehow use of ComposeMachineFilename */
11562 if (mUserData->s.fDirectoryIncludesUUID)
11563 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11564
11565 AssertReturn(!strMachineDirName.isEmpty(), false);
11566 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11567
11568 return strMachineDirName == strConfigFileOnly;
11569}
11570
11571/**
11572 * Discards all changes to machine settings.
11573 *
11574 * @param aNotify Whether to notify the direct session about changes or not.
11575 *
11576 * @note Locks objects for writing!
11577 */
11578void Machine::i_rollback(bool aNotify)
11579{
11580 AutoCaller autoCaller(this);
11581 AssertComRCReturn(autoCaller.rc(), (void)0);
11582
11583 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11584
11585 if (!mStorageControllers.isNull())
11586 {
11587 if (mStorageControllers.isBackedUp())
11588 {
11589 /* unitialize all new devices (absent in the backed up list). */
11590 StorageControllerList *backedList = mStorageControllers.backedUpData();
11591 for (StorageControllerList::const_iterator
11592 it = mStorageControllers->begin();
11593 it != mStorageControllers->end();
11594 ++it)
11595 {
11596 if ( std::find(backedList->begin(), backedList->end(), *it)
11597 == backedList->end()
11598 )
11599 {
11600 (*it)->uninit();
11601 }
11602 }
11603
11604 /* restore the list */
11605 mStorageControllers.rollback();
11606 }
11607
11608 /* rollback any changes to devices after restoring the list */
11609 if (mData->flModifications & IsModified_Storage)
11610 {
11611 for (StorageControllerList::const_iterator
11612 it = mStorageControllers->begin();
11613 it != mStorageControllers->end();
11614 ++it)
11615 {
11616 (*it)->i_rollback();
11617 }
11618 }
11619 }
11620
11621 if (!mUSBControllers.isNull())
11622 {
11623 if (mUSBControllers.isBackedUp())
11624 {
11625 /* unitialize all new devices (absent in the backed up list). */
11626 USBControllerList *backedList = mUSBControllers.backedUpData();
11627 for (USBControllerList::const_iterator
11628 it = mUSBControllers->begin();
11629 it != mUSBControllers->end();
11630 ++it)
11631 {
11632 if ( std::find(backedList->begin(), backedList->end(), *it)
11633 == backedList->end()
11634 )
11635 {
11636 (*it)->uninit();
11637 }
11638 }
11639
11640 /* restore the list */
11641 mUSBControllers.rollback();
11642 }
11643
11644 /* rollback any changes to devices after restoring the list */
11645 if (mData->flModifications & IsModified_USB)
11646 {
11647 for (USBControllerList::const_iterator
11648 it = mUSBControllers->begin();
11649 it != mUSBControllers->end();
11650 ++it)
11651 {
11652 (*it)->i_rollback();
11653 }
11654 }
11655 }
11656
11657 mUserData.rollback();
11658
11659 mHWData.rollback();
11660
11661 if (mData->flModifications & IsModified_Storage)
11662 i_rollbackMedia();
11663
11664 if (mBIOSSettings)
11665 mBIOSSettings->i_rollback();
11666
11667 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11668 mRecordingSettings->i_rollback();
11669
11670 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11671 mVRDEServer->i_rollback();
11672
11673 if (mAudioAdapter)
11674 mAudioAdapter->i_rollback();
11675
11676 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11677 mUSBDeviceFilters->i_rollback();
11678
11679 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11680 mBandwidthControl->i_rollback();
11681
11682 if (!mHWData.isNull())
11683 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11684 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11685 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11686 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11687
11688 if (mData->flModifications & IsModified_NetworkAdapters)
11689 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11690 if ( mNetworkAdapters[slot]
11691 && mNetworkAdapters[slot]->i_isModified())
11692 {
11693 mNetworkAdapters[slot]->i_rollback();
11694 networkAdapters[slot] = mNetworkAdapters[slot];
11695 }
11696
11697 if (mData->flModifications & IsModified_SerialPorts)
11698 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11699 if ( mSerialPorts[slot]
11700 && mSerialPorts[slot]->i_isModified())
11701 {
11702 mSerialPorts[slot]->i_rollback();
11703 serialPorts[slot] = mSerialPorts[slot];
11704 }
11705
11706 if (mData->flModifications & IsModified_ParallelPorts)
11707 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11708 if ( mParallelPorts[slot]
11709 && mParallelPorts[slot]->i_isModified())
11710 {
11711 mParallelPorts[slot]->i_rollback();
11712 parallelPorts[slot] = mParallelPorts[slot];
11713 }
11714
11715 if (aNotify)
11716 {
11717 /* inform the direct session about changes */
11718
11719 ComObjPtr<Machine> that = this;
11720 uint32_t flModifications = mData->flModifications;
11721 alock.release();
11722
11723 if (flModifications & IsModified_SharedFolders)
11724 that->i_onSharedFolderChange();
11725
11726 if (flModifications & IsModified_VRDEServer)
11727 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11728 if (flModifications & IsModified_USB)
11729 that->i_onUSBControllerChange();
11730
11731 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11732 if (networkAdapters[slot])
11733 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11734 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11735 if (serialPorts[slot])
11736 that->i_onSerialPortChange(serialPorts[slot]);
11737 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11738 if (parallelPorts[slot])
11739 that->i_onParallelPortChange(parallelPorts[slot]);
11740
11741 if (flModifications & IsModified_Storage)
11742 {
11743 for (StorageControllerList::const_iterator
11744 it = mStorageControllers->begin();
11745 it != mStorageControllers->end();
11746 ++it)
11747 {
11748 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11749 }
11750 }
11751
11752
11753#if 0
11754 if (flModifications & IsModified_BandwidthControl)
11755 that->onBandwidthControlChange();
11756#endif
11757 }
11758}
11759
11760/**
11761 * Commits all the changes to machine settings.
11762 *
11763 * Note that this operation is supposed to never fail.
11764 *
11765 * @note Locks this object and children for writing.
11766 */
11767void Machine::i_commit()
11768{
11769 AutoCaller autoCaller(this);
11770 AssertComRCReturnVoid(autoCaller.rc());
11771
11772 AutoCaller peerCaller(mPeer);
11773 AssertComRCReturnVoid(peerCaller.rc());
11774
11775 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11776
11777 /*
11778 * use safe commit to ensure Snapshot machines (that share mUserData)
11779 * will still refer to a valid memory location
11780 */
11781 mUserData.commitCopy();
11782
11783 mHWData.commit();
11784
11785 if (mMediumAttachments.isBackedUp())
11786 i_commitMedia(Global::IsOnline(mData->mMachineState));
11787
11788 mBIOSSettings->i_commit();
11789 mRecordingSettings->i_commit();
11790 mVRDEServer->i_commit();
11791 mAudioAdapter->i_commit();
11792 mUSBDeviceFilters->i_commit();
11793 mBandwidthControl->i_commit();
11794
11795 /* Since mNetworkAdapters is a list which might have been changed (resized)
11796 * without using the Backupable<> template we need to handle the copying
11797 * of the list entries manually, including the creation of peers for the
11798 * new objects. */
11799 bool commitNetworkAdapters = false;
11800 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11801 if (mPeer)
11802 {
11803 /* commit everything, even the ones which will go away */
11804 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11805 mNetworkAdapters[slot]->i_commit();
11806 /* copy over the new entries, creating a peer and uninit the original */
11807 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11808 for (size_t slot = 0; slot < newSize; slot++)
11809 {
11810 /* look if this adapter has a peer device */
11811 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11812 if (!peer)
11813 {
11814 /* no peer means the adapter is a newly created one;
11815 * create a peer owning data this data share it with */
11816 peer.createObject();
11817 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11818 }
11819 mPeer->mNetworkAdapters[slot] = peer;
11820 }
11821 /* uninit any no longer needed network adapters */
11822 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11823 mNetworkAdapters[slot]->uninit();
11824 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11825 {
11826 if (mPeer->mNetworkAdapters[slot])
11827 mPeer->mNetworkAdapters[slot]->uninit();
11828 }
11829 /* Keep the original network adapter count until this point, so that
11830 * discarding a chipset type change will not lose settings. */
11831 mNetworkAdapters.resize(newSize);
11832 mPeer->mNetworkAdapters.resize(newSize);
11833 }
11834 else
11835 {
11836 /* we have no peer (our parent is the newly created machine);
11837 * just commit changes to the network adapters */
11838 commitNetworkAdapters = true;
11839 }
11840 if (commitNetworkAdapters)
11841 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11842 mNetworkAdapters[slot]->i_commit();
11843
11844 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11845 mSerialPorts[slot]->i_commit();
11846 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11847 mParallelPorts[slot]->i_commit();
11848
11849 bool commitStorageControllers = false;
11850
11851 if (mStorageControllers.isBackedUp())
11852 {
11853 mStorageControllers.commit();
11854
11855 if (mPeer)
11856 {
11857 /* Commit all changes to new controllers (this will reshare data with
11858 * peers for those who have peers) */
11859 StorageControllerList *newList = new StorageControllerList();
11860 for (StorageControllerList::const_iterator
11861 it = mStorageControllers->begin();
11862 it != mStorageControllers->end();
11863 ++it)
11864 {
11865 (*it)->i_commit();
11866
11867 /* look if this controller has a peer device */
11868 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11869 if (!peer)
11870 {
11871 /* no peer means the device is a newly created one;
11872 * create a peer owning data this device share it with */
11873 peer.createObject();
11874 peer->init(mPeer, *it, true /* aReshare */);
11875 }
11876 else
11877 {
11878 /* remove peer from the old list */
11879 mPeer->mStorageControllers->remove(peer);
11880 }
11881 /* and add it to the new list */
11882 newList->push_back(peer);
11883 }
11884
11885 /* uninit old peer's controllers that are left */
11886 for (StorageControllerList::const_iterator
11887 it = mPeer->mStorageControllers->begin();
11888 it != mPeer->mStorageControllers->end();
11889 ++it)
11890 {
11891 (*it)->uninit();
11892 }
11893
11894 /* attach new list of controllers to our peer */
11895 mPeer->mStorageControllers.attach(newList);
11896 }
11897 else
11898 {
11899 /* we have no peer (our parent is the newly created machine);
11900 * just commit changes to devices */
11901 commitStorageControllers = true;
11902 }
11903 }
11904 else
11905 {
11906 /* the list of controllers itself is not changed,
11907 * just commit changes to controllers themselves */
11908 commitStorageControllers = true;
11909 }
11910
11911 if (commitStorageControllers)
11912 {
11913 for (StorageControllerList::const_iterator
11914 it = mStorageControllers->begin();
11915 it != mStorageControllers->end();
11916 ++it)
11917 {
11918 (*it)->i_commit();
11919 }
11920 }
11921
11922 bool commitUSBControllers = false;
11923
11924 if (mUSBControllers.isBackedUp())
11925 {
11926 mUSBControllers.commit();
11927
11928 if (mPeer)
11929 {
11930 /* Commit all changes to new controllers (this will reshare data with
11931 * peers for those who have peers) */
11932 USBControllerList *newList = new USBControllerList();
11933 for (USBControllerList::const_iterator
11934 it = mUSBControllers->begin();
11935 it != mUSBControllers->end();
11936 ++it)
11937 {
11938 (*it)->i_commit();
11939
11940 /* look if this controller has a peer device */
11941 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11942 if (!peer)
11943 {
11944 /* no peer means the device is a newly created one;
11945 * create a peer owning data this device share it with */
11946 peer.createObject();
11947 peer->init(mPeer, *it, true /* aReshare */);
11948 }
11949 else
11950 {
11951 /* remove peer from the old list */
11952 mPeer->mUSBControllers->remove(peer);
11953 }
11954 /* and add it to the new list */
11955 newList->push_back(peer);
11956 }
11957
11958 /* uninit old peer's controllers that are left */
11959 for (USBControllerList::const_iterator
11960 it = mPeer->mUSBControllers->begin();
11961 it != mPeer->mUSBControllers->end();
11962 ++it)
11963 {
11964 (*it)->uninit();
11965 }
11966
11967 /* attach new list of controllers to our peer */
11968 mPeer->mUSBControllers.attach(newList);
11969 }
11970 else
11971 {
11972 /* we have no peer (our parent is the newly created machine);
11973 * just commit changes to devices */
11974 commitUSBControllers = true;
11975 }
11976 }
11977 else
11978 {
11979 /* the list of controllers itself is not changed,
11980 * just commit changes to controllers themselves */
11981 commitUSBControllers = true;
11982 }
11983
11984 if (commitUSBControllers)
11985 {
11986 for (USBControllerList::const_iterator
11987 it = mUSBControllers->begin();
11988 it != mUSBControllers->end();
11989 ++it)
11990 {
11991 (*it)->i_commit();
11992 }
11993 }
11994
11995 if (i_isSessionMachine())
11996 {
11997 /* attach new data to the primary machine and reshare it */
11998 mPeer->mUserData.attach(mUserData);
11999 mPeer->mHWData.attach(mHWData);
12000 /* mmMediumAttachments is reshared by fixupMedia */
12001 // mPeer->mMediumAttachments.attach(mMediumAttachments);
12002 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
12003 }
12004}
12005
12006/**
12007 * Copies all the hardware data from the given machine.
12008 *
12009 * Currently, only called when the VM is being restored from a snapshot. In
12010 * particular, this implies that the VM is not running during this method's
12011 * call.
12012 *
12013 * @note This method must be called from under this object's lock.
12014 *
12015 * @note This method doesn't call #i_commit(), so all data remains backed up and
12016 * unsaved.
12017 */
12018void Machine::i_copyFrom(Machine *aThat)
12019{
12020 AssertReturnVoid(!i_isSnapshotMachine());
12021 AssertReturnVoid(aThat->i_isSnapshotMachine());
12022
12023 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12024
12025 mHWData.assignCopy(aThat->mHWData);
12026
12027 // create copies of all shared folders (mHWData after attaching a copy
12028 // contains just references to original objects)
12029 for (HWData::SharedFolderList::iterator
12030 it = mHWData->mSharedFolders.begin();
12031 it != mHWData->mSharedFolders.end();
12032 ++it)
12033 {
12034 ComObjPtr<SharedFolder> folder;
12035 folder.createObject();
12036 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12037 AssertComRC(rc);
12038 *it = folder;
12039 }
12040
12041 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12042 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12043 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12044 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12045 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12046 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12047
12048 /* create private copies of all controllers */
12049 mStorageControllers.backup();
12050 mStorageControllers->clear();
12051 for (StorageControllerList::const_iterator
12052 it = aThat->mStorageControllers->begin();
12053 it != aThat->mStorageControllers->end();
12054 ++it)
12055 {
12056 ComObjPtr<StorageController> ctrl;
12057 ctrl.createObject();
12058 ctrl->initCopy(this, *it);
12059 mStorageControllers->push_back(ctrl);
12060 }
12061
12062 /* create private copies of all USB controllers */
12063 mUSBControllers.backup();
12064 mUSBControllers->clear();
12065 for (USBControllerList::const_iterator
12066 it = aThat->mUSBControllers->begin();
12067 it != aThat->mUSBControllers->end();
12068 ++it)
12069 {
12070 ComObjPtr<USBController> ctrl;
12071 ctrl.createObject();
12072 ctrl->initCopy(this, *it);
12073 mUSBControllers->push_back(ctrl);
12074 }
12075
12076 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12077 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12078 {
12079 if (mNetworkAdapters[slot].isNotNull())
12080 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12081 else
12082 {
12083 unconst(mNetworkAdapters[slot]).createObject();
12084 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12085 }
12086 }
12087 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12088 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12089 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12090 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12091}
12092
12093/**
12094 * Returns whether the given storage controller is hotplug capable.
12095 *
12096 * @returns true if the controller supports hotplugging
12097 * false otherwise.
12098 * @param enmCtrlType The controller type to check for.
12099 */
12100bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12101{
12102 ComPtr<ISystemProperties> systemProperties;
12103 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12104 if (FAILED(rc))
12105 return false;
12106
12107 BOOL aHotplugCapable = FALSE;
12108 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12109
12110 return RT_BOOL(aHotplugCapable);
12111}
12112
12113#ifdef VBOX_WITH_RESOURCE_USAGE_API
12114
12115void Machine::i_getDiskList(MediaList &list)
12116{
12117 for (MediumAttachmentList::const_iterator
12118 it = mMediumAttachments->begin();
12119 it != mMediumAttachments->end();
12120 ++it)
12121 {
12122 MediumAttachment *pAttach = *it;
12123 /* just in case */
12124 AssertContinue(pAttach);
12125
12126 AutoCaller localAutoCallerA(pAttach);
12127 if (FAILED(localAutoCallerA.rc())) continue;
12128
12129 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12130
12131 if (pAttach->i_getType() == DeviceType_HardDisk)
12132 list.push_back(pAttach->i_getMedium());
12133 }
12134}
12135
12136void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12137{
12138 AssertReturnVoid(isWriteLockOnCurrentThread());
12139 AssertPtrReturnVoid(aCollector);
12140
12141 pm::CollectorHAL *hal = aCollector->getHAL();
12142 /* Create sub metrics */
12143 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12144 "Percentage of processor time spent in user mode by the VM process.");
12145 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12146 "Percentage of processor time spent in kernel mode by the VM process.");
12147 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12148 "Size of resident portion of VM process in memory.");
12149 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12150 "Actual size of all VM disks combined.");
12151 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12152 "Network receive rate.");
12153 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12154 "Network transmit rate.");
12155 /* Create and register base metrics */
12156 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12157 cpuLoadUser, cpuLoadKernel);
12158 aCollector->registerBaseMetric(cpuLoad);
12159 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12160 ramUsageUsed);
12161 aCollector->registerBaseMetric(ramUsage);
12162 MediaList disks;
12163 i_getDiskList(disks);
12164 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12165 diskUsageUsed);
12166 aCollector->registerBaseMetric(diskUsage);
12167
12168 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12170 new pm::AggregateAvg()));
12171 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12172 new pm::AggregateMin()));
12173 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12174 new pm::AggregateMax()));
12175 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12176 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12177 new pm::AggregateAvg()));
12178 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12179 new pm::AggregateMin()));
12180 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12181 new pm::AggregateMax()));
12182
12183 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12184 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12185 new pm::AggregateAvg()));
12186 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12187 new pm::AggregateMin()));
12188 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12189 new pm::AggregateMax()));
12190
12191 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12192 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12193 new pm::AggregateAvg()));
12194 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12195 new pm::AggregateMin()));
12196 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12197 new pm::AggregateMax()));
12198
12199
12200 /* Guest metrics collector */
12201 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12202 aCollector->registerGuest(mCollectorGuest);
12203 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12204
12205 /* Create sub metrics */
12206 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12207 "Percentage of processor time spent in user mode as seen by the guest.");
12208 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12209 "Percentage of processor time spent in kernel mode as seen by the guest.");
12210 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12211 "Percentage of processor time spent idling as seen by the guest.");
12212
12213 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12214 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12215 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12216 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12217 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12218 pm::SubMetric *guestMemCache = new pm::SubMetric(
12219 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12220
12221 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12222 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12223
12224 /* Create and register base metrics */
12225 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12226 machineNetRx, machineNetTx);
12227 aCollector->registerBaseMetric(machineNetRate);
12228
12229 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12230 guestLoadUser, guestLoadKernel, guestLoadIdle);
12231 aCollector->registerBaseMetric(guestCpuLoad);
12232
12233 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12234 guestMemTotal, guestMemFree,
12235 guestMemBalloon, guestMemShared,
12236 guestMemCache, guestPagedTotal);
12237 aCollector->registerBaseMetric(guestCpuMem);
12238
12239 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12240 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12241 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12242 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12243
12244 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12245 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12246 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12247 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12248
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12252 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12253
12254 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12257 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12258
12259 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12260 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12262 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12263
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12267 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12268
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12272 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12273
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12277 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12278
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12282 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12283
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12287 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12288
12289 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12290 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12291 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12292 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12293}
12294
12295void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12296{
12297 AssertReturnVoid(isWriteLockOnCurrentThread());
12298
12299 if (aCollector)
12300 {
12301 aCollector->unregisterMetricsFor(aMachine);
12302 aCollector->unregisterBaseMetricsFor(aMachine);
12303 }
12304}
12305
12306#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12307
12308
12309////////////////////////////////////////////////////////////////////////////////
12310
12311DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12312
12313HRESULT SessionMachine::FinalConstruct()
12314{
12315 LogFlowThisFunc(("\n"));
12316
12317 mClientToken = NULL;
12318
12319 return BaseFinalConstruct();
12320}
12321
12322void SessionMachine::FinalRelease()
12323{
12324 LogFlowThisFunc(("\n"));
12325
12326 Assert(!mClientToken);
12327 /* paranoia, should not hang around any more */
12328 if (mClientToken)
12329 {
12330 delete mClientToken;
12331 mClientToken = NULL;
12332 }
12333
12334 uninit(Uninit::Unexpected);
12335
12336 BaseFinalRelease();
12337}
12338
12339/**
12340 * @note Must be called only by Machine::LockMachine() from its own write lock.
12341 */
12342HRESULT SessionMachine::init(Machine *aMachine)
12343{
12344 LogFlowThisFuncEnter();
12345 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12346
12347 AssertReturn(aMachine, E_INVALIDARG);
12348
12349 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12350
12351 /* Enclose the state transition NotReady->InInit->Ready */
12352 AutoInitSpan autoInitSpan(this);
12353 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12354
12355 HRESULT rc = S_OK;
12356
12357 RT_ZERO(mAuthLibCtx);
12358
12359 /* create the machine client token */
12360 try
12361 {
12362 mClientToken = new ClientToken(aMachine, this);
12363 if (!mClientToken->isReady())
12364 {
12365 delete mClientToken;
12366 mClientToken = NULL;
12367 rc = E_FAIL;
12368 }
12369 }
12370 catch (std::bad_alloc &)
12371 {
12372 rc = E_OUTOFMEMORY;
12373 }
12374 if (FAILED(rc))
12375 return rc;
12376
12377 /* memorize the peer Machine */
12378 unconst(mPeer) = aMachine;
12379 /* share the parent pointer */
12380 unconst(mParent) = aMachine->mParent;
12381
12382 /* take the pointers to data to share */
12383 mData.share(aMachine->mData);
12384 mSSData.share(aMachine->mSSData);
12385
12386 mUserData.share(aMachine->mUserData);
12387 mHWData.share(aMachine->mHWData);
12388 mMediumAttachments.share(aMachine->mMediumAttachments);
12389
12390 mStorageControllers.allocate();
12391 for (StorageControllerList::const_iterator
12392 it = aMachine->mStorageControllers->begin();
12393 it != aMachine->mStorageControllers->end();
12394 ++it)
12395 {
12396 ComObjPtr<StorageController> ctl;
12397 ctl.createObject();
12398 ctl->init(this, *it);
12399 mStorageControllers->push_back(ctl);
12400 }
12401
12402 mUSBControllers.allocate();
12403 for (USBControllerList::const_iterator
12404 it = aMachine->mUSBControllers->begin();
12405 it != aMachine->mUSBControllers->end();
12406 ++it)
12407 {
12408 ComObjPtr<USBController> ctl;
12409 ctl.createObject();
12410 ctl->init(this, *it);
12411 mUSBControllers->push_back(ctl);
12412 }
12413
12414 unconst(mBIOSSettings).createObject();
12415 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12416 unconst(mRecordingSettings).createObject();
12417 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12418 /* create another VRDEServer object that will be mutable */
12419 unconst(mVRDEServer).createObject();
12420 mVRDEServer->init(this, aMachine->mVRDEServer);
12421 /* create another audio adapter object that will be mutable */
12422 unconst(mAudioAdapter).createObject();
12423 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12424 /* create a list of serial ports that will be mutable */
12425 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12426 {
12427 unconst(mSerialPorts[slot]).createObject();
12428 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12429 }
12430 /* create a list of parallel ports that will be mutable */
12431 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12432 {
12433 unconst(mParallelPorts[slot]).createObject();
12434 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12435 }
12436
12437 /* create another USB device filters object that will be mutable */
12438 unconst(mUSBDeviceFilters).createObject();
12439 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12440
12441 /* create a list of network adapters that will be mutable */
12442 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12443 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12444 {
12445 unconst(mNetworkAdapters[slot]).createObject();
12446 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12447 }
12448
12449 /* create another bandwidth control object that will be mutable */
12450 unconst(mBandwidthControl).createObject();
12451 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12452
12453 /* default is to delete saved state on Saved -> PoweredOff transition */
12454 mRemoveSavedState = true;
12455
12456 /* Confirm a successful initialization when it's the case */
12457 autoInitSpan.setSucceeded();
12458
12459 miNATNetworksStarted = 0;
12460
12461 LogFlowThisFuncLeave();
12462 return rc;
12463}
12464
12465/**
12466 * Uninitializes this session object. If the reason is other than
12467 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12468 * or the client watcher code.
12469 *
12470 * @param aReason uninitialization reason
12471 *
12472 * @note Locks mParent + this object for writing.
12473 */
12474void SessionMachine::uninit(Uninit::Reason aReason)
12475{
12476 LogFlowThisFuncEnter();
12477 LogFlowThisFunc(("reason=%d\n", aReason));
12478
12479 /*
12480 * Strongly reference ourselves to prevent this object deletion after
12481 * mData->mSession.mMachine.setNull() below (which can release the last
12482 * reference and call the destructor). Important: this must be done before
12483 * accessing any members (and before AutoUninitSpan that does it as well).
12484 * This self reference will be released as the very last step on return.
12485 */
12486 ComObjPtr<SessionMachine> selfRef;
12487 if (aReason != Uninit::Unexpected)
12488 selfRef = this;
12489
12490 /* Enclose the state transition Ready->InUninit->NotReady */
12491 AutoUninitSpan autoUninitSpan(this);
12492 if (autoUninitSpan.uninitDone())
12493 {
12494 LogFlowThisFunc(("Already uninitialized\n"));
12495 LogFlowThisFuncLeave();
12496 return;
12497 }
12498
12499 if (autoUninitSpan.initFailed())
12500 {
12501 /* We've been called by init() because it's failed. It's not really
12502 * necessary (nor it's safe) to perform the regular uninit sequence
12503 * below, the following is enough.
12504 */
12505 LogFlowThisFunc(("Initialization failed.\n"));
12506 /* destroy the machine client token */
12507 if (mClientToken)
12508 {
12509 delete mClientToken;
12510 mClientToken = NULL;
12511 }
12512 uninitDataAndChildObjects();
12513 mData.free();
12514 unconst(mParent) = NULL;
12515 unconst(mPeer) = NULL;
12516 LogFlowThisFuncLeave();
12517 return;
12518 }
12519
12520 MachineState_T lastState;
12521 {
12522 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12523 lastState = mData->mMachineState;
12524 }
12525 NOREF(lastState);
12526
12527#ifdef VBOX_WITH_USB
12528 // release all captured USB devices, but do this before requesting the locks below
12529 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12530 {
12531 /* Console::captureUSBDevices() is called in the VM process only after
12532 * setting the machine state to Starting or Restoring.
12533 * Console::detachAllUSBDevices() will be called upon successful
12534 * termination. So, we need to release USB devices only if there was
12535 * an abnormal termination of a running VM.
12536 *
12537 * This is identical to SessionMachine::DetachAllUSBDevices except
12538 * for the aAbnormal argument. */
12539 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12540 AssertComRC(rc);
12541 NOREF(rc);
12542
12543 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12544 if (service)
12545 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12546 }
12547#endif /* VBOX_WITH_USB */
12548
12549 // we need to lock this object in uninit() because the lock is shared
12550 // with mPeer (as well as data we modify below). mParent lock is needed
12551 // by several calls to it.
12552 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12553
12554#ifdef VBOX_WITH_RESOURCE_USAGE_API
12555 /*
12556 * It is safe to call Machine::i_unregisterMetrics() here because
12557 * PerformanceCollector::samplerCallback no longer accesses guest methods
12558 * holding the lock.
12559 */
12560 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12561 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12562 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12563 if (mCollectorGuest)
12564 {
12565 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12566 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12567 mCollectorGuest = NULL;
12568 }
12569#endif
12570
12571 if (aReason == Uninit::Abnormal)
12572 {
12573 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12574
12575 /* reset the state to Aborted */
12576 if (mData->mMachineState != MachineState_Aborted)
12577 i_setMachineState(MachineState_Aborted);
12578 }
12579
12580 // any machine settings modified?
12581 if (mData->flModifications)
12582 {
12583 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12584 i_rollback(false /* aNotify */);
12585 }
12586
12587 mData->mSession.mPID = NIL_RTPROCESS;
12588
12589 if (aReason == Uninit::Unexpected)
12590 {
12591 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12592 * client watcher thread to update the set of machines that have open
12593 * sessions. */
12594 mParent->i_updateClientWatcher();
12595 }
12596
12597 /* uninitialize all remote controls */
12598 if (mData->mSession.mRemoteControls.size())
12599 {
12600 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12601 mData->mSession.mRemoteControls.size()));
12602
12603 /* Always restart a the beginning, since the iterator is invalidated
12604 * by using erase(). */
12605 for (Data::Session::RemoteControlList::iterator
12606 it = mData->mSession.mRemoteControls.begin();
12607 it != mData->mSession.mRemoteControls.end();
12608 it = mData->mSession.mRemoteControls.begin())
12609 {
12610 ComPtr<IInternalSessionControl> pControl = *it;
12611 mData->mSession.mRemoteControls.erase(it);
12612 multilock.release();
12613 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12614 HRESULT rc = pControl->Uninitialize();
12615 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12616 if (FAILED(rc))
12617 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12618 multilock.acquire();
12619 }
12620 mData->mSession.mRemoteControls.clear();
12621 }
12622
12623 /* Remove all references to the NAT network service. The service will stop
12624 * if all references (also from other VMs) are removed. */
12625 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12626 {
12627 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12628 {
12629 BOOL enabled;
12630 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12631 if ( FAILED(hrc)
12632 || !enabled)
12633 continue;
12634
12635 NetworkAttachmentType_T type;
12636 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12637 if ( SUCCEEDED(hrc)
12638 && type == NetworkAttachmentType_NATNetwork)
12639 {
12640 Bstr name;
12641 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12642 if (SUCCEEDED(hrc))
12643 {
12644 multilock.release();
12645 Utf8Str strName(name);
12646 LogRel(("VM '%s' stops using NAT network '%s'\n",
12647 mUserData->s.strName.c_str(), strName.c_str()));
12648 mParent->i_natNetworkRefDec(strName);
12649 multilock.acquire();
12650 }
12651 }
12652 }
12653 }
12654
12655 /*
12656 * An expected uninitialization can come only from #i_checkForDeath().
12657 * Otherwise it means that something's gone really wrong (for example,
12658 * the Session implementation has released the VirtualBox reference
12659 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12660 * etc). However, it's also possible, that the client releases the IPC
12661 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12662 * but the VirtualBox release event comes first to the server process.
12663 * This case is practically possible, so we should not assert on an
12664 * unexpected uninit, just log a warning.
12665 */
12666
12667 if (aReason == Uninit::Unexpected)
12668 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12669
12670 if (aReason != Uninit::Normal)
12671 {
12672 mData->mSession.mDirectControl.setNull();
12673 }
12674 else
12675 {
12676 /* this must be null here (see #OnSessionEnd()) */
12677 Assert(mData->mSession.mDirectControl.isNull());
12678 Assert(mData->mSession.mState == SessionState_Unlocking);
12679 Assert(!mData->mSession.mProgress.isNull());
12680 }
12681 if (mData->mSession.mProgress)
12682 {
12683 if (aReason == Uninit::Normal)
12684 mData->mSession.mProgress->i_notifyComplete(S_OK);
12685 else
12686 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12687 COM_IIDOF(ISession),
12688 getComponentName(),
12689 tr("The VM session was aborted"));
12690 mData->mSession.mProgress.setNull();
12691 }
12692
12693 if (mConsoleTaskData.mProgress)
12694 {
12695 Assert(aReason == Uninit::Abnormal);
12696 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12697 COM_IIDOF(ISession),
12698 getComponentName(),
12699 tr("The VM session was aborted"));
12700 mConsoleTaskData.mProgress.setNull();
12701 }
12702
12703 /* remove the association between the peer machine and this session machine */
12704 Assert( (SessionMachine*)mData->mSession.mMachine == this
12705 || aReason == Uninit::Unexpected);
12706
12707 /* reset the rest of session data */
12708 mData->mSession.mLockType = LockType_Null;
12709 mData->mSession.mMachine.setNull();
12710 mData->mSession.mState = SessionState_Unlocked;
12711 mData->mSession.mName.setNull();
12712
12713 /* destroy the machine client token before leaving the exclusive lock */
12714 if (mClientToken)
12715 {
12716 delete mClientToken;
12717 mClientToken = NULL;
12718 }
12719
12720 /* fire an event */
12721 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12722
12723 uninitDataAndChildObjects();
12724
12725 /* free the essential data structure last */
12726 mData.free();
12727
12728 /* release the exclusive lock before setting the below two to NULL */
12729 multilock.release();
12730
12731 unconst(mParent) = NULL;
12732 unconst(mPeer) = NULL;
12733
12734 AuthLibUnload(&mAuthLibCtx);
12735
12736 LogFlowThisFuncLeave();
12737}
12738
12739// util::Lockable interface
12740////////////////////////////////////////////////////////////////////////////////
12741
12742/**
12743 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12744 * with the primary Machine instance (mPeer).
12745 */
12746RWLockHandle *SessionMachine::lockHandle() const
12747{
12748 AssertReturn(mPeer != NULL, NULL);
12749 return mPeer->lockHandle();
12750}
12751
12752// IInternalMachineControl methods
12753////////////////////////////////////////////////////////////////////////////////
12754
12755/**
12756 * Passes collected guest statistics to performance collector object
12757 */
12758HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12759 ULONG aCpuKernel, ULONG aCpuIdle,
12760 ULONG aMemTotal, ULONG aMemFree,
12761 ULONG aMemBalloon, ULONG aMemShared,
12762 ULONG aMemCache, ULONG aPageTotal,
12763 ULONG aAllocVMM, ULONG aFreeVMM,
12764 ULONG aBalloonedVMM, ULONG aSharedVMM,
12765 ULONG aVmNetRx, ULONG aVmNetTx)
12766{
12767#ifdef VBOX_WITH_RESOURCE_USAGE_API
12768 if (mCollectorGuest)
12769 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12770 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12771 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12772 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12773
12774 return S_OK;
12775#else
12776 NOREF(aValidStats);
12777 NOREF(aCpuUser);
12778 NOREF(aCpuKernel);
12779 NOREF(aCpuIdle);
12780 NOREF(aMemTotal);
12781 NOREF(aMemFree);
12782 NOREF(aMemBalloon);
12783 NOREF(aMemShared);
12784 NOREF(aMemCache);
12785 NOREF(aPageTotal);
12786 NOREF(aAllocVMM);
12787 NOREF(aFreeVMM);
12788 NOREF(aBalloonedVMM);
12789 NOREF(aSharedVMM);
12790 NOREF(aVmNetRx);
12791 NOREF(aVmNetTx);
12792 return E_NOTIMPL;
12793#endif
12794}
12795
12796////////////////////////////////////////////////////////////////////////////////
12797//
12798// SessionMachine task records
12799//
12800////////////////////////////////////////////////////////////////////////////////
12801
12802/**
12803 * Task record for saving the machine state.
12804 */
12805class SessionMachine::SaveStateTask
12806 : public Machine::Task
12807{
12808public:
12809 SaveStateTask(SessionMachine *m,
12810 Progress *p,
12811 const Utf8Str &t,
12812 Reason_T enmReason,
12813 const Utf8Str &strStateFilePath)
12814 : Task(m, p, t),
12815 m_enmReason(enmReason),
12816 m_strStateFilePath(strStateFilePath)
12817 {}
12818
12819private:
12820 void handler()
12821 {
12822 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12823 }
12824
12825 Reason_T m_enmReason;
12826 Utf8Str m_strStateFilePath;
12827
12828 friend class SessionMachine;
12829};
12830
12831/**
12832 * Task thread implementation for SessionMachine::SaveState(), called from
12833 * SessionMachine::taskHandler().
12834 *
12835 * @note Locks this object for writing.
12836 *
12837 * @param task
12838 * @return
12839 */
12840void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12841{
12842 LogFlowThisFuncEnter();
12843
12844 AutoCaller autoCaller(this);
12845 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12846 if (FAILED(autoCaller.rc()))
12847 {
12848 /* we might have been uninitialized because the session was accidentally
12849 * closed by the client, so don't assert */
12850 HRESULT rc = setError(E_FAIL,
12851 tr("The session has been accidentally closed"));
12852 task.m_pProgress->i_notifyComplete(rc);
12853 LogFlowThisFuncLeave();
12854 return;
12855 }
12856
12857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12858
12859 HRESULT rc = S_OK;
12860
12861 try
12862 {
12863 ComPtr<IInternalSessionControl> directControl;
12864 if (mData->mSession.mLockType == LockType_VM)
12865 directControl = mData->mSession.mDirectControl;
12866 if (directControl.isNull())
12867 throw setError(VBOX_E_INVALID_VM_STATE,
12868 tr("Trying to save state without a running VM"));
12869 alock.release();
12870 BOOL fSuspendedBySave;
12871 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12872 Assert(!fSuspendedBySave);
12873 alock.acquire();
12874
12875 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12876 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12877 throw E_FAIL);
12878
12879 if (SUCCEEDED(rc))
12880 {
12881 mSSData->strStateFilePath = task.m_strStateFilePath;
12882
12883 /* save all VM settings */
12884 rc = i_saveSettings(NULL);
12885 // no need to check whether VirtualBox.xml needs saving also since
12886 // we can't have a name change pending at this point
12887 }
12888 else
12889 {
12890 // On failure, set the state to the state we had at the beginning.
12891 i_setMachineState(task.m_machineStateBackup);
12892 i_updateMachineStateOnClient();
12893
12894 // Delete the saved state file (might have been already created).
12895 // No need to check whether this is shared with a snapshot here
12896 // because we certainly created a fresh saved state file here.
12897 RTFileDelete(task.m_strStateFilePath.c_str());
12898 }
12899 }
12900 catch (HRESULT aRC) { rc = aRC; }
12901
12902 task.m_pProgress->i_notifyComplete(rc);
12903
12904 LogFlowThisFuncLeave();
12905}
12906
12907/**
12908 * @note Locks this object for writing.
12909 */
12910HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12911{
12912 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12913}
12914
12915HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12916{
12917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12918
12919 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12920 if (FAILED(rc)) return rc;
12921
12922 if ( mData->mMachineState != MachineState_Running
12923 && mData->mMachineState != MachineState_Paused
12924 )
12925 return setError(VBOX_E_INVALID_VM_STATE,
12926 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12927 Global::stringifyMachineState(mData->mMachineState));
12928
12929 ComObjPtr<Progress> pProgress;
12930 pProgress.createObject();
12931 rc = pProgress->init(i_getVirtualBox(),
12932 static_cast<IMachine *>(this) /* aInitiator */,
12933 tr("Saving the execution state of the virtual machine"),
12934 FALSE /* aCancelable */);
12935 if (FAILED(rc))
12936 return rc;
12937
12938 Utf8Str strStateFilePath;
12939 i_composeSavedStateFilename(strStateFilePath);
12940
12941 /* create and start the task on a separate thread (note that it will not
12942 * start working until we release alock) */
12943 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12944 rc = pTask->createThread();
12945 if (FAILED(rc))
12946 return rc;
12947
12948 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12949 i_setMachineState(MachineState_Saving);
12950 i_updateMachineStateOnClient();
12951
12952 pProgress.queryInterfaceTo(aProgress.asOutParam());
12953
12954 return S_OK;
12955}
12956
12957/**
12958 * @note Locks this object for writing.
12959 */
12960HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12961{
12962 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12963
12964 HRESULT rc = i_checkStateDependency(MutableStateDep);
12965 if (FAILED(rc)) return rc;
12966
12967 if ( mData->mMachineState != MachineState_PoweredOff
12968 && mData->mMachineState != MachineState_Teleported
12969 && mData->mMachineState != MachineState_Aborted
12970 )
12971 return setError(VBOX_E_INVALID_VM_STATE,
12972 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12973 Global::stringifyMachineState(mData->mMachineState));
12974
12975 com::Utf8Str stateFilePathFull;
12976 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12977 if (RT_FAILURE(vrc))
12978 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12979 tr("Invalid saved state file path '%s' (%Rrc)"),
12980 aSavedStateFile.c_str(),
12981 vrc);
12982
12983 mSSData->strStateFilePath = stateFilePathFull;
12984
12985 /* The below i_setMachineState() will detect the state transition and will
12986 * update the settings file */
12987
12988 return i_setMachineState(MachineState_Saved);
12989}
12990
12991/**
12992 * @note Locks this object for writing.
12993 */
12994HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12995{
12996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12997
12998 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12999 if (FAILED(rc)) return rc;
13000
13001 if (mData->mMachineState != MachineState_Saved)
13002 return setError(VBOX_E_INVALID_VM_STATE,
13003 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
13004 Global::stringifyMachineState(mData->mMachineState));
13005
13006 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13007
13008 /*
13009 * Saved -> PoweredOff transition will be detected in the SessionMachine
13010 * and properly handled.
13011 */
13012 rc = i_setMachineState(MachineState_PoweredOff);
13013 return rc;
13014}
13015
13016
13017/**
13018 * @note Locks the same as #i_setMachineState() does.
13019 */
13020HRESULT SessionMachine::updateState(MachineState_T aState)
13021{
13022 return i_setMachineState(aState);
13023}
13024
13025/**
13026 * @note Locks this object for writing.
13027 */
13028HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13029{
13030 IProgress *pProgress(aProgress);
13031
13032 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13033
13034 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13035
13036 if (mData->mSession.mState != SessionState_Locked)
13037 return VBOX_E_INVALID_OBJECT_STATE;
13038
13039 if (!mData->mSession.mProgress.isNull())
13040 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13041
13042 /* If we didn't reference the NAT network service yet, add a reference to
13043 * force a start */
13044 if (miNATNetworksStarted < 1)
13045 {
13046 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13047 {
13048 BOOL enabled;
13049 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13050 if ( FAILED(hrc)
13051 || !enabled)
13052 continue;
13053
13054 NetworkAttachmentType_T type;
13055 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13056 if ( SUCCEEDED(hrc)
13057 && type == NetworkAttachmentType_NATNetwork)
13058 {
13059 Bstr name;
13060 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13061 if (SUCCEEDED(hrc))
13062 {
13063 Utf8Str strName(name);
13064 LogRel(("VM '%s' starts using NAT network '%s'\n",
13065 mUserData->s.strName.c_str(), strName.c_str()));
13066 mPeer->lockHandle()->unlockWrite();
13067 mParent->i_natNetworkRefInc(strName);
13068#ifdef RT_LOCK_STRICT
13069 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13070#else
13071 mPeer->lockHandle()->lockWrite();
13072#endif
13073 }
13074 }
13075 }
13076 miNATNetworksStarted++;
13077 }
13078
13079 LogFlowThisFunc(("returns S_OK.\n"));
13080 return S_OK;
13081}
13082
13083/**
13084 * @note Locks this object for writing.
13085 */
13086HRESULT SessionMachine::endPowerUp(LONG aResult)
13087{
13088 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13089
13090 if (mData->mSession.mState != SessionState_Locked)
13091 return VBOX_E_INVALID_OBJECT_STATE;
13092
13093 /* Finalize the LaunchVMProcess progress object. */
13094 if (mData->mSession.mProgress)
13095 {
13096 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13097 mData->mSession.mProgress.setNull();
13098 }
13099
13100 if (SUCCEEDED((HRESULT)aResult))
13101 {
13102#ifdef VBOX_WITH_RESOURCE_USAGE_API
13103 /* The VM has been powered up successfully, so it makes sense
13104 * now to offer the performance metrics for a running machine
13105 * object. Doing it earlier wouldn't be safe. */
13106 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13107 mData->mSession.mPID);
13108#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13109 }
13110
13111 return S_OK;
13112}
13113
13114/**
13115 * @note Locks this object for writing.
13116 */
13117HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13118{
13119 LogFlowThisFuncEnter();
13120
13121 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13122
13123 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13124 E_FAIL);
13125
13126 /* create a progress object to track operation completion */
13127 ComObjPtr<Progress> pProgress;
13128 pProgress.createObject();
13129 pProgress->init(i_getVirtualBox(),
13130 static_cast<IMachine *>(this) /* aInitiator */,
13131 tr("Stopping the virtual machine"),
13132 FALSE /* aCancelable */);
13133
13134 /* fill in the console task data */
13135 mConsoleTaskData.mLastState = mData->mMachineState;
13136 mConsoleTaskData.mProgress = pProgress;
13137
13138 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13139 i_setMachineState(MachineState_Stopping);
13140
13141 pProgress.queryInterfaceTo(aProgress.asOutParam());
13142
13143 return S_OK;
13144}
13145
13146/**
13147 * @note Locks this object for writing.
13148 */
13149HRESULT SessionMachine::endPoweringDown(LONG aResult,
13150 const com::Utf8Str &aErrMsg)
13151{
13152 LogFlowThisFuncEnter();
13153
13154 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13155
13156 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13157 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13158 && mConsoleTaskData.mLastState != MachineState_Null,
13159 E_FAIL);
13160
13161 /*
13162 * On failure, set the state to the state we had when BeginPoweringDown()
13163 * was called (this is expected by Console::PowerDown() and the associated
13164 * task). On success the VM process already changed the state to
13165 * MachineState_PoweredOff, so no need to do anything.
13166 */
13167 if (FAILED(aResult))
13168 i_setMachineState(mConsoleTaskData.mLastState);
13169
13170 /* notify the progress object about operation completion */
13171 Assert(mConsoleTaskData.mProgress);
13172 if (SUCCEEDED(aResult))
13173 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13174 else
13175 {
13176 if (aErrMsg.length())
13177 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13178 COM_IIDOF(ISession),
13179 getComponentName(),
13180 aErrMsg.c_str());
13181 else
13182 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13183 }
13184
13185 /* clear out the temporary saved state data */
13186 mConsoleTaskData.mLastState = MachineState_Null;
13187 mConsoleTaskData.mProgress.setNull();
13188
13189 LogFlowThisFuncLeave();
13190 return S_OK;
13191}
13192
13193
13194/**
13195 * Goes through the USB filters of the given machine to see if the given
13196 * device matches any filter or not.
13197 *
13198 * @note Locks the same as USBController::hasMatchingFilter() does.
13199 */
13200HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13201 BOOL *aMatched,
13202 ULONG *aMaskedInterfaces)
13203{
13204 LogFlowThisFunc(("\n"));
13205
13206#ifdef VBOX_WITH_USB
13207 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13208#else
13209 NOREF(aDevice);
13210 NOREF(aMaskedInterfaces);
13211 *aMatched = FALSE;
13212#endif
13213
13214 return S_OK;
13215}
13216
13217/**
13218 * @note Locks the same as Host::captureUSBDevice() does.
13219 */
13220HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13221{
13222 LogFlowThisFunc(("\n"));
13223
13224#ifdef VBOX_WITH_USB
13225 /* if captureDeviceForVM() fails, it must have set extended error info */
13226 clearError();
13227 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13228 if (FAILED(rc)) return rc;
13229
13230 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13231 AssertReturn(service, E_FAIL);
13232 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13233#else
13234 NOREF(aId);
13235 return E_NOTIMPL;
13236#endif
13237}
13238
13239/**
13240 * @note Locks the same as Host::detachUSBDevice() does.
13241 */
13242HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13243 BOOL aDone)
13244{
13245 LogFlowThisFunc(("\n"));
13246
13247#ifdef VBOX_WITH_USB
13248 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13249 AssertReturn(service, E_FAIL);
13250 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13251#else
13252 NOREF(aId);
13253 NOREF(aDone);
13254 return E_NOTIMPL;
13255#endif
13256}
13257
13258/**
13259 * Inserts all machine filters to the USB proxy service and then calls
13260 * Host::autoCaptureUSBDevices().
13261 *
13262 * Called by Console from the VM process upon VM startup.
13263 *
13264 * @note Locks what called methods lock.
13265 */
13266HRESULT SessionMachine::autoCaptureUSBDevices()
13267{
13268 LogFlowThisFunc(("\n"));
13269
13270#ifdef VBOX_WITH_USB
13271 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13272 AssertComRC(rc);
13273 NOREF(rc);
13274
13275 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13276 AssertReturn(service, E_FAIL);
13277 return service->autoCaptureDevicesForVM(this);
13278#else
13279 return S_OK;
13280#endif
13281}
13282
13283/**
13284 * Removes all machine filters from the USB proxy service and then calls
13285 * Host::detachAllUSBDevices().
13286 *
13287 * Called by Console from the VM process upon normal VM termination or by
13288 * SessionMachine::uninit() upon abnormal VM termination (from under the
13289 * Machine/SessionMachine lock).
13290 *
13291 * @note Locks what called methods lock.
13292 */
13293HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13294{
13295 LogFlowThisFunc(("\n"));
13296
13297#ifdef VBOX_WITH_USB
13298 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13299 AssertComRC(rc);
13300 NOREF(rc);
13301
13302 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13303 AssertReturn(service, E_FAIL);
13304 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13305#else
13306 NOREF(aDone);
13307 return S_OK;
13308#endif
13309}
13310
13311/**
13312 * @note Locks this object for writing.
13313 */
13314HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13315 ComPtr<IProgress> &aProgress)
13316{
13317 LogFlowThisFuncEnter();
13318
13319 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13320 /*
13321 * We don't assert below because it might happen that a non-direct session
13322 * informs us it is closed right after we've been uninitialized -- it's ok.
13323 */
13324
13325 /* get IInternalSessionControl interface */
13326 ComPtr<IInternalSessionControl> control(aSession);
13327
13328 ComAssertRet(!control.isNull(), E_INVALIDARG);
13329
13330 /* Creating a Progress object requires the VirtualBox lock, and
13331 * thus locking it here is required by the lock order rules. */
13332 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13333
13334 if (control == mData->mSession.mDirectControl)
13335 {
13336 /* The direct session is being normally closed by the client process
13337 * ----------------------------------------------------------------- */
13338
13339 /* go to the closing state (essential for all open*Session() calls and
13340 * for #i_checkForDeath()) */
13341 Assert(mData->mSession.mState == SessionState_Locked);
13342 mData->mSession.mState = SessionState_Unlocking;
13343
13344 /* set direct control to NULL to release the remote instance */
13345 mData->mSession.mDirectControl.setNull();
13346 LogFlowThisFunc(("Direct control is set to NULL\n"));
13347
13348 if (mData->mSession.mProgress)
13349 {
13350 /* finalize the progress, someone might wait if a frontend
13351 * closes the session before powering on the VM. */
13352 mData->mSession.mProgress->notifyComplete(E_FAIL,
13353 COM_IIDOF(ISession),
13354 getComponentName(),
13355 tr("The VM session was closed before any attempt to power it on"));
13356 mData->mSession.mProgress.setNull();
13357 }
13358
13359 /* Create the progress object the client will use to wait until
13360 * #i_checkForDeath() is called to uninitialize this session object after
13361 * it releases the IPC semaphore.
13362 * Note! Because we're "reusing" mProgress here, this must be a proxy
13363 * object just like for LaunchVMProcess. */
13364 Assert(mData->mSession.mProgress.isNull());
13365 ComObjPtr<ProgressProxy> progress;
13366 progress.createObject();
13367 ComPtr<IUnknown> pPeer(mPeer);
13368 progress->init(mParent, pPeer,
13369 Bstr(tr("Closing session")).raw(),
13370 FALSE /* aCancelable */);
13371 progress.queryInterfaceTo(aProgress.asOutParam());
13372 mData->mSession.mProgress = progress;
13373 }
13374 else
13375 {
13376 /* the remote session is being normally closed */
13377 bool found = false;
13378 for (Data::Session::RemoteControlList::iterator
13379 it = mData->mSession.mRemoteControls.begin();
13380 it != mData->mSession.mRemoteControls.end();
13381 ++it)
13382 {
13383 if (control == *it)
13384 {
13385 found = true;
13386 // This MUST be erase(it), not remove(*it) as the latter
13387 // triggers a very nasty use after free due to the place where
13388 // the value "lives".
13389 mData->mSession.mRemoteControls.erase(it);
13390 break;
13391 }
13392 }
13393 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13394 E_INVALIDARG);
13395 }
13396
13397 /* signal the client watcher thread, because the client is going away */
13398 mParent->i_updateClientWatcher();
13399
13400 LogFlowThisFuncLeave();
13401 return S_OK;
13402}
13403
13404HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13405{
13406#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13407 ULONG uID;
13408 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13409 if (RT_SUCCESS(rc))
13410 {
13411 if (aID)
13412 *aID = uID;
13413 return S_OK;
13414 }
13415 return E_FAIL;
13416#else
13417 RT_NOREF(aParms, aID);
13418 ReturnComNotImplemented();
13419#endif
13420}
13421
13422HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13423{
13424#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13425 return mParent->i_onClipboardAreaUnregister(aID);
13426#else
13427 RT_NOREF(aID);
13428 ReturnComNotImplemented();
13429#endif
13430}
13431
13432HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13433{
13434#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13435 return mParent->i_onClipboardAreaAttach(aID);
13436#else
13437 RT_NOREF(aID);
13438 ReturnComNotImplemented();
13439#endif
13440}
13441HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13442{
13443#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13444 return mParent->i_onClipboardAreaDetach(aID);
13445#else
13446 RT_NOREF(aID);
13447 ReturnComNotImplemented();
13448#endif
13449}
13450
13451HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13452{
13453#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13454 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13455 if (aID)
13456 *aID = uID;
13457 return S_OK;
13458#else
13459 RT_NOREF(aID);
13460 ReturnComNotImplemented();
13461#endif
13462}
13463
13464HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13465{
13466#ifdef VBOX_WITH_SHARED_CLIPBOARD_TRANSFERS
13467 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13468 if (aRefCount)
13469 *aRefCount = uRefCount;
13470 return S_OK;
13471#else
13472 RT_NOREF(aID, aRefCount);
13473 ReturnComNotImplemented();
13474#endif
13475}
13476
13477HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13478 std::vector<com::Utf8Str> &aValues,
13479 std::vector<LONG64> &aTimestamps,
13480 std::vector<com::Utf8Str> &aFlags)
13481{
13482 LogFlowThisFunc(("\n"));
13483
13484#ifdef VBOX_WITH_GUEST_PROPS
13485 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13486
13487 size_t cEntries = mHWData->mGuestProperties.size();
13488 aNames.resize(cEntries);
13489 aValues.resize(cEntries);
13490 aTimestamps.resize(cEntries);
13491 aFlags.resize(cEntries);
13492
13493 size_t i = 0;
13494 for (HWData::GuestPropertyMap::const_iterator
13495 it = mHWData->mGuestProperties.begin();
13496 it != mHWData->mGuestProperties.end();
13497 ++it, ++i)
13498 {
13499 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13500 aNames[i] = it->first;
13501 aValues[i] = it->second.strValue;
13502 aTimestamps[i] = it->second.mTimestamp;
13503
13504 /* If it is NULL, keep it NULL. */
13505 if (it->second.mFlags)
13506 {
13507 GuestPropWriteFlags(it->second.mFlags, szFlags);
13508 aFlags[i] = szFlags;
13509 }
13510 else
13511 aFlags[i] = "";
13512 }
13513 return S_OK;
13514#else
13515 ReturnComNotImplemented();
13516#endif
13517}
13518
13519HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13520 const com::Utf8Str &aValue,
13521 LONG64 aTimestamp,
13522 const com::Utf8Str &aFlags)
13523{
13524 LogFlowThisFunc(("\n"));
13525
13526#ifdef VBOX_WITH_GUEST_PROPS
13527 try
13528 {
13529 /*
13530 * Convert input up front.
13531 */
13532 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13533 if (aFlags.length())
13534 {
13535 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13536 AssertRCReturn(vrc, E_INVALIDARG);
13537 }
13538
13539 /*
13540 * Now grab the object lock, validate the state and do the update.
13541 */
13542
13543 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13544
13545 if (!Global::IsOnline(mData->mMachineState))
13546 {
13547 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13548 VBOX_E_INVALID_VM_STATE);
13549 }
13550
13551 i_setModified(IsModified_MachineData);
13552 mHWData.backup();
13553
13554 bool fDelete = !aValue.length();
13555 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13556 if (it != mHWData->mGuestProperties.end())
13557 {
13558 if (!fDelete)
13559 {
13560 it->second.strValue = aValue;
13561 it->second.mTimestamp = aTimestamp;
13562 it->second.mFlags = fFlags;
13563 }
13564 else
13565 mHWData->mGuestProperties.erase(it);
13566
13567 mData->mGuestPropertiesModified = TRUE;
13568 }
13569 else if (!fDelete)
13570 {
13571 HWData::GuestProperty prop;
13572 prop.strValue = aValue;
13573 prop.mTimestamp = aTimestamp;
13574 prop.mFlags = fFlags;
13575
13576 mHWData->mGuestProperties[aName] = prop;
13577 mData->mGuestPropertiesModified = TRUE;
13578 }
13579
13580 alock.release();
13581
13582 mParent->i_onGuestPropertyChange(mData->mUuid,
13583 Bstr(aName).raw(),
13584 Bstr(aValue).raw(),
13585 Bstr(aFlags).raw());
13586 }
13587 catch (...)
13588 {
13589 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13590 }
13591 return S_OK;
13592#else
13593 ReturnComNotImplemented();
13594#endif
13595}
13596
13597
13598HRESULT SessionMachine::lockMedia()
13599{
13600 AutoMultiWriteLock2 alock(this->lockHandle(),
13601 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13602
13603 AssertReturn( mData->mMachineState == MachineState_Starting
13604 || mData->mMachineState == MachineState_Restoring
13605 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13606
13607 clearError();
13608 alock.release();
13609 return i_lockMedia();
13610}
13611
13612HRESULT SessionMachine::unlockMedia()
13613{
13614 HRESULT hrc = i_unlockMedia();
13615 return hrc;
13616}
13617
13618HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13619 ComPtr<IMediumAttachment> &aNewAttachment)
13620{
13621 // request the host lock first, since might be calling Host methods for getting host drives;
13622 // next, protect the media tree all the while we're in here, as well as our member variables
13623 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13624 this->lockHandle(),
13625 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13626
13627 IMediumAttachment *iAttach = aAttachment;
13628 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13629
13630 Utf8Str ctrlName;
13631 LONG lPort;
13632 LONG lDevice;
13633 bool fTempEject;
13634 {
13635 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13636
13637 /* Need to query the details first, as the IMediumAttachment reference
13638 * might be to the original settings, which we are going to change. */
13639 ctrlName = pAttach->i_getControllerName();
13640 lPort = pAttach->i_getPort();
13641 lDevice = pAttach->i_getDevice();
13642 fTempEject = pAttach->i_getTempEject();
13643 }
13644
13645 if (!fTempEject)
13646 {
13647 /* Remember previously mounted medium. The medium before taking the
13648 * backup is not necessarily the same thing. */
13649 ComObjPtr<Medium> oldmedium;
13650 oldmedium = pAttach->i_getMedium();
13651
13652 i_setModified(IsModified_Storage);
13653 mMediumAttachments.backup();
13654
13655 // The backup operation makes the pAttach reference point to the
13656 // old settings. Re-get the correct reference.
13657 pAttach = i_findAttachment(*mMediumAttachments.data(),
13658 ctrlName,
13659 lPort,
13660 lDevice);
13661
13662 {
13663 AutoCaller autoAttachCaller(this);
13664 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13665
13666 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13667 if (!oldmedium.isNull())
13668 oldmedium->i_removeBackReference(mData->mUuid);
13669
13670 pAttach->i_updateMedium(NULL);
13671 pAttach->i_updateEjected();
13672 }
13673
13674 i_setModified(IsModified_Storage);
13675 }
13676 else
13677 {
13678 {
13679 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13680 pAttach->i_updateEjected();
13681 }
13682 }
13683
13684 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13685
13686 return S_OK;
13687}
13688
13689HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13690 com::Utf8Str &aResult)
13691{
13692 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13693
13694 HRESULT hr = S_OK;
13695
13696 if (!mAuthLibCtx.hAuthLibrary)
13697 {
13698 /* Load the external authentication library. */
13699 Bstr authLibrary;
13700 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13701
13702 Utf8Str filename = authLibrary;
13703
13704 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13705 if (RT_FAILURE(vrc))
13706 hr = setErrorBoth(E_FAIL, vrc,
13707 tr("Could not load the external authentication library '%s' (%Rrc)"),
13708 filename.c_str(), vrc);
13709 }
13710
13711 /* The auth library might need the machine lock. */
13712 alock.release();
13713
13714 if (FAILED(hr))
13715 return hr;
13716
13717 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13718 {
13719 enum VRDEAuthParams
13720 {
13721 parmUuid = 1,
13722 parmGuestJudgement,
13723 parmUser,
13724 parmPassword,
13725 parmDomain,
13726 parmClientId
13727 };
13728
13729 AuthResult result = AuthResultAccessDenied;
13730
13731 Guid uuid(aAuthParams[parmUuid]);
13732 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13733 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13734
13735 result = AuthLibAuthenticate(&mAuthLibCtx,
13736 uuid.raw(), guestJudgement,
13737 aAuthParams[parmUser].c_str(),
13738 aAuthParams[parmPassword].c_str(),
13739 aAuthParams[parmDomain].c_str(),
13740 u32ClientId);
13741
13742 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13743 size_t cbPassword = aAuthParams[parmPassword].length();
13744 if (cbPassword)
13745 {
13746 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13747 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13748 }
13749
13750 if (result == AuthResultAccessGranted)
13751 aResult = "granted";
13752 else
13753 aResult = "denied";
13754
13755 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13756 aAuthParams[parmUser].c_str(), aResult.c_str()));
13757 }
13758 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13759 {
13760 enum VRDEAuthDisconnectParams
13761 {
13762 parmUuid = 1,
13763 parmClientId
13764 };
13765
13766 Guid uuid(aAuthParams[parmUuid]);
13767 uint32_t u32ClientId = 0;
13768 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13769 }
13770 else
13771 {
13772 hr = E_INVALIDARG;
13773 }
13774
13775 return hr;
13776}
13777
13778// public methods only for internal purposes
13779/////////////////////////////////////////////////////////////////////////////
13780
13781#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13782/**
13783 * Called from the client watcher thread to check for expected or unexpected
13784 * death of the client process that has a direct session to this machine.
13785 *
13786 * On Win32 and on OS/2, this method is called only when we've got the
13787 * mutex (i.e. the client has either died or terminated normally) so it always
13788 * returns @c true (the client is terminated, the session machine is
13789 * uninitialized).
13790 *
13791 * On other platforms, the method returns @c true if the client process has
13792 * terminated normally or abnormally and the session machine was uninitialized,
13793 * and @c false if the client process is still alive.
13794 *
13795 * @note Locks this object for writing.
13796 */
13797bool SessionMachine::i_checkForDeath()
13798{
13799 Uninit::Reason reason;
13800 bool terminated = false;
13801
13802 /* Enclose autoCaller with a block because calling uninit() from under it
13803 * will deadlock. */
13804 {
13805 AutoCaller autoCaller(this);
13806 if (!autoCaller.isOk())
13807 {
13808 /* return true if not ready, to cause the client watcher to exclude
13809 * the corresponding session from watching */
13810 LogFlowThisFunc(("Already uninitialized!\n"));
13811 return true;
13812 }
13813
13814 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13815
13816 /* Determine the reason of death: if the session state is Closing here,
13817 * everything is fine. Otherwise it means that the client did not call
13818 * OnSessionEnd() before it released the IPC semaphore. This may happen
13819 * either because the client process has abnormally terminated, or
13820 * because it simply forgot to call ISession::Close() before exiting. We
13821 * threat the latter also as an abnormal termination (see
13822 * Session::uninit() for details). */
13823 reason = mData->mSession.mState == SessionState_Unlocking ?
13824 Uninit::Normal :
13825 Uninit::Abnormal;
13826
13827 if (mClientToken)
13828 terminated = mClientToken->release();
13829 } /* AutoCaller block */
13830
13831 if (terminated)
13832 uninit(reason);
13833
13834 return terminated;
13835}
13836
13837void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13838{
13839 LogFlowThisFunc(("\n"));
13840
13841 strTokenId.setNull();
13842
13843 AutoCaller autoCaller(this);
13844 AssertComRCReturnVoid(autoCaller.rc());
13845
13846 Assert(mClientToken);
13847 if (mClientToken)
13848 mClientToken->getId(strTokenId);
13849}
13850#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13851IToken *SessionMachine::i_getToken()
13852{
13853 LogFlowThisFunc(("\n"));
13854
13855 AutoCaller autoCaller(this);
13856 AssertComRCReturn(autoCaller.rc(), NULL);
13857
13858 Assert(mClientToken);
13859 if (mClientToken)
13860 return mClientToken->getToken();
13861 else
13862 return NULL;
13863}
13864#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13865
13866Machine::ClientToken *SessionMachine::i_getClientToken()
13867{
13868 LogFlowThisFunc(("\n"));
13869
13870 AutoCaller autoCaller(this);
13871 AssertComRCReturn(autoCaller.rc(), NULL);
13872
13873 return mClientToken;
13874}
13875
13876
13877/**
13878 * @note Locks this object for reading.
13879 */
13880HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13881{
13882 LogFlowThisFunc(("\n"));
13883
13884 AutoCaller autoCaller(this);
13885 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13886
13887 ComPtr<IInternalSessionControl> directControl;
13888 {
13889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13890 if (mData->mSession.mLockType == LockType_VM)
13891 directControl = mData->mSession.mDirectControl;
13892 }
13893
13894 /* ignore notifications sent after #OnSessionEnd() is called */
13895 if (!directControl)
13896 return S_OK;
13897
13898 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13899}
13900
13901/**
13902 * @note Locks this object for reading.
13903 */
13904HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13905 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13906 IN_BSTR aGuestIp, LONG aGuestPort)
13907{
13908 LogFlowThisFunc(("\n"));
13909
13910 AutoCaller autoCaller(this);
13911 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13912
13913 ComPtr<IInternalSessionControl> directControl;
13914 {
13915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13916 if (mData->mSession.mLockType == LockType_VM)
13917 directControl = mData->mSession.mDirectControl;
13918 }
13919
13920 /* ignore notifications sent after #OnSessionEnd() is called */
13921 if (!directControl)
13922 return S_OK;
13923 /*
13924 * instead acting like callback we ask IVirtualBox deliver corresponding event
13925 */
13926
13927 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13928 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13929 return S_OK;
13930}
13931
13932/**
13933 * @note Locks this object for reading.
13934 */
13935HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13936{
13937 LogFlowThisFunc(("\n"));
13938
13939 AutoCaller autoCaller(this);
13940 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13941
13942 ComPtr<IInternalSessionControl> directControl;
13943 {
13944 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13945 if (mData->mSession.mLockType == LockType_VM)
13946 directControl = mData->mSession.mDirectControl;
13947 }
13948
13949 /* ignore notifications sent after #OnSessionEnd() is called */
13950 if (!directControl)
13951 return S_OK;
13952
13953 return directControl->OnAudioAdapterChange(audioAdapter);
13954}
13955
13956/**
13957 * @note Locks this object for reading.
13958 */
13959HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13960{
13961 LogFlowThisFunc(("\n"));
13962
13963 AutoCaller autoCaller(this);
13964 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13965
13966 ComPtr<IInternalSessionControl> directControl;
13967 {
13968 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13969 if (mData->mSession.mLockType == LockType_VM)
13970 directControl = mData->mSession.mDirectControl;
13971 }
13972
13973 /* ignore notifications sent after #OnSessionEnd() is called */
13974 if (!directControl)
13975 return S_OK;
13976
13977 return directControl->OnSerialPortChange(serialPort);
13978}
13979
13980/**
13981 * @note Locks this object for reading.
13982 */
13983HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13984{
13985 LogFlowThisFunc(("\n"));
13986
13987 AutoCaller autoCaller(this);
13988 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13989
13990 ComPtr<IInternalSessionControl> directControl;
13991 {
13992 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13993 if (mData->mSession.mLockType == LockType_VM)
13994 directControl = mData->mSession.mDirectControl;
13995 }
13996
13997 /* ignore notifications sent after #OnSessionEnd() is called */
13998 if (!directControl)
13999 return S_OK;
14000
14001 return directControl->OnParallelPortChange(parallelPort);
14002}
14003
14004/**
14005 * @note Locks this object for reading.
14006 */
14007HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
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 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14022
14023 /* ignore notifications sent after #OnSessionEnd() is called */
14024 if (!directControl)
14025 return S_OK;
14026
14027 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14028}
14029
14030/**
14031 * @note Locks this object for reading.
14032 */
14033HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14034{
14035 LogFlowThisFunc(("\n"));
14036
14037 AutoCaller autoCaller(this);
14038 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14039
14040 ComPtr<IInternalSessionControl> directControl;
14041 {
14042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14043 if (mData->mSession.mLockType == LockType_VM)
14044 directControl = mData->mSession.mDirectControl;
14045 }
14046
14047 mParent->i_onMediumChanged(aAttachment);
14048
14049 /* ignore notifications sent after #OnSessionEnd() is called */
14050 if (!directControl)
14051 return S_OK;
14052
14053 return directControl->OnMediumChange(aAttachment, aForce);
14054}
14055
14056HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14057{
14058 LogFlowThisFunc(("\n"));
14059
14060 AutoCaller autoCaller(this);
14061 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14062
14063 ComPtr<IInternalSessionControl> directControl;
14064 {
14065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14066 if (mData->mSession.mLockType == LockType_VM)
14067 directControl = mData->mSession.mDirectControl;
14068 }
14069
14070 /* ignore notifications sent after #OnSessionEnd() is called */
14071 if (!directControl)
14072 return S_OK;
14073
14074 return directControl->OnVMProcessPriorityChange(aPriority);
14075}
14076
14077/**
14078 * @note Locks this object for reading.
14079 */
14080HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14081{
14082 LogFlowThisFunc(("\n"));
14083
14084 AutoCaller autoCaller(this);
14085 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14086
14087 ComPtr<IInternalSessionControl> directControl;
14088 {
14089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14090 if (mData->mSession.mLockType == LockType_VM)
14091 directControl = mData->mSession.mDirectControl;
14092 }
14093
14094 /* ignore notifications sent after #OnSessionEnd() is called */
14095 if (!directControl)
14096 return S_OK;
14097
14098 return directControl->OnCPUChange(aCPU, aRemove);
14099}
14100
14101HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14107
14108 ComPtr<IInternalSessionControl> directControl;
14109 {
14110 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14111 if (mData->mSession.mLockType == LockType_VM)
14112 directControl = mData->mSession.mDirectControl;
14113 }
14114
14115 /* ignore notifications sent after #OnSessionEnd() is called */
14116 if (!directControl)
14117 return S_OK;
14118
14119 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14120}
14121
14122/**
14123 * @note Locks this object for reading.
14124 */
14125HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14126{
14127 LogFlowThisFunc(("\n"));
14128
14129 AutoCaller autoCaller(this);
14130 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14131
14132 ComPtr<IInternalSessionControl> directControl;
14133 {
14134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14135 if (mData->mSession.mLockType == LockType_VM)
14136 directControl = mData->mSession.mDirectControl;
14137 }
14138
14139 /* ignore notifications sent after #OnSessionEnd() is called */
14140 if (!directControl)
14141 return S_OK;
14142
14143 return directControl->OnVRDEServerChange(aRestart);
14144}
14145
14146/**
14147 * @note Locks this object for reading.
14148 */
14149HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14150{
14151 LogFlowThisFunc(("\n"));
14152
14153 AutoCaller autoCaller(this);
14154 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14155
14156 ComPtr<IInternalSessionControl> directControl;
14157 {
14158 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14159 if (mData->mSession.mLockType == LockType_VM)
14160 directControl = mData->mSession.mDirectControl;
14161 }
14162
14163 /* ignore notifications sent after #OnSessionEnd() is called */
14164 if (!directControl)
14165 return S_OK;
14166
14167 return directControl->OnRecordingChange(aEnable);
14168}
14169
14170/**
14171 * @note Locks this object for reading.
14172 */
14173HRESULT SessionMachine::i_onUSBControllerChange()
14174{
14175 LogFlowThisFunc(("\n"));
14176
14177 AutoCaller autoCaller(this);
14178 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14179
14180 ComPtr<IInternalSessionControl> directControl;
14181 {
14182 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14183 if (mData->mSession.mLockType == LockType_VM)
14184 directControl = mData->mSession.mDirectControl;
14185 }
14186
14187 /* ignore notifications sent after #OnSessionEnd() is called */
14188 if (!directControl)
14189 return S_OK;
14190
14191 return directControl->OnUSBControllerChange();
14192}
14193
14194/**
14195 * @note Locks this object for reading.
14196 */
14197HRESULT SessionMachine::i_onSharedFolderChange()
14198{
14199 LogFlowThisFunc(("\n"));
14200
14201 AutoCaller autoCaller(this);
14202 AssertComRCReturnRC(autoCaller.rc());
14203
14204 ComPtr<IInternalSessionControl> directControl;
14205 {
14206 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14207 if (mData->mSession.mLockType == LockType_VM)
14208 directControl = mData->mSession.mDirectControl;
14209 }
14210
14211 /* ignore notifications sent after #OnSessionEnd() is called */
14212 if (!directControl)
14213 return S_OK;
14214
14215 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14216}
14217
14218/**
14219 * @note Locks this object for reading.
14220 */
14221HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14222{
14223 LogFlowThisFunc(("\n"));
14224
14225 AutoCaller autoCaller(this);
14226 AssertComRCReturnRC(autoCaller.rc());
14227
14228 ComPtr<IInternalSessionControl> directControl;
14229 {
14230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14231 if (mData->mSession.mLockType == LockType_VM)
14232 directControl = mData->mSession.mDirectControl;
14233 }
14234
14235 /* ignore notifications sent after #OnSessionEnd() is called */
14236 if (!directControl)
14237 return S_OK;
14238
14239 return directControl->OnClipboardModeChange(aClipboardMode);
14240}
14241
14242/**
14243 * @note Locks this object for reading.
14244 */
14245HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14246{
14247 LogFlowThisFunc(("\n"));
14248
14249 AutoCaller autoCaller(this);
14250 AssertComRCReturnRC(autoCaller.rc());
14251
14252 ComPtr<IInternalSessionControl> directControl;
14253 {
14254 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14255 if (mData->mSession.mLockType == LockType_VM)
14256 directControl = mData->mSession.mDirectControl;
14257 }
14258
14259 /* ignore notifications sent after #OnSessionEnd() is called */
14260 if (!directControl)
14261 return S_OK;
14262
14263 return directControl->OnDnDModeChange(aDnDMode);
14264}
14265
14266/**
14267 * @note Locks this object for reading.
14268 */
14269HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14270{
14271 LogFlowThisFunc(("\n"));
14272
14273 AutoCaller autoCaller(this);
14274 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14275
14276 ComPtr<IInternalSessionControl> directControl;
14277 {
14278 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14279 if (mData->mSession.mLockType == LockType_VM)
14280 directControl = mData->mSession.mDirectControl;
14281 }
14282
14283 /* ignore notifications sent after #OnSessionEnd() is called */
14284 if (!directControl)
14285 return S_OK;
14286
14287 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14288}
14289
14290/**
14291 * @note Locks this object for reading.
14292 */
14293HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14294{
14295 LogFlowThisFunc(("\n"));
14296
14297 AutoCaller autoCaller(this);
14298 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14299
14300 ComPtr<IInternalSessionControl> directControl;
14301 {
14302 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14303 if (mData->mSession.mLockType == LockType_VM)
14304 directControl = mData->mSession.mDirectControl;
14305 }
14306
14307 /* ignore notifications sent after #OnSessionEnd() is called */
14308 if (!directControl)
14309 return S_OK;
14310
14311 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14312}
14313
14314/**
14315 * Returns @c true if this machine's USB controller reports it has a matching
14316 * filter for the given USB device and @c false otherwise.
14317 *
14318 * @note locks this object for reading.
14319 */
14320bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14321{
14322 AutoCaller autoCaller(this);
14323 /* silently return if not ready -- this method may be called after the
14324 * direct machine session has been called */
14325 if (!autoCaller.isOk())
14326 return false;
14327
14328#ifdef VBOX_WITH_USB
14329 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14330
14331 switch (mData->mMachineState)
14332 {
14333 case MachineState_Starting:
14334 case MachineState_Restoring:
14335 case MachineState_TeleportingIn:
14336 case MachineState_Paused:
14337 case MachineState_Running:
14338 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14339 * elsewhere... */
14340 alock.release();
14341 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14342 default: break;
14343 }
14344#else
14345 NOREF(aDevice);
14346 NOREF(aMaskedIfs);
14347#endif
14348 return false;
14349}
14350
14351/**
14352 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14353 */
14354HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14355 IVirtualBoxErrorInfo *aError,
14356 ULONG aMaskedIfs,
14357 const com::Utf8Str &aCaptureFilename)
14358{
14359 LogFlowThisFunc(("\n"));
14360
14361 AutoCaller autoCaller(this);
14362
14363 /* This notification may happen after the machine object has been
14364 * uninitialized (the session was closed), so don't assert. */
14365 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14366
14367 ComPtr<IInternalSessionControl> directControl;
14368 {
14369 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14370 if (mData->mSession.mLockType == LockType_VM)
14371 directControl = mData->mSession.mDirectControl;
14372 }
14373
14374 /* fail on notifications sent after #OnSessionEnd() is called, it is
14375 * expected by the caller */
14376 if (!directControl)
14377 return E_FAIL;
14378
14379 /* No locks should be held at this point. */
14380 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14381 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14382
14383 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14384}
14385
14386/**
14387 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14388 */
14389HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14390 IVirtualBoxErrorInfo *aError)
14391{
14392 LogFlowThisFunc(("\n"));
14393
14394 AutoCaller autoCaller(this);
14395
14396 /* This notification may happen after the machine object has been
14397 * uninitialized (the session was closed), so don't assert. */
14398 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14399
14400 ComPtr<IInternalSessionControl> directControl;
14401 {
14402 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14403 if (mData->mSession.mLockType == LockType_VM)
14404 directControl = mData->mSession.mDirectControl;
14405 }
14406
14407 /* fail on notifications sent after #OnSessionEnd() is called, it is
14408 * expected by the caller */
14409 if (!directControl)
14410 return E_FAIL;
14411
14412 /* No locks should be held at this point. */
14413 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14414 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14415
14416 return directControl->OnUSBDeviceDetach(aId, aError);
14417}
14418
14419// protected methods
14420/////////////////////////////////////////////////////////////////////////////
14421
14422/**
14423 * Deletes the given file if it is no longer in use by either the current machine state
14424 * (if the machine is "saved") or any of the machine's snapshots.
14425 *
14426 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14427 * but is different for each SnapshotMachine. When calling this, the order of calling this
14428 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14429 * is therefore critical. I know, it's all rather messy.
14430 *
14431 * @param strStateFile
14432 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14433 * the test for whether the saved state file is in use.
14434 */
14435void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14436 Snapshot *pSnapshotToIgnore)
14437{
14438 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14439 if ( (strStateFile.isNotEmpty())
14440 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14441 )
14442 // ... and it must also not be shared with other snapshots
14443 if ( !mData->mFirstSnapshot
14444 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14445 // this checks the SnapshotMachine's state file paths
14446 )
14447 RTFileDelete(strStateFile.c_str());
14448}
14449
14450/**
14451 * Locks the attached media.
14452 *
14453 * All attached hard disks are locked for writing and DVD/floppy are locked for
14454 * reading. Parents of attached hard disks (if any) are locked for reading.
14455 *
14456 * This method also performs accessibility check of all media it locks: if some
14457 * media is inaccessible, the method will return a failure and a bunch of
14458 * extended error info objects per each inaccessible medium.
14459 *
14460 * Note that this method is atomic: if it returns a success, all media are
14461 * locked as described above; on failure no media is locked at all (all
14462 * succeeded individual locks will be undone).
14463 *
14464 * The caller is responsible for doing the necessary state sanity checks.
14465 *
14466 * The locks made by this method must be undone by calling #unlockMedia() when
14467 * no more needed.
14468 */
14469HRESULT SessionMachine::i_lockMedia()
14470{
14471 AutoCaller autoCaller(this);
14472 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14473
14474 AutoMultiWriteLock2 alock(this->lockHandle(),
14475 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14476
14477 /* bail out if trying to lock things with already set up locking */
14478 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14479
14480 MultiResult mrc(S_OK);
14481
14482 /* Collect locking information for all medium objects attached to the VM. */
14483 for (MediumAttachmentList::const_iterator
14484 it = mMediumAttachments->begin();
14485 it != mMediumAttachments->end();
14486 ++it)
14487 {
14488 MediumAttachment *pAtt = *it;
14489 DeviceType_T devType = pAtt->i_getType();
14490 Medium *pMedium = pAtt->i_getMedium();
14491
14492 MediumLockList *pMediumLockList(new MediumLockList());
14493 // There can be attachments without a medium (floppy/dvd), and thus
14494 // it's impossible to create a medium lock list. It still makes sense
14495 // to have the empty medium lock list in the map in case a medium is
14496 // attached later.
14497 if (pMedium != NULL)
14498 {
14499 MediumType_T mediumType = pMedium->i_getType();
14500 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14501 || mediumType == MediumType_Shareable;
14502 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14503
14504 alock.release();
14505 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14506 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14507 false /* fMediumLockWriteAll */,
14508 NULL,
14509 *pMediumLockList);
14510 alock.acquire();
14511 if (FAILED(mrc))
14512 {
14513 delete pMediumLockList;
14514 mData->mSession.mLockedMedia.Clear();
14515 break;
14516 }
14517 }
14518
14519 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14520 if (FAILED(rc))
14521 {
14522 mData->mSession.mLockedMedia.Clear();
14523 mrc = setError(rc,
14524 tr("Collecting locking information for all attached media failed"));
14525 break;
14526 }
14527 }
14528
14529 if (SUCCEEDED(mrc))
14530 {
14531 /* Now lock all media. If this fails, nothing is locked. */
14532 alock.release();
14533 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14534 alock.acquire();
14535 if (FAILED(rc))
14536 {
14537 mrc = setError(rc,
14538 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14539 }
14540 }
14541
14542 return mrc;
14543}
14544
14545/**
14546 * Undoes the locks made by by #lockMedia().
14547 */
14548HRESULT SessionMachine::i_unlockMedia()
14549{
14550 AutoCaller autoCaller(this);
14551 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14552
14553 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14554
14555 /* we may be holding important error info on the current thread;
14556 * preserve it */
14557 ErrorInfoKeeper eik;
14558
14559 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14560 AssertComRC(rc);
14561 return rc;
14562}
14563
14564/**
14565 * Helper to change the machine state (reimplementation).
14566 *
14567 * @note Locks this object for writing.
14568 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14569 * it can cause crashes in random places due to unexpectedly committing
14570 * the current settings. The caller is responsible for that. The call
14571 * to saveStateSettings is fine, because this method does not commit.
14572 */
14573HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14574{
14575 LogFlowThisFuncEnter();
14576 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14577
14578 AutoCaller autoCaller(this);
14579 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14580
14581 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14582
14583 MachineState_T oldMachineState = mData->mMachineState;
14584
14585 AssertMsgReturn(oldMachineState != aMachineState,
14586 ("oldMachineState=%s, aMachineState=%s\n",
14587 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14588 E_FAIL);
14589
14590 HRESULT rc = S_OK;
14591
14592 int stsFlags = 0;
14593 bool deleteSavedState = false;
14594
14595 /* detect some state transitions */
14596
14597 if ( ( oldMachineState == MachineState_Saved
14598 && aMachineState == MachineState_Restoring)
14599 || ( ( oldMachineState == MachineState_PoweredOff
14600 || oldMachineState == MachineState_Teleported
14601 || oldMachineState == MachineState_Aborted
14602 )
14603 && ( aMachineState == MachineState_TeleportingIn
14604 || aMachineState == MachineState_Starting
14605 )
14606 )
14607 )
14608 {
14609 /* The EMT thread is about to start */
14610
14611 /* Nothing to do here for now... */
14612
14613 /// @todo NEWMEDIA don't let mDVDDrive and other children
14614 /// change anything when in the Starting/Restoring state
14615 }
14616 else if ( ( oldMachineState == MachineState_Running
14617 || oldMachineState == MachineState_Paused
14618 || oldMachineState == MachineState_Teleporting
14619 || oldMachineState == MachineState_OnlineSnapshotting
14620 || oldMachineState == MachineState_LiveSnapshotting
14621 || oldMachineState == MachineState_Stuck
14622 || oldMachineState == MachineState_Starting
14623 || oldMachineState == MachineState_Stopping
14624 || oldMachineState == MachineState_Saving
14625 || oldMachineState == MachineState_Restoring
14626 || oldMachineState == MachineState_TeleportingPausedVM
14627 || oldMachineState == MachineState_TeleportingIn
14628 )
14629 && ( aMachineState == MachineState_PoweredOff
14630 || aMachineState == MachineState_Saved
14631 || aMachineState == MachineState_Teleported
14632 || aMachineState == MachineState_Aborted
14633 )
14634 )
14635 {
14636 /* The EMT thread has just stopped, unlock attached media. Note that as
14637 * opposed to locking that is done from Console, we do unlocking here
14638 * because the VM process may have aborted before having a chance to
14639 * properly unlock all media it locked. */
14640
14641 unlockMedia();
14642 }
14643
14644 if (oldMachineState == MachineState_Restoring)
14645 {
14646 if (aMachineState != MachineState_Saved)
14647 {
14648 /*
14649 * delete the saved state file once the machine has finished
14650 * restoring from it (note that Console sets the state from
14651 * Restoring to Saved if the VM couldn't restore successfully,
14652 * to give the user an ability to fix an error and retry --
14653 * we keep the saved state file in this case)
14654 */
14655 deleteSavedState = true;
14656 }
14657 }
14658 else if ( oldMachineState == MachineState_Saved
14659 && ( aMachineState == MachineState_PoweredOff
14660 || aMachineState == MachineState_Aborted
14661 || aMachineState == MachineState_Teleported
14662 )
14663 )
14664 {
14665 /*
14666 * delete the saved state after SessionMachine::ForgetSavedState() is called
14667 * or if the VM process (owning a direct VM session) crashed while the
14668 * VM was Saved
14669 */
14670
14671 /// @todo (dmik)
14672 // Not sure that deleting the saved state file just because of the
14673 // client death before it attempted to restore the VM is a good
14674 // thing. But when it crashes we need to go to the Aborted state
14675 // which cannot have the saved state file associated... The only
14676 // way to fix this is to make the Aborted condition not a VM state
14677 // but a bool flag: i.e., when a crash occurs, set it to true and
14678 // change the state to PoweredOff or Saved depending on the
14679 // saved state presence.
14680
14681 deleteSavedState = true;
14682 mData->mCurrentStateModified = TRUE;
14683 stsFlags |= SaveSTS_CurStateModified;
14684 }
14685
14686 if ( aMachineState == MachineState_Starting
14687 || aMachineState == MachineState_Restoring
14688 || aMachineState == MachineState_TeleportingIn
14689 )
14690 {
14691 /* set the current state modified flag to indicate that the current
14692 * state is no more identical to the state in the
14693 * current snapshot */
14694 if (!mData->mCurrentSnapshot.isNull())
14695 {
14696 mData->mCurrentStateModified = TRUE;
14697 stsFlags |= SaveSTS_CurStateModified;
14698 }
14699 }
14700
14701 if (deleteSavedState)
14702 {
14703 if (mRemoveSavedState)
14704 {
14705 Assert(!mSSData->strStateFilePath.isEmpty());
14706
14707 // it is safe to delete the saved state file if ...
14708 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14709 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14710 // ... none of the snapshots share the saved state file
14711 )
14712 RTFileDelete(mSSData->strStateFilePath.c_str());
14713 }
14714
14715 mSSData->strStateFilePath.setNull();
14716 stsFlags |= SaveSTS_StateFilePath;
14717 }
14718
14719 /* redirect to the underlying peer machine */
14720 mPeer->i_setMachineState(aMachineState);
14721
14722 if ( oldMachineState != MachineState_RestoringSnapshot
14723 && ( aMachineState == MachineState_PoweredOff
14724 || aMachineState == MachineState_Teleported
14725 || aMachineState == MachineState_Aborted
14726 || aMachineState == MachineState_Saved))
14727 {
14728 /* the machine has stopped execution
14729 * (or the saved state file was adopted) */
14730 stsFlags |= SaveSTS_StateTimeStamp;
14731 }
14732
14733 if ( ( oldMachineState == MachineState_PoweredOff
14734 || oldMachineState == MachineState_Aborted
14735 || oldMachineState == MachineState_Teleported
14736 )
14737 && aMachineState == MachineState_Saved)
14738 {
14739 /* the saved state file was adopted */
14740 Assert(!mSSData->strStateFilePath.isEmpty());
14741 stsFlags |= SaveSTS_StateFilePath;
14742 }
14743
14744#ifdef VBOX_WITH_GUEST_PROPS
14745 if ( aMachineState == MachineState_PoweredOff
14746 || aMachineState == MachineState_Aborted
14747 || aMachineState == MachineState_Teleported)
14748 {
14749 /* Make sure any transient guest properties get removed from the
14750 * property store on shutdown. */
14751 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14752
14753 /* remove it from the settings representation */
14754 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14755 for (settings::GuestPropertiesList::iterator
14756 it = llGuestProperties.begin();
14757 it != llGuestProperties.end();
14758 /*nothing*/)
14759 {
14760 const settings::GuestProperty &prop = *it;
14761 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14762 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14763 {
14764 it = llGuestProperties.erase(it);
14765 fNeedsSaving = true;
14766 }
14767 else
14768 {
14769 ++it;
14770 }
14771 }
14772
14773 /* Additionally remove it from the HWData representation. Required to
14774 * keep everything in sync, as this is what the API keeps using. */
14775 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14776 for (HWData::GuestPropertyMap::iterator
14777 it = llHWGuestProperties.begin();
14778 it != llHWGuestProperties.end();
14779 /*nothing*/)
14780 {
14781 uint32_t fFlags = it->second.mFlags;
14782 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14783 {
14784 /* iterator where we need to continue after the erase call
14785 * (C++03 is a fact still, and it doesn't return the iterator
14786 * which would allow continuing) */
14787 HWData::GuestPropertyMap::iterator it2 = it;
14788 ++it2;
14789 llHWGuestProperties.erase(it);
14790 it = it2;
14791 fNeedsSaving = true;
14792 }
14793 else
14794 {
14795 ++it;
14796 }
14797 }
14798
14799 if (fNeedsSaving)
14800 {
14801 mData->mCurrentStateModified = TRUE;
14802 stsFlags |= SaveSTS_CurStateModified;
14803 }
14804 }
14805#endif /* VBOX_WITH_GUEST_PROPS */
14806
14807 rc = i_saveStateSettings(stsFlags);
14808
14809 if ( ( oldMachineState != MachineState_PoweredOff
14810 && oldMachineState != MachineState_Aborted
14811 && oldMachineState != MachineState_Teleported
14812 )
14813 && ( aMachineState == MachineState_PoweredOff
14814 || aMachineState == MachineState_Aborted
14815 || aMachineState == MachineState_Teleported
14816 )
14817 )
14818 {
14819 /* we've been shut down for any reason */
14820 /* no special action so far */
14821 }
14822
14823 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14824 LogFlowThisFuncLeave();
14825 return rc;
14826}
14827
14828/**
14829 * Sends the current machine state value to the VM process.
14830 *
14831 * @note Locks this object for reading, then calls a client process.
14832 */
14833HRESULT SessionMachine::i_updateMachineStateOnClient()
14834{
14835 AutoCaller autoCaller(this);
14836 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14837
14838 ComPtr<IInternalSessionControl> directControl;
14839 {
14840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14841 AssertReturn(!!mData, E_FAIL);
14842 if (mData->mSession.mLockType == LockType_VM)
14843 directControl = mData->mSession.mDirectControl;
14844
14845 /* directControl may be already set to NULL here in #OnSessionEnd()
14846 * called too early by the direct session process while there is still
14847 * some operation (like deleting the snapshot) in progress. The client
14848 * process in this case is waiting inside Session::close() for the
14849 * "end session" process object to complete, while #uninit() called by
14850 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14851 * operation to complete. For now, we accept this inconsistent behavior
14852 * and simply do nothing here. */
14853
14854 if (mData->mSession.mState == SessionState_Unlocking)
14855 return S_OK;
14856 }
14857
14858 /* ignore notifications sent after #OnSessionEnd() is called */
14859 if (!directControl)
14860 return S_OK;
14861
14862 return directControl->UpdateMachineState(mData->mMachineState);
14863}
14864
14865
14866/*static*/
14867HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14868{
14869 va_list args;
14870 va_start(args, pcszMsg);
14871 HRESULT rc = setErrorInternal(aResultCode,
14872 getStaticClassIID(),
14873 getStaticComponentName(),
14874 Utf8Str(pcszMsg, args),
14875 false /* aWarning */,
14876 true /* aLogIt */);
14877 va_end(args);
14878 return rc;
14879}
14880
14881
14882HRESULT Machine::updateState(MachineState_T aState)
14883{
14884 NOREF(aState);
14885 ReturnComNotImplemented();
14886}
14887
14888HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14889{
14890 NOREF(aProgress);
14891 ReturnComNotImplemented();
14892}
14893
14894HRESULT Machine::endPowerUp(LONG aResult)
14895{
14896 NOREF(aResult);
14897 ReturnComNotImplemented();
14898}
14899
14900HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14901{
14902 NOREF(aProgress);
14903 ReturnComNotImplemented();
14904}
14905
14906HRESULT Machine::endPoweringDown(LONG aResult,
14907 const com::Utf8Str &aErrMsg)
14908{
14909 NOREF(aResult);
14910 NOREF(aErrMsg);
14911 ReturnComNotImplemented();
14912}
14913
14914HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14915 BOOL *aMatched,
14916 ULONG *aMaskedInterfaces)
14917{
14918 NOREF(aDevice);
14919 NOREF(aMatched);
14920 NOREF(aMaskedInterfaces);
14921 ReturnComNotImplemented();
14922
14923}
14924
14925HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14926{
14927 NOREF(aId); NOREF(aCaptureFilename);
14928 ReturnComNotImplemented();
14929}
14930
14931HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14932 BOOL aDone)
14933{
14934 NOREF(aId);
14935 NOREF(aDone);
14936 ReturnComNotImplemented();
14937}
14938
14939HRESULT Machine::autoCaptureUSBDevices()
14940{
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14945{
14946 NOREF(aDone);
14947 ReturnComNotImplemented();
14948}
14949
14950HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14951 ComPtr<IProgress> &aProgress)
14952{
14953 NOREF(aSession);
14954 NOREF(aProgress);
14955 ReturnComNotImplemented();
14956}
14957
14958HRESULT Machine::finishOnlineMergeMedium()
14959{
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14964{
14965 RT_NOREF(aParms, aID);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974
14975HRESULT Machine::clipboardAreaAttach(ULONG aID)
14976{
14977 RT_NOREF(aID);
14978 ReturnComNotImplemented();
14979}
14980HRESULT Machine::clipboardAreaDetach(ULONG aID)
14981{
14982 RT_NOREF(aID);
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14987{
14988 RT_NOREF(aID);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14993{
14994 RT_NOREF(aID, aRefCount);
14995 ReturnComNotImplemented();
14996}
14997
14998HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14999 std::vector<com::Utf8Str> &aValues,
15000 std::vector<LONG64> &aTimestamps,
15001 std::vector<com::Utf8Str> &aFlags)
15002{
15003 NOREF(aNames);
15004 NOREF(aValues);
15005 NOREF(aTimestamps);
15006 NOREF(aFlags);
15007 ReturnComNotImplemented();
15008}
15009
15010HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15011 const com::Utf8Str &aValue,
15012 LONG64 aTimestamp,
15013 const com::Utf8Str &aFlags)
15014{
15015 NOREF(aName);
15016 NOREF(aValue);
15017 NOREF(aTimestamp);
15018 NOREF(aFlags);
15019 ReturnComNotImplemented();
15020}
15021
15022HRESULT Machine::lockMedia()
15023{
15024 ReturnComNotImplemented();
15025}
15026
15027HRESULT Machine::unlockMedia()
15028{
15029 ReturnComNotImplemented();
15030}
15031
15032HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15033 ComPtr<IMediumAttachment> &aNewAttachment)
15034{
15035 NOREF(aAttachment);
15036 NOREF(aNewAttachment);
15037 ReturnComNotImplemented();
15038}
15039
15040HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15041 ULONG aCpuUser,
15042 ULONG aCpuKernel,
15043 ULONG aCpuIdle,
15044 ULONG aMemTotal,
15045 ULONG aMemFree,
15046 ULONG aMemBalloon,
15047 ULONG aMemShared,
15048 ULONG aMemCache,
15049 ULONG aPagedTotal,
15050 ULONG aMemAllocTotal,
15051 ULONG aMemFreeTotal,
15052 ULONG aMemBalloonTotal,
15053 ULONG aMemSharedTotal,
15054 ULONG aVmNetRx,
15055 ULONG aVmNetTx)
15056{
15057 NOREF(aValidStats);
15058 NOREF(aCpuUser);
15059 NOREF(aCpuKernel);
15060 NOREF(aCpuIdle);
15061 NOREF(aMemTotal);
15062 NOREF(aMemFree);
15063 NOREF(aMemBalloon);
15064 NOREF(aMemShared);
15065 NOREF(aMemCache);
15066 NOREF(aPagedTotal);
15067 NOREF(aMemAllocTotal);
15068 NOREF(aMemFreeTotal);
15069 NOREF(aMemBalloonTotal);
15070 NOREF(aMemSharedTotal);
15071 NOREF(aVmNetRx);
15072 NOREF(aVmNetTx);
15073 ReturnComNotImplemented();
15074}
15075
15076HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15077 com::Utf8Str &aResult)
15078{
15079 NOREF(aAuthParams);
15080 NOREF(aResult);
15081 ReturnComNotImplemented();
15082}
15083
15084com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15085{
15086 com::Utf8Str strControllerName = "Unknown";
15087 switch (aBusType)
15088 {
15089 case StorageBus_IDE:
15090 {
15091 strControllerName = "IDE";
15092 break;
15093 }
15094 case StorageBus_SATA:
15095 {
15096 strControllerName = "SATA";
15097 break;
15098 }
15099 case StorageBus_SCSI:
15100 {
15101 strControllerName = "SCSI";
15102 break;
15103 }
15104 case StorageBus_Floppy:
15105 {
15106 strControllerName = "Floppy";
15107 break;
15108 }
15109 case StorageBus_SAS:
15110 {
15111 strControllerName = "SAS";
15112 break;
15113 }
15114 case StorageBus_USB:
15115 {
15116 strControllerName = "USB";
15117 break;
15118 }
15119 default:
15120 break;
15121 }
15122 return strControllerName;
15123}
15124
15125HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15126{
15127 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15128
15129 AutoCaller autoCaller(this);
15130 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15131
15132 HRESULT rc = S_OK;
15133
15134 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15135 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15136 rc = getUSBDeviceFilters(usbDeviceFilters);
15137 if (FAILED(rc)) return rc;
15138
15139 NOREF(aFlags);
15140 com::Utf8Str osTypeId;
15141 ComObjPtr<GuestOSType> osType = NULL;
15142
15143 /* Get the guest os type as a string from the VB. */
15144 rc = getOSTypeId(osTypeId);
15145 if (FAILED(rc)) return rc;
15146
15147 /* Get the os type obj that coresponds, can be used to get
15148 * the defaults for this guest OS. */
15149 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15150 if (FAILED(rc)) return rc;
15151
15152 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15153
15154 /* Let the OS type select 64-bit ness. */
15155 mHWData->mLongMode = osType->i_is64Bit()
15156 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15157
15158 /* Let the OS type enable the X2APIC */
15159 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15160
15161 /* This one covers IOAPICEnabled. */
15162 mBIOSSettings->i_applyDefaults(osType);
15163
15164 /* Initialize default record settings. */
15165 mRecordingSettings->i_applyDefaults();
15166
15167 /* Initialize default BIOS settings here */
15168 /* Hardware virtualization must be ON by default */
15169 mHWData->mAPIC = true;
15170 mHWData->mHWVirtExEnabled = true;
15171
15172 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15173 if (FAILED(rc)) return rc;
15174
15175 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15176 if (FAILED(rc)) return rc;
15177
15178 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15179 if (FAILED(rc)) return rc;
15180
15181 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15182 if (FAILED(rc)) return rc;
15183
15184 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15188 if (FAILED(rc)) return rc;
15189
15190 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15191 if (FAILED(rc)) return rc;
15192
15193 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15194 if (FAILED(rc)) return rc;
15195
15196 BOOL mRTCUseUTC;
15197 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15198 if (FAILED(rc)) return rc;
15199
15200 setRTCUseUTC(mRTCUseUTC);
15201 if (FAILED(rc)) return rc;
15202
15203 /* the setter does more than just the assignment, so use it */
15204 ChipsetType_T enmChipsetType;
15205 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15206 if (FAILED(rc)) return rc;
15207
15208 rc = COMSETTER(ChipsetType)(enmChipsetType);
15209 if (FAILED(rc)) return rc;
15210
15211 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15212 if (FAILED(rc)) return rc;
15213
15214 /* Apply network adapters defaults */
15215 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15216 mNetworkAdapters[slot]->i_applyDefaults(osType);
15217
15218 /* Apply serial port defaults */
15219 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15220 mSerialPorts[slot]->i_applyDefaults(osType);
15221
15222 /* Apply parallel port defaults - not OS dependent*/
15223 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15224 mParallelPorts[slot]->i_applyDefaults();
15225
15226 /* Audio stuff. */
15227 AudioControllerType_T audioController;
15228 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15229 if (FAILED(rc)) return rc;
15230
15231 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15232 if (FAILED(rc)) return rc;
15233
15234 AudioCodecType_T audioCodec;
15235 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15239 if (FAILED(rc)) return rc;
15240
15241 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15242 if (FAILED(rc)) return rc;
15243
15244 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15245 if (FAILED(rc)) return rc;
15246
15247 /* Storage Controllers */
15248 StorageControllerType_T hdStorageControllerType;
15249 StorageBus_T hdStorageBusType;
15250 StorageControllerType_T dvdStorageControllerType;
15251 StorageBus_T dvdStorageBusType;
15252 BOOL recommendedFloppy;
15253 ComPtr<IStorageController> floppyController;
15254 ComPtr<IStorageController> hdController;
15255 ComPtr<IStorageController> dvdController;
15256 Utf8Str strFloppyName, strDVDName, strHDName;
15257
15258 /* GUI auto generates controller names using bus type. Do the same*/
15259 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15260
15261 /* Floppy recommended? add one. */
15262 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15263 if (FAILED(rc)) return rc;
15264 if (recommendedFloppy)
15265 {
15266 rc = addStorageController(strFloppyName,
15267 StorageBus_Floppy,
15268 floppyController);
15269 if (FAILED(rc)) return rc;
15270 }
15271
15272 /* Setup one DVD storage controller. */
15273 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15274 if (FAILED(rc)) return rc;
15275
15276 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15277 if (FAILED(rc)) return rc;
15278
15279 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15280
15281 rc = addStorageController(strDVDName,
15282 dvdStorageBusType,
15283 dvdController);
15284 if (FAILED(rc)) return rc;
15285
15286 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15287 if (FAILED(rc)) return rc;
15288
15289 /* Setup one HDD storage controller. */
15290 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15291 if (FAILED(rc)) return rc;
15292
15293 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15294 if (FAILED(rc)) return rc;
15295
15296 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15297
15298 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15299 {
15300 rc = addStorageController(strHDName,
15301 hdStorageBusType,
15302 hdController);
15303 if (FAILED(rc)) return rc;
15304
15305 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15306 if (FAILED(rc)) return rc;
15307 }
15308 else
15309 {
15310 /* The HD controller is the same as DVD: */
15311 hdController = dvdController;
15312 }
15313
15314 /* Limit the AHCI port count if it's used because windows has trouble with
15315 * too many ports and other guest (OS X in particular) may take extra long
15316 * boot: */
15317
15318 // pParent = static_cast<Medium*>(aP)
15319 IStorageController *temp = hdController;
15320 ComObjPtr<StorageController> storageController;
15321 storageController = static_cast<StorageController *>(temp);
15322
15323 // tempHDController = aHDController;
15324 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15325 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15326 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15327 storageController->COMSETTER(PortCount)(1);
15328
15329 /* USB stuff */
15330
15331 bool ohciEnabled = false;
15332
15333 ComPtr<IUSBController> usbController;
15334 BOOL recommendedUSB3;
15335 BOOL recommendedUSB;
15336 BOOL usbProxyAvailable;
15337
15338 getUSBProxyAvailable(&usbProxyAvailable);
15339 if (FAILED(rc)) return rc;
15340
15341 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15342 if (FAILED(rc)) return rc;
15343 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15344 if (FAILED(rc)) return rc;
15345
15346 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15347 {
15348#ifdef VBOX_WITH_EXTPACK
15349 /* USB 3.0 is only available if the proper ExtPack is installed. */
15350 ExtPackManager *aManager = mParent->i_getExtPackManager();
15351 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15352 {
15353 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15354 if (FAILED(rc)) return rc;
15355
15356 /* xHci includes OHCI */
15357 ohciEnabled = true;
15358 }
15359#endif
15360 }
15361 if ( !ohciEnabled
15362 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15363 {
15364 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15365 if (FAILED(rc)) return rc;
15366 ohciEnabled = true;
15367
15368#ifdef VBOX_WITH_EXTPACK
15369 /* USB 2.0 is only available if the proper ExtPack is installed.
15370 * Note. Configuring EHCI here and providing messages about
15371 * the missing extpack isn't exactly clean, but it is a
15372 * necessary evil to patch over legacy compatability issues
15373 * introduced by the new distribution model. */
15374 ExtPackManager *manager = mParent->i_getExtPackManager();
15375 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15376 {
15377 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15378 if (FAILED(rc)) return rc;
15379 }
15380#endif
15381 }
15382
15383 /* Set recommended human interface device types: */
15384 BOOL recommendedUSBHID;
15385 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15386 if (FAILED(rc)) return rc;
15387
15388 if (recommendedUSBHID)
15389 {
15390 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15391 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15392 if (!ohciEnabled && !usbDeviceFilters.isNull())
15393 {
15394 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15395 if (FAILED(rc)) return rc;
15396 }
15397 }
15398
15399 BOOL recommendedUSBTablet;
15400 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15401 if (FAILED(rc)) return rc;
15402
15403 if (recommendedUSBTablet)
15404 {
15405 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15406 if (!ohciEnabled && !usbDeviceFilters.isNull())
15407 {
15408 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15409 if (FAILED(rc)) return rc;
15410 }
15411 }
15412 return S_OK;
15413}
15414
15415/* This isn't handled entirely by the wrapper generator yet. */
15416#ifdef VBOX_WITH_XPCOM
15417NS_DECL_CLASSINFO(SessionMachine)
15418NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15419
15420NS_DECL_CLASSINFO(SnapshotMachine)
15421NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15422#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