VirtualBox

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

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

Main/Machine: Workaround for windows VM hang when 2D accel is enabled with the VMSVGA based graphics controllers. To be fixed properly soon.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 535.6 KB
Line 
1/* $Id: MachineImpl.cpp 80752 2019-09-12 11:03:41Z 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);
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 com::Utf8Str &aEnvironment,
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, aEnvironment, 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 Utf8Str &strEnvironment,
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 ULONG uPid = 0;
7609 rc = pVBoxSDS->LaunchVMProcess(Bstr(idStr).raw(), Bstr(strMachineName).raw(), Bstr(strFrontend).raw(),
7610 Bstr(strEnvironment).raw(), Bstr(strSupHardeningLogArg).raw(),
7611 idCallerSession, &uPid);
7612 if (FAILED(rc))
7613 return setError(rc, tr("Failed to start the machine '%s'. Process creation failed"), strMachineName.c_str());
7614 pid = (RTPROCESS)uPid;
7615 }
7616 else
7617#endif /* VBOX_WITH_VBOXSDS && RT_OS_WINDOWS */
7618 {
7619 int vrc = MachineLaunchVMCommonWorker(idStr, strMachineName, strFrontend, strEnvironment, strSupHardeningLogArg,
7620 strAppOverride, 0 /*fFlags*/, NULL /*pvExtraData*/, pid);
7621 if (RT_FAILURE(vrc))
7622 return setErrorBoth(VBOX_E_IPRT_ERROR, vrc,
7623 tr("Could not launch the VM process for the machine '%s' (%Rrc)"), strMachineName.c_str(), vrc);
7624 }
7625
7626 LogRel(("Launched VM: %u pid: %u (%#x) frontend: %s name: %s\n",
7627 idStr.c_str(), pid, pid, strFrontend.c_str(), strMachineName.c_str()));
7628
7629 if (!fSeparate)
7630 {
7631 /*
7632 * Note that we don't release the lock here before calling the client,
7633 * because it doesn't need to call us back if called with a NULL argument.
7634 * Releasing the lock here is dangerous because we didn't prepare the
7635 * launch data yet, but the client we've just started may happen to be
7636 * too fast and call LockMachine() that will fail (because of PID, etc.),
7637 * so that the Machine will never get out of the Spawning session state.
7638 */
7639
7640 /* inform the session that it will be a remote one */
7641 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7642#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7643 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7644#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7645 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7646#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7647 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7648
7649 if (FAILED(rc))
7650 {
7651 /* restore the session state */
7652 mData->mSession.mState = SessionState_Unlocked;
7653 alock.release();
7654 mParent->i_addProcessToReap(pid);
7655 /* The failure may occur w/o any error info (from RPC), so provide one */
7656 return setError(VBOX_E_VM_ERROR,
7657 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7658 }
7659
7660 /* attach launch data to the machine */
7661 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7662 mData->mSession.mRemoteControls.push_back(aControl);
7663 mData->mSession.mProgress = aProgress;
7664 mData->mSession.mPID = pid;
7665 mData->mSession.mState = SessionState_Spawning;
7666 Assert(strCanonicalName.isNotEmpty());
7667 mData->mSession.mName = strCanonicalName;
7668 }
7669 else
7670 {
7671 /* For separate UI process we declare the launch as completed instantly, as the
7672 * actual headless VM start may or may not come. No point in remembering anything
7673 * yet, as what matters for us is when the headless VM gets started. */
7674 aProgress->i_notifyComplete(S_OK);
7675 }
7676
7677 alock.release();
7678 mParent->i_addProcessToReap(pid);
7679
7680 LogFlowThisFuncLeave();
7681 return S_OK;
7682}
7683
7684/**
7685 * Returns @c true if the given session machine instance has an open direct
7686 * session (and optionally also for direct sessions which are closing) and
7687 * returns the session control machine instance if so.
7688 *
7689 * Note that when the method returns @c false, the arguments remain unchanged.
7690 *
7691 * @param aMachine Session machine object.
7692 * @param aControl Direct session control object (optional).
7693 * @param aRequireVM If true then only allow VM sessions.
7694 * @param aAllowClosing If true then additionally a session which is currently
7695 * being closed will also be allowed.
7696 *
7697 * @note locks this object for reading.
7698 */
7699bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7700 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7701 bool aRequireVM /*= false*/,
7702 bool aAllowClosing /*= false*/)
7703{
7704 AutoLimitedCaller autoCaller(this);
7705 AssertComRCReturn(autoCaller.rc(), false);
7706
7707 /* just return false for inaccessible machines */
7708 if (getObjectState().getState() != ObjectState::Ready)
7709 return false;
7710
7711 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7712
7713 if ( ( mData->mSession.mState == SessionState_Locked
7714 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7715 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7716 )
7717 {
7718 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7719
7720 aMachine = mData->mSession.mMachine;
7721
7722 if (aControl != NULL)
7723 *aControl = mData->mSession.mDirectControl;
7724
7725 return true;
7726 }
7727
7728 return false;
7729}
7730
7731/**
7732 * Returns @c true if the given machine has an spawning direct session.
7733 *
7734 * @note locks this object for reading.
7735 */
7736bool Machine::i_isSessionSpawning()
7737{
7738 AutoLimitedCaller autoCaller(this);
7739 AssertComRCReturn(autoCaller.rc(), false);
7740
7741 /* just return false for inaccessible machines */
7742 if (getObjectState().getState() != ObjectState::Ready)
7743 return false;
7744
7745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7746
7747 if (mData->mSession.mState == SessionState_Spawning)
7748 return true;
7749
7750 return false;
7751}
7752
7753/**
7754 * Called from the client watcher thread to check for unexpected client process
7755 * death during Session_Spawning state (e.g. before it successfully opened a
7756 * direct session).
7757 *
7758 * On Win32 and on OS/2, this method is called only when we've got the
7759 * direct client's process termination notification, so it always returns @c
7760 * true.
7761 *
7762 * On other platforms, this method returns @c true if the client process is
7763 * terminated and @c false if it's still alive.
7764 *
7765 * @note Locks this object for writing.
7766 */
7767bool Machine::i_checkForSpawnFailure()
7768{
7769 AutoCaller autoCaller(this);
7770 if (!autoCaller.isOk())
7771 {
7772 /* nothing to do */
7773 LogFlowThisFunc(("Already uninitialized!\n"));
7774 return true;
7775 }
7776
7777 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7778
7779 if (mData->mSession.mState != SessionState_Spawning)
7780 {
7781 /* nothing to do */
7782 LogFlowThisFunc(("Not spawning any more!\n"));
7783 return true;
7784 }
7785
7786 HRESULT rc = S_OK;
7787
7788 /* PID not yet initialized, skip check. */
7789 if (mData->mSession.mPID == NIL_RTPROCESS)
7790 return false;
7791
7792 RTPROCSTATUS status;
7793 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7794
7795 if (vrc != VERR_PROCESS_RUNNING)
7796 {
7797 Utf8Str strExtraInfo;
7798
7799#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7800 /* If the startup logfile exists and is of non-zero length, tell the
7801 user to look there for more details to encourage them to attach it
7802 when reporting startup issues. */
7803 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7804 uint64_t cbStartupLogFile = 0;
7805 int vrc2 = RTFileQuerySizeByPath(strHardeningLogFile.c_str(), &cbStartupLogFile);
7806 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7807 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7808#endif
7809
7810 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7811 rc = setError(E_FAIL,
7812 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7813 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7814 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7815 rc = setError(E_FAIL,
7816 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7817 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7818 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7819 rc = setError(E_FAIL,
7820 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7821 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7822 else
7823 rc = setErrorBoth(E_FAIL, vrc,
7824 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7825 i_getName().c_str(), vrc, strExtraInfo.c_str());
7826 }
7827
7828 if (FAILED(rc))
7829 {
7830 /* Close the remote session, remove the remote control from the list
7831 * and reset session state to Closed (@note keep the code in sync with
7832 * the relevant part in LockMachine()). */
7833
7834 Assert(mData->mSession.mRemoteControls.size() == 1);
7835 if (mData->mSession.mRemoteControls.size() == 1)
7836 {
7837 ErrorInfoKeeper eik;
7838 mData->mSession.mRemoteControls.front()->Uninitialize();
7839 }
7840
7841 mData->mSession.mRemoteControls.clear();
7842 mData->mSession.mState = SessionState_Unlocked;
7843
7844 /* finalize the progress after setting the state */
7845 if (!mData->mSession.mProgress.isNull())
7846 {
7847 mData->mSession.mProgress->notifyComplete(rc);
7848 mData->mSession.mProgress.setNull();
7849 }
7850
7851 mData->mSession.mPID = NIL_RTPROCESS;
7852
7853 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7854 return true;
7855 }
7856
7857 return false;
7858}
7859
7860/**
7861 * Checks whether the machine can be registered. If so, commits and saves
7862 * all settings.
7863 *
7864 * @note Must be called from mParent's write lock. Locks this object and
7865 * children for writing.
7866 */
7867HRESULT Machine::i_prepareRegister()
7868{
7869 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7870
7871 AutoLimitedCaller autoCaller(this);
7872 AssertComRCReturnRC(autoCaller.rc());
7873
7874 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7875
7876 /* wait for state dependents to drop to zero */
7877 i_ensureNoStateDependencies();
7878
7879 if (!mData->mAccessible)
7880 return setError(VBOX_E_INVALID_OBJECT_STATE,
7881 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7882 mUserData->s.strName.c_str(),
7883 mData->mUuid.toString().c_str());
7884
7885 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7886
7887 if (mData->mRegistered)
7888 return setError(VBOX_E_INVALID_OBJECT_STATE,
7889 tr("The machine '%s' with UUID {%s} is already registered"),
7890 mUserData->s.strName.c_str(),
7891 mData->mUuid.toString().c_str());
7892
7893 HRESULT rc = S_OK;
7894
7895 // Ensure the settings are saved. If we are going to be registered and
7896 // no config file exists yet, create it by calling i_saveSettings() too.
7897 if ( (mData->flModifications)
7898 || (!mData->pMachineConfigFile->fileExists())
7899 )
7900 {
7901 rc = i_saveSettings(NULL);
7902 // no need to check whether VirtualBox.xml needs saving too since
7903 // we can't have a machine XML file rename pending
7904 if (FAILED(rc)) return rc;
7905 }
7906
7907 /* more config checking goes here */
7908
7909 if (SUCCEEDED(rc))
7910 {
7911 /* we may have had implicit modifications we want to fix on success */
7912 i_commit();
7913
7914 mData->mRegistered = true;
7915 }
7916 else
7917 {
7918 /* we may have had implicit modifications we want to cancel on failure*/
7919 i_rollback(false /* aNotify */);
7920 }
7921
7922 return rc;
7923}
7924
7925/**
7926 * Increases the number of objects dependent on the machine state or on the
7927 * registered state. Guarantees that these two states will not change at least
7928 * until #i_releaseStateDependency() is called.
7929 *
7930 * Depending on the @a aDepType value, additional state checks may be made.
7931 * These checks will set extended error info on failure. See
7932 * #i_checkStateDependency() for more info.
7933 *
7934 * If this method returns a failure, the dependency is not added and the caller
7935 * is not allowed to rely on any particular machine state or registration state
7936 * value and may return the failed result code to the upper level.
7937 *
7938 * @param aDepType Dependency type to add.
7939 * @param aState Current machine state (NULL if not interested).
7940 * @param aRegistered Current registered state (NULL if not interested).
7941 *
7942 * @note Locks this object for writing.
7943 */
7944HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7945 MachineState_T *aState /* = NULL */,
7946 BOOL *aRegistered /* = NULL */)
7947{
7948 AutoCaller autoCaller(this);
7949 AssertComRCReturnRC(autoCaller.rc());
7950
7951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7952
7953 HRESULT rc = i_checkStateDependency(aDepType);
7954 if (FAILED(rc)) return rc;
7955
7956 {
7957 if (mData->mMachineStateChangePending != 0)
7958 {
7959 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7960 * drop to zero so don't add more. It may make sense to wait a bit
7961 * and retry before reporting an error (since the pending state
7962 * transition should be really quick) but let's just assert for
7963 * now to see if it ever happens on practice. */
7964
7965 AssertFailed();
7966
7967 return setError(E_ACCESSDENIED,
7968 tr("Machine state change is in progress. Please retry the operation later."));
7969 }
7970
7971 ++mData->mMachineStateDeps;
7972 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7973 }
7974
7975 if (aState)
7976 *aState = mData->mMachineState;
7977 if (aRegistered)
7978 *aRegistered = mData->mRegistered;
7979
7980 return S_OK;
7981}
7982
7983/**
7984 * Decreases the number of objects dependent on the machine state.
7985 * Must always complete the #i_addStateDependency() call after the state
7986 * dependency is no more necessary.
7987 */
7988void Machine::i_releaseStateDependency()
7989{
7990 AutoCaller autoCaller(this);
7991 AssertComRCReturnVoid(autoCaller.rc());
7992
7993 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7994
7995 /* releaseStateDependency() w/o addStateDependency()? */
7996 AssertReturnVoid(mData->mMachineStateDeps != 0);
7997 -- mData->mMachineStateDeps;
7998
7999 if (mData->mMachineStateDeps == 0)
8000 {
8001 /* inform i_ensureNoStateDependencies() that there are no more deps */
8002 if (mData->mMachineStateChangePending != 0)
8003 {
8004 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8005 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8006 }
8007 }
8008}
8009
8010Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8011{
8012 /* start with nothing found */
8013 Utf8Str strResult("");
8014
8015 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8016
8017 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8018 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8019 // found:
8020 strResult = it->second; // source is a Utf8Str
8021
8022 return strResult;
8023}
8024
8025// protected methods
8026/////////////////////////////////////////////////////////////////////////////
8027
8028/**
8029 * Performs machine state checks based on the @a aDepType value. If a check
8030 * fails, this method will set extended error info, otherwise it will return
8031 * S_OK. It is supposed, that on failure, the caller will immediately return
8032 * the return value of this method to the upper level.
8033 *
8034 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8035 *
8036 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8037 * current state of this machine object allows to change settings of the
8038 * machine (i.e. the machine is not registered, or registered but not running
8039 * and not saved). It is useful to call this method from Machine setters
8040 * before performing any change.
8041 *
8042 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8043 * as for MutableStateDep except that if the machine is saved, S_OK is also
8044 * returned. This is useful in setters which allow changing machine
8045 * properties when it is in the saved state.
8046 *
8047 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8048 * if the current state of this machine object allows to change runtime
8049 * changeable settings of the machine (i.e. the machine is not registered, or
8050 * registered but either running or not running and not saved). It is useful
8051 * to call this method from Machine setters before performing any changes to
8052 * runtime changeable settings.
8053 *
8054 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8055 * the same as for MutableOrRunningStateDep except that if the machine is
8056 * saved, S_OK is also returned. This is useful in setters which allow
8057 * changing runtime and saved state changeable machine properties.
8058 *
8059 * @param aDepType Dependency type to check.
8060 *
8061 * @note Non Machine based classes should use #i_addStateDependency() and
8062 * #i_releaseStateDependency() methods or the smart AutoStateDependency
8063 * template.
8064 *
8065 * @note This method must be called from under this object's read or write
8066 * lock.
8067 */
8068HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8069{
8070 switch (aDepType)
8071 {
8072 case AnyStateDep:
8073 {
8074 break;
8075 }
8076 case MutableStateDep:
8077 {
8078 if ( mData->mRegistered
8079 && ( !i_isSessionMachine()
8080 || ( mData->mMachineState != MachineState_Aborted
8081 && mData->mMachineState != MachineState_Teleported
8082 && mData->mMachineState != MachineState_PoweredOff
8083 )
8084 )
8085 )
8086 return setError(VBOX_E_INVALID_VM_STATE,
8087 tr("The machine is not mutable (state is %s)"),
8088 Global::stringifyMachineState(mData->mMachineState));
8089 break;
8090 }
8091 case MutableOrSavedStateDep:
8092 {
8093 if ( mData->mRegistered
8094 && ( !i_isSessionMachine()
8095 || ( mData->mMachineState != MachineState_Aborted
8096 && mData->mMachineState != MachineState_Teleported
8097 && mData->mMachineState != MachineState_Saved
8098 && mData->mMachineState != MachineState_PoweredOff
8099 )
8100 )
8101 )
8102 return setError(VBOX_E_INVALID_VM_STATE,
8103 tr("The machine is not mutable or saved (state is %s)"),
8104 Global::stringifyMachineState(mData->mMachineState));
8105 break;
8106 }
8107 case MutableOrRunningStateDep:
8108 {
8109 if ( mData->mRegistered
8110 && ( !i_isSessionMachine()
8111 || ( mData->mMachineState != MachineState_Aborted
8112 && mData->mMachineState != MachineState_Teleported
8113 && mData->mMachineState != MachineState_PoweredOff
8114 && !Global::IsOnline(mData->mMachineState)
8115 )
8116 )
8117 )
8118 return setError(VBOX_E_INVALID_VM_STATE,
8119 tr("The machine is not mutable or running (state is %s)"),
8120 Global::stringifyMachineState(mData->mMachineState));
8121 break;
8122 }
8123 case MutableOrSavedOrRunningStateDep:
8124 {
8125 if ( mData->mRegistered
8126 && ( !i_isSessionMachine()
8127 || ( mData->mMachineState != MachineState_Aborted
8128 && mData->mMachineState != MachineState_Teleported
8129 && mData->mMachineState != MachineState_Saved
8130 && mData->mMachineState != MachineState_PoweredOff
8131 && !Global::IsOnline(mData->mMachineState)
8132 )
8133 )
8134 )
8135 return setError(VBOX_E_INVALID_VM_STATE,
8136 tr("The machine is not mutable, saved or running (state is %s)"),
8137 Global::stringifyMachineState(mData->mMachineState));
8138 break;
8139 }
8140 }
8141
8142 return S_OK;
8143}
8144
8145/**
8146 * Helper to initialize all associated child objects and allocate data
8147 * structures.
8148 *
8149 * This method must be called as a part of the object's initialization procedure
8150 * (usually done in the #init() method).
8151 *
8152 * @note Must be called only from #init() or from #i_registeredInit().
8153 */
8154HRESULT Machine::initDataAndChildObjects()
8155{
8156 AutoCaller autoCaller(this);
8157 AssertComRCReturnRC(autoCaller.rc());
8158 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8159 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8160
8161 AssertReturn(!mData->mAccessible, E_FAIL);
8162
8163 /* allocate data structures */
8164 mSSData.allocate();
8165 mUserData.allocate();
8166 mHWData.allocate();
8167 mMediumAttachments.allocate();
8168 mStorageControllers.allocate();
8169 mUSBControllers.allocate();
8170
8171 /* initialize mOSTypeId */
8172 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8173
8174/** @todo r=bird: init() methods never fails, right? Why don't we make them
8175 * return void then! */
8176
8177 /* create associated BIOS settings object */
8178 unconst(mBIOSSettings).createObject();
8179 mBIOSSettings->init(this);
8180
8181 /* create associated record settings object */
8182 unconst(mRecordingSettings).createObject();
8183 mRecordingSettings->init(this);
8184
8185 /* create an associated VRDE object (default is disabled) */
8186 unconst(mVRDEServer).createObject();
8187 mVRDEServer->init(this);
8188
8189 /* create associated serial port objects */
8190 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8191 {
8192 unconst(mSerialPorts[slot]).createObject();
8193 mSerialPorts[slot]->init(this, slot);
8194 }
8195
8196 /* create associated parallel port objects */
8197 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8198 {
8199 unconst(mParallelPorts[slot]).createObject();
8200 mParallelPorts[slot]->init(this, slot);
8201 }
8202
8203 /* create the audio adapter object (always present, default is disabled) */
8204 unconst(mAudioAdapter).createObject();
8205 mAudioAdapter->init(this);
8206
8207 /* create the USB device filters object (always present) */
8208 unconst(mUSBDeviceFilters).createObject();
8209 mUSBDeviceFilters->init(this);
8210
8211 /* create associated network adapter objects */
8212 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8213 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8214 {
8215 unconst(mNetworkAdapters[slot]).createObject();
8216 mNetworkAdapters[slot]->init(this, slot);
8217 }
8218
8219 /* create the bandwidth control */
8220 unconst(mBandwidthControl).createObject();
8221 mBandwidthControl->init(this);
8222
8223 return S_OK;
8224}
8225
8226/**
8227 * Helper to uninitialize all associated child objects and to free all data
8228 * structures.
8229 *
8230 * This method must be called as a part of the object's uninitialization
8231 * procedure (usually done in the #uninit() method).
8232 *
8233 * @note Must be called only from #uninit() or from #i_registeredInit().
8234 */
8235void Machine::uninitDataAndChildObjects()
8236{
8237 AutoCaller autoCaller(this);
8238 AssertComRCReturnVoid(autoCaller.rc());
8239 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8240 || getObjectState().getState() == ObjectState::Limited);
8241
8242 /* tell all our other child objects we've been uninitialized */
8243 if (mBandwidthControl)
8244 {
8245 mBandwidthControl->uninit();
8246 unconst(mBandwidthControl).setNull();
8247 }
8248
8249 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8250 {
8251 if (mNetworkAdapters[slot])
8252 {
8253 mNetworkAdapters[slot]->uninit();
8254 unconst(mNetworkAdapters[slot]).setNull();
8255 }
8256 }
8257
8258 if (mUSBDeviceFilters)
8259 {
8260 mUSBDeviceFilters->uninit();
8261 unconst(mUSBDeviceFilters).setNull();
8262 }
8263
8264 if (mAudioAdapter)
8265 {
8266 mAudioAdapter->uninit();
8267 unconst(mAudioAdapter).setNull();
8268 }
8269
8270 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8271 {
8272 if (mParallelPorts[slot])
8273 {
8274 mParallelPorts[slot]->uninit();
8275 unconst(mParallelPorts[slot]).setNull();
8276 }
8277 }
8278
8279 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8280 {
8281 if (mSerialPorts[slot])
8282 {
8283 mSerialPorts[slot]->uninit();
8284 unconst(mSerialPorts[slot]).setNull();
8285 }
8286 }
8287
8288 if (mVRDEServer)
8289 {
8290 mVRDEServer->uninit();
8291 unconst(mVRDEServer).setNull();
8292 }
8293
8294 if (mBIOSSettings)
8295 {
8296 mBIOSSettings->uninit();
8297 unconst(mBIOSSettings).setNull();
8298 }
8299
8300 if (mRecordingSettings)
8301 {
8302 mRecordingSettings->uninit();
8303 unconst(mRecordingSettings).setNull();
8304 }
8305
8306 /* Deassociate media (only when a real Machine or a SnapshotMachine
8307 * instance is uninitialized; SessionMachine instances refer to real
8308 * Machine media). This is necessary for a clean re-initialization of
8309 * the VM after successfully re-checking the accessibility state. Note
8310 * that in case of normal Machine or SnapshotMachine uninitialization (as
8311 * a result of unregistering or deleting the snapshot), outdated media
8312 * attachments will already be uninitialized and deleted, so this
8313 * code will not affect them. */
8314 if ( !mMediumAttachments.isNull()
8315 && !i_isSessionMachine()
8316 )
8317 {
8318 for (MediumAttachmentList::const_iterator
8319 it = mMediumAttachments->begin();
8320 it != mMediumAttachments->end();
8321 ++it)
8322 {
8323 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8324 if (pMedium.isNull())
8325 continue;
8326 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8327 AssertComRC(rc);
8328 }
8329 }
8330
8331 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8332 {
8333 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8334 if (mData->mFirstSnapshot)
8335 {
8336 // snapshots tree is protected by machine write lock; strictly
8337 // this isn't necessary here since we're deleting the entire
8338 // machine, but otherwise we assert in Snapshot::uninit()
8339 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8340 mData->mFirstSnapshot->uninit();
8341 mData->mFirstSnapshot.setNull();
8342 }
8343
8344 mData->mCurrentSnapshot.setNull();
8345 }
8346
8347 /* free data structures (the essential mData structure is not freed here
8348 * since it may be still in use) */
8349 mMediumAttachments.free();
8350 mStorageControllers.free();
8351 mUSBControllers.free();
8352 mHWData.free();
8353 mUserData.free();
8354 mSSData.free();
8355}
8356
8357/**
8358 * Returns a pointer to the Machine object for this machine that acts like a
8359 * parent for complex machine data objects such as shared folders, etc.
8360 *
8361 * For primary Machine objects and for SnapshotMachine objects, returns this
8362 * object's pointer itself. For SessionMachine objects, returns the peer
8363 * (primary) machine pointer.
8364 */
8365Machine *Machine::i_getMachine()
8366{
8367 if (i_isSessionMachine())
8368 return (Machine*)mPeer;
8369 return this;
8370}
8371
8372/**
8373 * Makes sure that there are no machine state dependents. If necessary, waits
8374 * for the number of dependents to drop to zero.
8375 *
8376 * Make sure this method is called from under this object's write lock to
8377 * guarantee that no new dependents may be added when this method returns
8378 * control to the caller.
8379 *
8380 * @note Locks this object for writing. The lock will be released while waiting
8381 * (if necessary).
8382 *
8383 * @warning To be used only in methods that change the machine state!
8384 */
8385void Machine::i_ensureNoStateDependencies()
8386{
8387 AssertReturnVoid(isWriteLockOnCurrentThread());
8388
8389 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8390
8391 /* Wait for all state dependents if necessary */
8392 if (mData->mMachineStateDeps != 0)
8393 {
8394 /* lazy semaphore creation */
8395 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8396 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8397
8398 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8399 mData->mMachineStateDeps));
8400
8401 ++mData->mMachineStateChangePending;
8402
8403 /* reset the semaphore before waiting, the last dependent will signal
8404 * it */
8405 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8406
8407 alock.release();
8408
8409 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8410
8411 alock.acquire();
8412
8413 -- mData->mMachineStateChangePending;
8414 }
8415}
8416
8417/**
8418 * Changes the machine state and informs callbacks.
8419 *
8420 * This method is not intended to fail so it either returns S_OK or asserts (and
8421 * returns a failure).
8422 *
8423 * @note Locks this object for writing.
8424 */
8425HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8426{
8427 LogFlowThisFuncEnter();
8428 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8429 Assert(aMachineState != MachineState_Null);
8430
8431 AutoCaller autoCaller(this);
8432 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8433
8434 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8435
8436 /* wait for state dependents to drop to zero */
8437 i_ensureNoStateDependencies();
8438
8439 MachineState_T const enmOldState = mData->mMachineState;
8440 if (enmOldState != aMachineState)
8441 {
8442 mData->mMachineState = aMachineState;
8443 RTTimeNow(&mData->mLastStateChange);
8444
8445#ifdef VBOX_WITH_DTRACE_R3_MAIN
8446 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8447#endif
8448 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8449 }
8450
8451 LogFlowThisFuncLeave();
8452 return S_OK;
8453}
8454
8455/**
8456 * Searches for a shared folder with the given logical name
8457 * in the collection of shared folders.
8458 *
8459 * @param aName logical name of the shared folder
8460 * @param aSharedFolder where to return the found object
8461 * @param aSetError whether to set the error info if the folder is
8462 * not found
8463 * @return
8464 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8465 *
8466 * @note
8467 * must be called from under the object's lock!
8468 */
8469HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8470 ComObjPtr<SharedFolder> &aSharedFolder,
8471 bool aSetError /* = false */)
8472{
8473 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8474 for (HWData::SharedFolderList::const_iterator
8475 it = mHWData->mSharedFolders.begin();
8476 it != mHWData->mSharedFolders.end();
8477 ++it)
8478 {
8479 SharedFolder *pSF = *it;
8480 AutoCaller autoCaller(pSF);
8481 if (pSF->i_getName() == aName)
8482 {
8483 aSharedFolder = pSF;
8484 rc = S_OK;
8485 break;
8486 }
8487 }
8488
8489 if (aSetError && FAILED(rc))
8490 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8491
8492 return rc;
8493}
8494
8495/**
8496 * Initializes all machine instance data from the given settings structures
8497 * from XML. The exception is the machine UUID which needs special handling
8498 * depending on the caller's use case, so the caller needs to set that herself.
8499 *
8500 * This gets called in several contexts during machine initialization:
8501 *
8502 * -- When machine XML exists on disk already and needs to be loaded into memory,
8503 * for example, from #i_registeredInit() to load all registered machines on
8504 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8505 * attached to the machine should be part of some media registry already.
8506 *
8507 * -- During OVF import, when a machine config has been constructed from an
8508 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8509 * ensure that the media listed as attachments in the config (which have
8510 * been imported from the OVF) receive the correct registry ID.
8511 *
8512 * -- During VM cloning.
8513 *
8514 * @param config Machine settings from XML.
8515 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8516 * for each attached medium in the config.
8517 * @return
8518 */
8519HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8520 const Guid *puuidRegistry)
8521{
8522 // copy name, description, OS type, teleporter, UTC etc.
8523 mUserData->s = config.machineUserData;
8524
8525 // look up the object by Id to check it is valid
8526 ComObjPtr<GuestOSType> pGuestOSType;
8527 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8528 if (!pGuestOSType.isNull())
8529 mUserData->s.strOsType = pGuestOSType->i_id();
8530
8531 // stateFile (optional)
8532 if (config.strStateFile.isEmpty())
8533 mSSData->strStateFilePath.setNull();
8534 else
8535 {
8536 Utf8Str stateFilePathFull(config.strStateFile);
8537 int vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8538 if (RT_FAILURE(vrc))
8539 return setErrorBoth(E_FAIL, vrc,
8540 tr("Invalid saved state file path '%s' (%Rrc)"),
8541 config.strStateFile.c_str(),
8542 vrc);
8543 mSSData->strStateFilePath = stateFilePathFull;
8544 }
8545
8546 // snapshot folder needs special processing so set it again
8547 HRESULT rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8548 if (FAILED(rc)) return rc;
8549
8550 /* Copy the extra data items (config may or may not be the same as
8551 * mData->pMachineConfigFile) if necessary. When loading the XML files
8552 * from disk they are the same, but not for OVF import. */
8553 if (mData->pMachineConfigFile != &config)
8554 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8555
8556 /* currentStateModified (optional, default is true) */
8557 mData->mCurrentStateModified = config.fCurrentStateModified;
8558
8559 mData->mLastStateChange = config.timeLastStateChange;
8560
8561 /*
8562 * note: all mUserData members must be assigned prior this point because
8563 * we need to commit changes in order to let mUserData be shared by all
8564 * snapshot machine instances.
8565 */
8566 mUserData.commitCopy();
8567
8568 // machine registry, if present (must be loaded before snapshots)
8569 if (config.canHaveOwnMediaRegistry())
8570 {
8571 // determine machine folder
8572 Utf8Str strMachineFolder = i_getSettingsFileFull();
8573 strMachineFolder.stripFilename();
8574 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8575 config.mediaRegistry,
8576 strMachineFolder);
8577 if (FAILED(rc)) return rc;
8578 }
8579
8580 /* Snapshot node (optional) */
8581 size_t cRootSnapshots;
8582 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8583 {
8584 // there must be only one root snapshot
8585 Assert(cRootSnapshots == 1);
8586
8587 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8588
8589 rc = i_loadSnapshot(snap,
8590 config.uuidCurrentSnapshot,
8591 NULL); // no parent == first snapshot
8592 if (FAILED(rc)) return rc;
8593 }
8594
8595 // hardware data
8596 rc = i_loadHardware(puuidRegistry, NULL, config.hardwareMachine, &config.debugging, &config.autostart);
8597 if (FAILED(rc)) return rc;
8598
8599 /*
8600 * NOTE: the assignment below must be the last thing to do,
8601 * otherwise it will be not possible to change the settings
8602 * somewhere in the code above because all setters will be
8603 * blocked by i_checkStateDependency(MutableStateDep).
8604 */
8605
8606 /* set the machine state to Aborted or Saved when appropriate */
8607 if (config.fAborted)
8608 {
8609 mSSData->strStateFilePath.setNull();
8610
8611 /* no need to use i_setMachineState() during init() */
8612 mData->mMachineState = MachineState_Aborted;
8613 }
8614 else if (!mSSData->strStateFilePath.isEmpty())
8615 {
8616 /* no need to use i_setMachineState() during init() */
8617 mData->mMachineState = MachineState_Saved;
8618 }
8619
8620 // after loading settings, we are no longer different from the XML on disk
8621 mData->flModifications = 0;
8622
8623 return S_OK;
8624}
8625
8626/**
8627 * Recursively loads all snapshots starting from the given.
8628 *
8629 * @param data snapshot settings.
8630 * @param aCurSnapshotId Current snapshot ID from the settings file.
8631 * @param aParentSnapshot Parent snapshot.
8632 */
8633HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8634 const Guid &aCurSnapshotId,
8635 Snapshot *aParentSnapshot)
8636{
8637 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8638 AssertReturn(!i_isSessionMachine(), E_FAIL);
8639
8640 HRESULT rc = S_OK;
8641
8642 Utf8Str strStateFile;
8643 if (!data.strStateFile.isEmpty())
8644 {
8645 /* optional */
8646 strStateFile = data.strStateFile;
8647 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8648 if (RT_FAILURE(vrc))
8649 return setErrorBoth(E_FAIL, vrc,
8650 tr("Invalid saved state file path '%s' (%Rrc)"),
8651 strStateFile.c_str(),
8652 vrc);
8653 }
8654
8655 /* create a snapshot machine object */
8656 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8657 pSnapshotMachine.createObject();
8658 rc = pSnapshotMachine->initFromSettings(this,
8659 data.hardware,
8660 &data.debugging,
8661 &data.autostart,
8662 data.uuid.ref(),
8663 strStateFile);
8664 if (FAILED(rc)) return rc;
8665
8666 /* create a snapshot object */
8667 ComObjPtr<Snapshot> pSnapshot;
8668 pSnapshot.createObject();
8669 /* initialize the snapshot */
8670 rc = pSnapshot->init(mParent, // VirtualBox object
8671 data.uuid,
8672 data.strName,
8673 data.strDescription,
8674 data.timestamp,
8675 pSnapshotMachine,
8676 aParentSnapshot);
8677 if (FAILED(rc)) return rc;
8678
8679 /* memorize the first snapshot if necessary */
8680 if (!mData->mFirstSnapshot)
8681 mData->mFirstSnapshot = pSnapshot;
8682
8683 /* memorize the current snapshot when appropriate */
8684 if ( !mData->mCurrentSnapshot
8685 && pSnapshot->i_getId() == aCurSnapshotId
8686 )
8687 mData->mCurrentSnapshot = pSnapshot;
8688
8689 // now create the children
8690 for (settings::SnapshotsList::const_iterator
8691 it = data.llChildSnapshots.begin();
8692 it != data.llChildSnapshots.end();
8693 ++it)
8694 {
8695 const settings::Snapshot &childData = *it;
8696 // recurse
8697 rc = i_loadSnapshot(childData,
8698 aCurSnapshotId,
8699 pSnapshot); // parent = the one we created above
8700 if (FAILED(rc)) return rc;
8701 }
8702
8703 return rc;
8704}
8705
8706/**
8707 * Loads settings into mHWData.
8708 *
8709 * @param puuidRegistry Registry ID.
8710 * @param puuidSnapshot Snapshot ID
8711 * @param data Reference to the hardware settings.
8712 * @param pDbg Pointer to the debugging settings.
8713 * @param pAutostart Pointer to the autostart settings.
8714 */
8715HRESULT Machine::i_loadHardware(const Guid *puuidRegistry,
8716 const Guid *puuidSnapshot,
8717 const settings::Hardware &data,
8718 const settings::Debugging *pDbg,
8719 const settings::Autostart *pAutostart)
8720{
8721 AssertReturn(!i_isSessionMachine(), E_FAIL);
8722
8723 HRESULT rc = S_OK;
8724
8725 try
8726 {
8727 ComObjPtr<GuestOSType> pGuestOSType;
8728 mParent->i_findGuestOSType(mUserData->s.strOsType, pGuestOSType);
8729
8730 /* The hardware version attribute (optional). */
8731 mHWData->mHWVersion = data.strVersion;
8732 mHWData->mHardwareUUID = data.uuid;
8733
8734 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8735 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8736 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8737 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8738 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8739 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8740 mHWData->mHWVirtExUseNativeApi = data.fUseNativeApi;
8741 mHWData->mPAEEnabled = data.fPAE;
8742 mHWData->mLongMode = data.enmLongMode;
8743 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8744 mHWData->mAPIC = data.fAPIC;
8745 mHWData->mX2APIC = data.fX2APIC;
8746 mHWData->mIBPBOnVMExit = data.fIBPBOnVMExit;
8747 mHWData->mIBPBOnVMEntry = data.fIBPBOnVMEntry;
8748 mHWData->mSpecCtrl = data.fSpecCtrl;
8749 mHWData->mSpecCtrlByHost = data.fSpecCtrlByHost;
8750 mHWData->mL1DFlushOnSched = data.fL1DFlushOnSched;
8751 mHWData->mL1DFlushOnVMEntry = data.fL1DFlushOnVMEntry;
8752 mHWData->mMDSClearOnSched = data.fMDSClearOnSched;
8753 mHWData->mMDSClearOnVMEntry = data.fMDSClearOnVMEntry;
8754 mHWData->mNestedHWVirt = data.fNestedHWVirt;
8755 mHWData->mCPUCount = data.cCPUs;
8756 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8757 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8758 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8759 mHWData->mCpuProfile = data.strCpuProfile;
8760
8761 // cpu
8762 if (mHWData->mCPUHotPlugEnabled)
8763 {
8764 for (settings::CpuList::const_iterator
8765 it = data.llCpus.begin();
8766 it != data.llCpus.end();
8767 ++it)
8768 {
8769 const settings::Cpu &cpu = *it;
8770
8771 mHWData->mCPUAttached[cpu.ulId] = true;
8772 }
8773 }
8774
8775 // cpuid leafs
8776 for (settings::CpuIdLeafsList::const_iterator
8777 it = data.llCpuIdLeafs.begin();
8778 it != data.llCpuIdLeafs.end();
8779 ++it)
8780 {
8781 const settings::CpuIdLeaf &rLeaf= *it;
8782 if ( rLeaf.idx < UINT32_C(0x20)
8783 || rLeaf.idx - UINT32_C(0x80000000) < UINT32_C(0x20)
8784 || rLeaf.idx - UINT32_C(0xc0000000) < UINT32_C(0x10) )
8785 mHWData->mCpuIdLeafList.push_back(rLeaf);
8786 /* else: just ignore */
8787 }
8788
8789 mHWData->mMemorySize = data.ulMemorySizeMB;
8790 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8791
8792 // boot order
8793 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8794 {
8795 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8796 if (it == data.mapBootOrder.end())
8797 mHWData->mBootOrder[i] = DeviceType_Null;
8798 else
8799 mHWData->mBootOrder[i] = it->second;
8800 }
8801
8802 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8803 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8804 mHWData->mMonitorCount = data.cMonitors;
8805 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8806 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8807 mHWData->mFirmwareType = data.firmwareType;
8808 mHWData->mPointingHIDType = data.pointingHIDType;
8809 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8810 mHWData->mChipsetType = data.chipsetType;
8811 mHWData->mParavirtProvider = data.paravirtProvider;
8812 mHWData->mParavirtDebug = data.strParavirtDebug;
8813 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8814 mHWData->mHPETEnabled = data.fHPETEnabled;
8815
8816 /* VRDEServer */
8817 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8818 if (FAILED(rc)) return rc;
8819
8820 /* BIOS */
8821 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8822 if (FAILED(rc)) return rc;
8823
8824 /* Recording settings */
8825 rc = mRecordingSettings->i_loadSettings(data.recordingSettings);
8826 if (FAILED(rc)) return rc;
8827
8828 // Bandwidth control (must come before network adapters)
8829 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8830 if (FAILED(rc)) return rc;
8831
8832 /* USB controllers */
8833 for (settings::USBControllerList::const_iterator
8834 it = data.usbSettings.llUSBControllers.begin();
8835 it != data.usbSettings.llUSBControllers.end();
8836 ++it)
8837 {
8838 const settings::USBController &settingsCtrl = *it;
8839 ComObjPtr<USBController> newCtrl;
8840
8841 newCtrl.createObject();
8842 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8843 mUSBControllers->push_back(newCtrl);
8844 }
8845
8846 /* USB device filters */
8847 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8848 if (FAILED(rc)) return rc;
8849
8850 // network adapters (establish array size first and apply defaults, to
8851 // ensure reading the same settings as we saved, since the list skips
8852 // adapters having defaults)
8853 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8854 size_t oldCount = mNetworkAdapters.size();
8855 if (newCount > oldCount)
8856 {
8857 mNetworkAdapters.resize(newCount);
8858 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8859 {
8860 unconst(mNetworkAdapters[slot]).createObject();
8861 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8862 }
8863 }
8864 else if (newCount < oldCount)
8865 mNetworkAdapters.resize(newCount);
8866 for (unsigned i = 0; i < mNetworkAdapters.size(); i++)
8867 mNetworkAdapters[i]->i_applyDefaults(pGuestOSType);
8868 for (settings::NetworkAdaptersList::const_iterator
8869 it = data.llNetworkAdapters.begin();
8870 it != data.llNetworkAdapters.end();
8871 ++it)
8872 {
8873 const settings::NetworkAdapter &nic = *it;
8874
8875 /* slot uniqueness is guaranteed by XML Schema */
8876 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8877 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8878 if (FAILED(rc)) return rc;
8879 }
8880
8881 // serial ports (establish defaults first, to ensure reading the same
8882 // settings as we saved, since the list skips ports having defaults)
8883 for (unsigned i = 0; i < RT_ELEMENTS(mSerialPorts); i++)
8884 mSerialPorts[i]->i_applyDefaults(pGuestOSType);
8885 for (settings::SerialPortsList::const_iterator
8886 it = data.llSerialPorts.begin();
8887 it != data.llSerialPorts.end();
8888 ++it)
8889 {
8890 const settings::SerialPort &s = *it;
8891
8892 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8893 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8894 if (FAILED(rc)) return rc;
8895 }
8896
8897 // parallel ports (establish defaults first, to ensure reading the same
8898 // settings as we saved, since the list skips ports having defaults)
8899 for (unsigned i = 0; i < RT_ELEMENTS(mParallelPorts); i++)
8900 mParallelPorts[i]->i_applyDefaults();
8901 for (settings::ParallelPortsList::const_iterator
8902 it = data.llParallelPorts.begin();
8903 it != data.llParallelPorts.end();
8904 ++it)
8905 {
8906 const settings::ParallelPort &p = *it;
8907
8908 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8909 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8910 if (FAILED(rc)) return rc;
8911 }
8912
8913 /* AudioAdapter */
8914 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8915 if (FAILED(rc)) return rc;
8916
8917 /* storage controllers */
8918 rc = i_loadStorageControllers(data.storage,
8919 puuidRegistry,
8920 puuidSnapshot);
8921 if (FAILED(rc)) return rc;
8922
8923 /* Shared folders */
8924 for (settings::SharedFoldersList::const_iterator
8925 it = data.llSharedFolders.begin();
8926 it != data.llSharedFolders.end();
8927 ++it)
8928 {
8929 const settings::SharedFolder &sf = *it;
8930
8931 ComObjPtr<SharedFolder> sharedFolder;
8932 /* Check for double entries. Not allowed! */
8933 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8934 if (SUCCEEDED(rc))
8935 return setError(VBOX_E_OBJECT_IN_USE,
8936 tr("Shared folder named '%s' already exists"),
8937 sf.strName.c_str());
8938
8939 /* Create the new shared folder. Don't break on error. This will be
8940 * reported when the machine starts. */
8941 sharedFolder.createObject();
8942 rc = sharedFolder->init(i_getMachine(),
8943 sf.strName,
8944 sf.strHostPath,
8945 RT_BOOL(sf.fWritable),
8946 RT_BOOL(sf.fAutoMount),
8947 sf.strAutoMountPoint,
8948 false /* fFailOnError */);
8949 if (FAILED(rc)) return rc;
8950 mHWData->mSharedFolders.push_back(sharedFolder);
8951 }
8952
8953 // Clipboard
8954 mHWData->mClipboardMode = data.clipboardMode;
8955
8956 // drag'n'drop
8957 mHWData->mDnDMode = data.dndMode;
8958
8959 // guest settings
8960 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8961
8962 // IO settings
8963 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8964 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8965
8966 // Host PCI devices
8967 for (settings::HostPCIDeviceAttachmentList::const_iterator
8968 it = data.pciAttachments.begin();
8969 it != data.pciAttachments.end();
8970 ++it)
8971 {
8972 const settings::HostPCIDeviceAttachment &hpda = *it;
8973 ComObjPtr<PCIDeviceAttachment> pda;
8974
8975 pda.createObject();
8976 pda->i_loadSettings(this, hpda);
8977 mHWData->mPCIDeviceAssignments.push_back(pda);
8978 }
8979
8980 /*
8981 * (The following isn't really real hardware, but it lives in HWData
8982 * for reasons of convenience.)
8983 */
8984
8985#ifdef VBOX_WITH_GUEST_PROPS
8986 /* Guest properties (optional) */
8987
8988 /* Only load transient guest properties for configs which have saved
8989 * state, because there shouldn't be any for powered off VMs. The same
8990 * logic applies for snapshots, as offline snapshots shouldn't have
8991 * any such properties. They confuse the code in various places.
8992 * Note: can't rely on the machine state, as it isn't set yet. */
8993 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
8994 /* apologies for the hacky unconst() usage, but this needs hacking
8995 * actually inconsistent settings into consistency, otherwise there
8996 * will be some corner cases where the inconsistency survives
8997 * surprisingly long without getting fixed, especially for snapshots
8998 * as there are no config changes. */
8999 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9000 for (settings::GuestPropertiesList::iterator
9001 it = llGuestProperties.begin();
9002 it != llGuestProperties.end();
9003 /*nothing*/)
9004 {
9005 const settings::GuestProperty &prop = *it;
9006 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
9007 GuestPropValidateFlags(prop.strFlags.c_str(), &fFlags);
9008 if ( fSkipTransientGuestProperties
9009 && ( fFlags & GUEST_PROP_F_TRANSIENT
9010 || fFlags & GUEST_PROP_F_TRANSRESET))
9011 {
9012 it = llGuestProperties.erase(it);
9013 continue;
9014 }
9015 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9016 mHWData->mGuestProperties[prop.strName] = property;
9017 ++it;
9018 }
9019#endif /* VBOX_WITH_GUEST_PROPS defined */
9020
9021 rc = i_loadDebugging(pDbg);
9022 if (FAILED(rc))
9023 return rc;
9024
9025 mHWData->mAutostart = *pAutostart;
9026
9027 /* default frontend */
9028 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9029 }
9030 catch (std::bad_alloc &)
9031 {
9032 return E_OUTOFMEMORY;
9033 }
9034
9035 AssertComRC(rc);
9036 return rc;
9037}
9038
9039/**
9040 * Called from i_loadHardware() to load the debugging settings of the
9041 * machine.
9042 *
9043 * @param pDbg Pointer to the settings.
9044 */
9045HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9046{
9047 mHWData->mDebugging = *pDbg;
9048 /* no more processing currently required, this will probably change. */
9049 return S_OK;
9050}
9051
9052/**
9053 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9054 *
9055 * @param data storage settings.
9056 * @param puuidRegistry media registry ID to set media to or NULL;
9057 * see Machine::i_loadMachineDataFromSettings()
9058 * @param puuidSnapshot snapshot ID
9059 * @return
9060 */
9061HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9062 const Guid *puuidRegistry,
9063 const Guid *puuidSnapshot)
9064{
9065 AssertReturn(!i_isSessionMachine(), E_FAIL);
9066
9067 HRESULT rc = S_OK;
9068
9069 for (settings::StorageControllersList::const_iterator
9070 it = data.llStorageControllers.begin();
9071 it != data.llStorageControllers.end();
9072 ++it)
9073 {
9074 const settings::StorageController &ctlData = *it;
9075
9076 ComObjPtr<StorageController> pCtl;
9077 /* Try to find one with the name first. */
9078 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9079 if (SUCCEEDED(rc))
9080 return setError(VBOX_E_OBJECT_IN_USE,
9081 tr("Storage controller named '%s' already exists"),
9082 ctlData.strName.c_str());
9083
9084 pCtl.createObject();
9085 rc = pCtl->init(this,
9086 ctlData.strName,
9087 ctlData.storageBus,
9088 ctlData.ulInstance,
9089 ctlData.fBootable);
9090 if (FAILED(rc)) return rc;
9091
9092 mStorageControllers->push_back(pCtl);
9093
9094 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9095 if (FAILED(rc)) return rc;
9096
9097 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9098 if (FAILED(rc)) return rc;
9099
9100 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9101 if (FAILED(rc)) return rc;
9102
9103 /* Load the attached devices now. */
9104 rc = i_loadStorageDevices(pCtl,
9105 ctlData,
9106 puuidRegistry,
9107 puuidSnapshot);
9108 if (FAILED(rc)) return rc;
9109 }
9110
9111 return S_OK;
9112}
9113
9114/**
9115 * Called from i_loadStorageControllers for a controller's devices.
9116 *
9117 * @param aStorageController
9118 * @param data
9119 * @param puuidRegistry media registry ID to set media to or NULL; see
9120 * Machine::i_loadMachineDataFromSettings()
9121 * @param puuidSnapshot pointer to the snapshot ID if this is a snapshot machine
9122 * @return
9123 */
9124HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9125 const settings::StorageController &data,
9126 const Guid *puuidRegistry,
9127 const Guid *puuidSnapshot)
9128{
9129 HRESULT rc = S_OK;
9130
9131 /* paranoia: detect duplicate attachments */
9132 for (settings::AttachedDevicesList::const_iterator
9133 it = data.llAttachedDevices.begin();
9134 it != data.llAttachedDevices.end();
9135 ++it)
9136 {
9137 const settings::AttachedDevice &ad = *it;
9138
9139 for (settings::AttachedDevicesList::const_iterator it2 = it;
9140 it2 != data.llAttachedDevices.end();
9141 ++it2)
9142 {
9143 if (it == it2)
9144 continue;
9145
9146 const settings::AttachedDevice &ad2 = *it2;
9147
9148 if ( ad.lPort == ad2.lPort
9149 && ad.lDevice == ad2.lDevice)
9150 {
9151 return setError(E_FAIL,
9152 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9153 aStorageController->i_getName().c_str(),
9154 ad.lPort,
9155 ad.lDevice,
9156 mUserData->s.strName.c_str());
9157 }
9158 }
9159 }
9160
9161 for (settings::AttachedDevicesList::const_iterator
9162 it = data.llAttachedDevices.begin();
9163 it != data.llAttachedDevices.end();
9164 ++it)
9165 {
9166 const settings::AttachedDevice &dev = *it;
9167 ComObjPtr<Medium> medium;
9168
9169 switch (dev.deviceType)
9170 {
9171 case DeviceType_Floppy:
9172 case DeviceType_DVD:
9173 if (dev.strHostDriveSrc.isNotEmpty())
9174 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9175 false /* fRefresh */, medium);
9176 else
9177 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9178 dev.uuid,
9179 false /* fRefresh */,
9180 false /* aSetError */,
9181 medium);
9182 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9183 // This is not an error. The host drive or UUID might have vanished, so just go
9184 // ahead without this removeable medium attachment
9185 rc = S_OK;
9186 break;
9187
9188 case DeviceType_HardDisk:
9189 {
9190 /* find a hard disk by UUID */
9191 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9192 if (FAILED(rc))
9193 {
9194 if (i_isSnapshotMachine())
9195 {
9196 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9197 // so the user knows that the bad disk is in a snapshot somewhere
9198 com::ErrorInfo info;
9199 return setError(E_FAIL,
9200 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9201 puuidSnapshot->raw(),
9202 info.getText().raw());
9203 }
9204 else
9205 return rc;
9206 }
9207
9208 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9209
9210 if (medium->i_getType() == MediumType_Immutable)
9211 {
9212 if (i_isSnapshotMachine())
9213 return setError(E_FAIL,
9214 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9215 "of the virtual machine '%s' ('%s')"),
9216 medium->i_getLocationFull().c_str(),
9217 dev.uuid.raw(),
9218 puuidSnapshot->raw(),
9219 mUserData->s.strName.c_str(),
9220 mData->m_strConfigFileFull.c_str());
9221
9222 return setError(E_FAIL,
9223 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9224 medium->i_getLocationFull().c_str(),
9225 dev.uuid.raw(),
9226 mUserData->s.strName.c_str(),
9227 mData->m_strConfigFileFull.c_str());
9228 }
9229
9230 if (medium->i_getType() == MediumType_MultiAttach)
9231 {
9232 if (i_isSnapshotMachine())
9233 return setError(E_FAIL,
9234 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9235 "of the virtual machine '%s' ('%s')"),
9236 medium->i_getLocationFull().c_str(),
9237 dev.uuid.raw(),
9238 puuidSnapshot->raw(),
9239 mUserData->s.strName.c_str(),
9240 mData->m_strConfigFileFull.c_str());
9241
9242 return setError(E_FAIL,
9243 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9244 medium->i_getLocationFull().c_str(),
9245 dev.uuid.raw(),
9246 mUserData->s.strName.c_str(),
9247 mData->m_strConfigFileFull.c_str());
9248 }
9249
9250 if ( !i_isSnapshotMachine()
9251 && medium->i_getChildren().size() != 0
9252 )
9253 return setError(E_FAIL,
9254 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9255 "because it has %d differencing child hard disks"),
9256 medium->i_getLocationFull().c_str(),
9257 dev.uuid.raw(),
9258 mUserData->s.strName.c_str(),
9259 mData->m_strConfigFileFull.c_str(),
9260 medium->i_getChildren().size());
9261
9262 if (i_findAttachment(*mMediumAttachments.data(),
9263 medium))
9264 return setError(E_FAIL,
9265 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9266 medium->i_getLocationFull().c_str(),
9267 dev.uuid.raw(),
9268 mUserData->s.strName.c_str(),
9269 mData->m_strConfigFileFull.c_str());
9270
9271 break;
9272 }
9273
9274 default:
9275 return setError(E_FAIL,
9276 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9277 medium->i_getLocationFull().c_str(),
9278 mUserData->s.strName.c_str(),
9279 mData->m_strConfigFileFull.c_str());
9280 }
9281
9282 if (FAILED(rc))
9283 break;
9284
9285 /* Bandwidth groups are loaded at this point. */
9286 ComObjPtr<BandwidthGroup> pBwGroup;
9287
9288 if (!dev.strBwGroup.isEmpty())
9289 {
9290 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9291 if (FAILED(rc))
9292 return setError(E_FAIL,
9293 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9294 medium->i_getLocationFull().c_str(),
9295 dev.strBwGroup.c_str(),
9296 mUserData->s.strName.c_str(),
9297 mData->m_strConfigFileFull.c_str());
9298 pBwGroup->i_reference();
9299 }
9300
9301 const Utf8Str controllerName = aStorageController->i_getName();
9302 ComObjPtr<MediumAttachment> pAttachment;
9303 pAttachment.createObject();
9304 rc = pAttachment->init(this,
9305 medium,
9306 controllerName,
9307 dev.lPort,
9308 dev.lDevice,
9309 dev.deviceType,
9310 false,
9311 dev.fPassThrough,
9312 dev.fTempEject,
9313 dev.fNonRotational,
9314 dev.fDiscard,
9315 dev.fHotPluggable,
9316 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9317 if (FAILED(rc)) break;
9318
9319 /* associate the medium with this machine and snapshot */
9320 if (!medium.isNull())
9321 {
9322 AutoCaller medCaller(medium);
9323 if (FAILED(medCaller.rc())) return medCaller.rc();
9324 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9325
9326 if (i_isSnapshotMachine())
9327 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9328 else
9329 rc = medium->i_addBackReference(mData->mUuid);
9330 /* If the medium->addBackReference fails it sets an appropriate
9331 * error message, so no need to do any guesswork here. */
9332
9333 if (puuidRegistry)
9334 // caller wants registry ID to be set on all attached media (OVF import case)
9335 medium->i_addRegistry(*puuidRegistry);
9336 }
9337
9338 if (FAILED(rc))
9339 break;
9340
9341 /* back up mMediumAttachments to let registeredInit() properly rollback
9342 * on failure (= limited accessibility) */
9343 i_setModified(IsModified_Storage);
9344 mMediumAttachments.backup();
9345 mMediumAttachments->push_back(pAttachment);
9346 }
9347
9348 return rc;
9349}
9350
9351/**
9352 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9353 *
9354 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9355 * @param aSnapshot where to return the found snapshot
9356 * @param aSetError true to set extended error info on failure
9357 */
9358HRESULT Machine::i_findSnapshotById(const Guid &aId,
9359 ComObjPtr<Snapshot> &aSnapshot,
9360 bool aSetError /* = false */)
9361{
9362 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9363
9364 if (!mData->mFirstSnapshot)
9365 {
9366 if (aSetError)
9367 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9368 return E_FAIL;
9369 }
9370
9371 if (aId.isZero())
9372 aSnapshot = mData->mFirstSnapshot;
9373 else
9374 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9375
9376 if (!aSnapshot)
9377 {
9378 if (aSetError)
9379 return setError(E_FAIL,
9380 tr("Could not find a snapshot with UUID {%s}"),
9381 aId.toString().c_str());
9382 return E_FAIL;
9383 }
9384
9385 return S_OK;
9386}
9387
9388/**
9389 * Returns the snapshot with the given name or fails of no such snapshot.
9390 *
9391 * @param strName snapshot name to find
9392 * @param aSnapshot where to return the found snapshot
9393 * @param aSetError true to set extended error info on failure
9394 */
9395HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9396 ComObjPtr<Snapshot> &aSnapshot,
9397 bool aSetError /* = false */)
9398{
9399 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9400
9401 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9402
9403 if (!mData->mFirstSnapshot)
9404 {
9405 if (aSetError)
9406 return setError(VBOX_E_OBJECT_NOT_FOUND,
9407 tr("This machine does not have any snapshots"));
9408 return VBOX_E_OBJECT_NOT_FOUND;
9409 }
9410
9411 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9412
9413 if (!aSnapshot)
9414 {
9415 if (aSetError)
9416 return setError(VBOX_E_OBJECT_NOT_FOUND,
9417 tr("Could not find a snapshot named '%s'"), strName.c_str());
9418 return VBOX_E_OBJECT_NOT_FOUND;
9419 }
9420
9421 return S_OK;
9422}
9423
9424/**
9425 * Returns a storage controller object with the given name.
9426 *
9427 * @param aName storage controller name to find
9428 * @param aStorageController where to return the found storage controller
9429 * @param aSetError true to set extended error info on failure
9430 */
9431HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9432 ComObjPtr<StorageController> &aStorageController,
9433 bool aSetError /* = false */)
9434{
9435 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9436
9437 for (StorageControllerList::const_iterator
9438 it = mStorageControllers->begin();
9439 it != mStorageControllers->end();
9440 ++it)
9441 {
9442 if ((*it)->i_getName() == aName)
9443 {
9444 aStorageController = (*it);
9445 return S_OK;
9446 }
9447 }
9448
9449 if (aSetError)
9450 return setError(VBOX_E_OBJECT_NOT_FOUND,
9451 tr("Could not find a storage controller named '%s'"),
9452 aName.c_str());
9453 return VBOX_E_OBJECT_NOT_FOUND;
9454}
9455
9456/**
9457 * Returns a USB controller object with the given name.
9458 *
9459 * @param aName USB controller name to find
9460 * @param aUSBController where to return the found USB controller
9461 * @param aSetError true to set extended error info on failure
9462 */
9463HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9464 ComObjPtr<USBController> &aUSBController,
9465 bool aSetError /* = false */)
9466{
9467 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9468
9469 for (USBControllerList::const_iterator
9470 it = mUSBControllers->begin();
9471 it != mUSBControllers->end();
9472 ++it)
9473 {
9474 if ((*it)->i_getName() == aName)
9475 {
9476 aUSBController = (*it);
9477 return S_OK;
9478 }
9479 }
9480
9481 if (aSetError)
9482 return setError(VBOX_E_OBJECT_NOT_FOUND,
9483 tr("Could not find a storage controller named '%s'"),
9484 aName.c_str());
9485 return VBOX_E_OBJECT_NOT_FOUND;
9486}
9487
9488/**
9489 * Returns the number of USB controller instance of the given type.
9490 *
9491 * @param enmType USB controller type.
9492 */
9493ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9494{
9495 ULONG cCtrls = 0;
9496
9497 for (USBControllerList::const_iterator
9498 it = mUSBControllers->begin();
9499 it != mUSBControllers->end();
9500 ++it)
9501 {
9502 if ((*it)->i_getControllerType() == enmType)
9503 cCtrls++;
9504 }
9505
9506 return cCtrls;
9507}
9508
9509HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9510 MediumAttachmentList &atts)
9511{
9512 AutoCaller autoCaller(this);
9513 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9514
9515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9516
9517 for (MediumAttachmentList::const_iterator
9518 it = mMediumAttachments->begin();
9519 it != mMediumAttachments->end();
9520 ++it)
9521 {
9522 const ComObjPtr<MediumAttachment> &pAtt = *it;
9523 // should never happen, but deal with NULL pointers in the list.
9524 AssertContinue(!pAtt.isNull());
9525
9526 // getControllerName() needs caller+read lock
9527 AutoCaller autoAttCaller(pAtt);
9528 if (FAILED(autoAttCaller.rc()))
9529 {
9530 atts.clear();
9531 return autoAttCaller.rc();
9532 }
9533 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9534
9535 if (pAtt->i_getControllerName() == aName)
9536 atts.push_back(pAtt);
9537 }
9538
9539 return S_OK;
9540}
9541
9542
9543/**
9544 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9545 * file if the machine name was changed and about creating a new settings file
9546 * if this is a new machine.
9547 *
9548 * @note Must be never called directly but only from #saveSettings().
9549 */
9550HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9551{
9552 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9553
9554 HRESULT rc = S_OK;
9555
9556 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9557
9558 /// @todo need to handle primary group change, too
9559
9560 /* attempt to rename the settings file if machine name is changed */
9561 if ( mUserData->s.fNameSync
9562 && mUserData.isBackedUp()
9563 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9564 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9565 )
9566 {
9567 bool dirRenamed = false;
9568 bool fileRenamed = false;
9569
9570 Utf8Str configFile, newConfigFile;
9571 Utf8Str configFilePrev, newConfigFilePrev;
9572 Utf8Str configDir, newConfigDir;
9573
9574 do
9575 {
9576 int vrc = VINF_SUCCESS;
9577
9578 Utf8Str name = mUserData.backedUpData()->s.strName;
9579 Utf8Str newName = mUserData->s.strName;
9580 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9581 if (group == "/")
9582 group.setNull();
9583 Utf8Str newGroup = mUserData->s.llGroups.front();
9584 if (newGroup == "/")
9585 newGroup.setNull();
9586
9587 configFile = mData->m_strConfigFileFull;
9588
9589 /* first, rename the directory if it matches the group and machine name */
9590 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9591 group.c_str(), RTPATH_DELIMITER, name.c_str());
9592 /** @todo hack, make somehow use of ComposeMachineFilename */
9593 if (mUserData->s.fDirectoryIncludesUUID)
9594 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9595 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9596 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9597 /** @todo hack, make somehow use of ComposeMachineFilename */
9598 if (mUserData->s.fDirectoryIncludesUUID)
9599 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9600 configDir = configFile;
9601 configDir.stripFilename();
9602 newConfigDir = configDir;
9603 if ( configDir.length() >= groupPlusName.length()
9604 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9605 groupPlusName.c_str()))
9606 {
9607 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9608 Utf8Str newConfigBaseDir(newConfigDir);
9609 newConfigDir.append(newGroupPlusName);
9610 /* consistency: use \ if appropriate on the platform */
9611 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9612 /* new dir and old dir cannot be equal here because of 'if'
9613 * above and because name != newName */
9614 Assert(configDir != newConfigDir);
9615 if (!fSettingsFileIsNew)
9616 {
9617 /* perform real rename only if the machine is not new */
9618 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9619 if ( vrc == VERR_FILE_NOT_FOUND
9620 || vrc == VERR_PATH_NOT_FOUND)
9621 {
9622 /* create the parent directory, then retry renaming */
9623 Utf8Str parent(newConfigDir);
9624 parent.stripFilename();
9625 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9626 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9627 }
9628 if (RT_FAILURE(vrc))
9629 {
9630 rc = setErrorBoth(E_FAIL, vrc,
9631 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9632 configDir.c_str(),
9633 newConfigDir.c_str(),
9634 vrc);
9635 break;
9636 }
9637 /* delete subdirectories which are no longer needed */
9638 Utf8Str dir(configDir);
9639 dir.stripFilename();
9640 while (dir != newConfigBaseDir && dir != ".")
9641 {
9642 vrc = RTDirRemove(dir.c_str());
9643 if (RT_FAILURE(vrc))
9644 break;
9645 dir.stripFilename();
9646 }
9647 dirRenamed = true;
9648 }
9649 }
9650
9651 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9652 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9653
9654 /* then try to rename the settings file itself */
9655 if (newConfigFile != configFile)
9656 {
9657 /* get the path to old settings file in renamed directory */
9658 configFile = Utf8StrFmt("%s%c%s",
9659 newConfigDir.c_str(),
9660 RTPATH_DELIMITER,
9661 RTPathFilename(configFile.c_str()));
9662 if (!fSettingsFileIsNew)
9663 {
9664 /* perform real rename only if the machine is not new */
9665 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9666 if (RT_FAILURE(vrc))
9667 {
9668 rc = setErrorBoth(E_FAIL, vrc,
9669 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9670 configFile.c_str(),
9671 newConfigFile.c_str(),
9672 vrc);
9673 break;
9674 }
9675 fileRenamed = true;
9676 configFilePrev = configFile;
9677 configFilePrev += "-prev";
9678 newConfigFilePrev = newConfigFile;
9679 newConfigFilePrev += "-prev";
9680 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9681 }
9682 }
9683
9684 // update m_strConfigFileFull amd mConfigFile
9685 mData->m_strConfigFileFull = newConfigFile;
9686 // compute the relative path too
9687 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9688
9689 // store the old and new so that VirtualBox::i_saveSettings() can update
9690 // the media registry
9691 if ( mData->mRegistered
9692 && (configDir != newConfigDir || configFile != newConfigFile))
9693 {
9694 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9695
9696 if (pfNeedsGlobalSaveSettings)
9697 *pfNeedsGlobalSaveSettings = true;
9698 }
9699
9700 // in the saved state file path, replace the old directory with the new directory
9701 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9702 {
9703 Utf8Str strStateFileName = mSSData->strStateFilePath.c_str() + configDir.length();
9704 mSSData->strStateFilePath = newConfigDir + strStateFileName;
9705 }
9706
9707 // and do the same thing for the saved state file paths of all the online snapshots
9708 if (mData->mFirstSnapshot)
9709 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9710 newConfigDir.c_str());
9711 }
9712 while (0);
9713
9714 if (FAILED(rc))
9715 {
9716 /* silently try to rename everything back */
9717 if (fileRenamed)
9718 {
9719 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9720 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9721 }
9722 if (dirRenamed)
9723 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9724 }
9725
9726 if (FAILED(rc)) return rc;
9727 }
9728
9729 if (fSettingsFileIsNew)
9730 {
9731 /* create a virgin config file */
9732 int vrc = VINF_SUCCESS;
9733
9734 /* ensure the settings directory exists */
9735 Utf8Str path(mData->m_strConfigFileFull);
9736 path.stripFilename();
9737 if (!RTDirExists(path.c_str()))
9738 {
9739 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9740 if (RT_FAILURE(vrc))
9741 {
9742 return setErrorBoth(E_FAIL, vrc,
9743 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9744 path.c_str(),
9745 vrc);
9746 }
9747 }
9748
9749 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9750 path = Utf8Str(mData->m_strConfigFileFull);
9751 RTFILE f = NIL_RTFILE;
9752 vrc = RTFileOpen(&f, path.c_str(),
9753 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9754 if (RT_FAILURE(vrc))
9755 return setErrorBoth(E_FAIL, vrc,
9756 tr("Could not create the settings file '%s' (%Rrc)"),
9757 path.c_str(),
9758 vrc);
9759 RTFileClose(f);
9760 }
9761
9762 return rc;
9763}
9764
9765/**
9766 * Saves and commits machine data, user data and hardware data.
9767 *
9768 * Note that on failure, the data remains uncommitted.
9769 *
9770 * @a aFlags may combine the following flags:
9771 *
9772 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9773 * Used when saving settings after an operation that makes them 100%
9774 * correspond to the settings from the current snapshot.
9775 * - SaveS_Force: settings will be saved without doing a deep compare of the
9776 * settings structures. This is used when this is called because snapshots
9777 * have changed to avoid the overhead of the deep compare.
9778 *
9779 * @note Must be called from under this object's write lock. Locks children for
9780 * writing.
9781 *
9782 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9783 * initialized to false and that will be set to true by this function if
9784 * the caller must invoke VirtualBox::i_saveSettings() because the global
9785 * settings have changed. This will happen if a machine rename has been
9786 * saved and the global machine and media registries will therefore need
9787 * updating.
9788 * @param aFlags Flags.
9789 */
9790HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9791 int aFlags /*= 0*/)
9792{
9793 LogFlowThisFuncEnter();
9794
9795 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9796
9797 /* make sure child objects are unable to modify the settings while we are
9798 * saving them */
9799 i_ensureNoStateDependencies();
9800
9801 AssertReturn(!i_isSnapshotMachine(),
9802 E_FAIL);
9803
9804 if (!mData->mAccessible)
9805 return setError(VBOX_E_INVALID_VM_STATE,
9806 tr("The machine is not accessible, so cannot save settings"));
9807
9808 HRESULT rc = S_OK;
9809 bool fNeedsWrite = false;
9810
9811 /* First, prepare to save settings. It will care about renaming the
9812 * settings directory and file if the machine name was changed and about
9813 * creating a new settings file if this is a new machine. */
9814 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9815 if (FAILED(rc)) return rc;
9816
9817 // keep a pointer to the current settings structures
9818 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9819 settings::MachineConfigFile *pNewConfig = NULL;
9820
9821 try
9822 {
9823 // make a fresh one to have everyone write stuff into
9824 pNewConfig = new settings::MachineConfigFile(NULL);
9825 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9826
9827 // now go and copy all the settings data from COM to the settings structures
9828 // (this calls i_saveSettings() on all the COM objects in the machine)
9829 i_copyMachineDataToSettings(*pNewConfig);
9830
9831 if (aFlags & SaveS_ResetCurStateModified)
9832 {
9833 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9834 mData->mCurrentStateModified = FALSE;
9835 fNeedsWrite = true; // always, no need to compare
9836 }
9837 else if (aFlags & SaveS_Force)
9838 {
9839 fNeedsWrite = true; // always, no need to compare
9840 }
9841 else
9842 {
9843 if (!mData->mCurrentStateModified)
9844 {
9845 // do a deep compare of the settings that we just saved with the settings
9846 // previously stored in the config file; this invokes MachineConfigFile::operator==
9847 // which does a deep compare of all the settings, which is expensive but less expensive
9848 // than writing out XML in vain
9849 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9850
9851 // could still be modified if any settings changed
9852 mData->mCurrentStateModified = fAnySettingsChanged;
9853
9854 fNeedsWrite = fAnySettingsChanged;
9855 }
9856 else
9857 fNeedsWrite = true;
9858 }
9859
9860 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9861
9862 if (fNeedsWrite)
9863 // now spit it all out!
9864 pNewConfig->write(mData->m_strConfigFileFull);
9865
9866 mData->pMachineConfigFile = pNewConfig;
9867 delete pOldConfig;
9868 i_commit();
9869
9870 // after saving settings, we are no longer different from the XML on disk
9871 mData->flModifications = 0;
9872 }
9873 catch (HRESULT err)
9874 {
9875 // we assume that error info is set by the thrower
9876 rc = err;
9877
9878 // restore old config
9879 delete pNewConfig;
9880 mData->pMachineConfigFile = pOldConfig;
9881 }
9882 catch (...)
9883 {
9884 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9885 }
9886
9887 if (fNeedsWrite)
9888 {
9889 /* Fire the data change event, even on failure (since we've already
9890 * committed all data). This is done only for SessionMachines because
9891 * mutable Machine instances are always not registered (i.e. private
9892 * to the client process that creates them) and thus don't need to
9893 * inform callbacks. */
9894 if (i_isSessionMachine())
9895 mParent->i_onMachineDataChange(mData->mUuid);
9896 }
9897
9898 LogFlowThisFunc(("rc=%08X\n", rc));
9899 LogFlowThisFuncLeave();
9900 return rc;
9901}
9902
9903/**
9904 * Implementation for saving the machine settings into the given
9905 * settings::MachineConfigFile instance. This copies machine extradata
9906 * from the previous machine config file in the instance data, if any.
9907 *
9908 * This gets called from two locations:
9909 *
9910 * -- Machine::i_saveSettings(), during the regular XML writing;
9911 *
9912 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9913 * exported to OVF and we write the VirtualBox proprietary XML
9914 * into a <vbox:Machine> tag.
9915 *
9916 * This routine fills all the fields in there, including snapshots, *except*
9917 * for the following:
9918 *
9919 * -- fCurrentStateModified. There is some special logic associated with that.
9920 *
9921 * The caller can then call MachineConfigFile::write() or do something else
9922 * with it.
9923 *
9924 * Caller must hold the machine lock!
9925 *
9926 * This throws XML errors and HRESULT, so the caller must have a catch block!
9927 */
9928void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9929{
9930 // deep copy extradata, being extra careful with self assignment (the STL
9931 // map assignment on Mac OS X clang based Xcode isn't checking)
9932 if (&config != mData->pMachineConfigFile)
9933 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9934
9935 config.uuid = mData->mUuid;
9936
9937 // copy name, description, OS type, teleport, UTC etc.
9938 config.machineUserData = mUserData->s;
9939
9940 if ( mData->mMachineState == MachineState_Saved
9941 || mData->mMachineState == MachineState_Restoring
9942 // when doing certain snapshot operations we may or may not have
9943 // a saved state in the current state, so keep everything as is
9944 || ( ( mData->mMachineState == MachineState_Snapshotting
9945 || mData->mMachineState == MachineState_DeletingSnapshot
9946 || mData->mMachineState == MachineState_RestoringSnapshot)
9947 && (!mSSData->strStateFilePath.isEmpty())
9948 )
9949 )
9950 {
9951 Assert(!mSSData->strStateFilePath.isEmpty());
9952 /* try to make the file name relative to the settings file dir */
9953 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9954 }
9955 else
9956 {
9957 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
9958 config.strStateFile.setNull();
9959 }
9960
9961 if (mData->mCurrentSnapshot)
9962 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
9963 else
9964 config.uuidCurrentSnapshot.clear();
9965
9966 config.timeLastStateChange = mData->mLastStateChange;
9967 config.fAborted = (mData->mMachineState == MachineState_Aborted);
9968 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
9969
9970 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
9971 if (FAILED(rc)) throw rc;
9972
9973 // save machine's media registry if this is VirtualBox 4.0 or later
9974 if (config.canHaveOwnMediaRegistry())
9975 {
9976 // determine machine folder
9977 Utf8Str strMachineFolder = i_getSettingsFileFull();
9978 strMachineFolder.stripFilename();
9979 mParent->i_saveMediaRegistry(config.mediaRegistry,
9980 i_getId(), // only media with registry ID == machine UUID
9981 strMachineFolder);
9982 // this throws HRESULT
9983 }
9984
9985 // save snapshots
9986 rc = i_saveAllSnapshots(config);
9987 if (FAILED(rc)) throw rc;
9988}
9989
9990/**
9991 * Saves all snapshots of the machine into the given machine config file. Called
9992 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
9993 * @param config
9994 * @return
9995 */
9996HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
9997{
9998 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9999
10000 HRESULT rc = S_OK;
10001
10002 try
10003 {
10004 config.llFirstSnapshot.clear();
10005
10006 if (mData->mFirstSnapshot)
10007 {
10008 // the settings use a list for "the first snapshot"
10009 config.llFirstSnapshot.push_back(settings::Snapshot::Empty);
10010
10011 // get reference to the snapshot on the list and work on that
10012 // element straight in the list to avoid excessive copying later
10013 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10014 if (FAILED(rc)) throw rc;
10015 }
10016
10017// if (mType == IsSessionMachine)
10018// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10019
10020 }
10021 catch (HRESULT err)
10022 {
10023 /* we assume that error info is set by the thrower */
10024 rc = err;
10025 }
10026 catch (...)
10027 {
10028 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10029 }
10030
10031 return rc;
10032}
10033
10034/**
10035 * Saves the VM hardware configuration. It is assumed that the
10036 * given node is empty.
10037 *
10038 * @param data Reference to the settings object for the hardware config.
10039 * @param pDbg Pointer to the settings object for the debugging config
10040 * which happens to live in mHWData.
10041 * @param pAutostart Pointer to the settings object for the autostart config
10042 * which happens to live in mHWData.
10043 */
10044HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10045 settings::Autostart *pAutostart)
10046{
10047 HRESULT rc = S_OK;
10048
10049 try
10050 {
10051 /* The hardware version attribute (optional).
10052 Automatically upgrade from 1 to current default hardware version
10053 when there is no saved state. (ugly!) */
10054 if ( mHWData->mHWVersion == "1"
10055 && mSSData->strStateFilePath.isEmpty()
10056 )
10057 mHWData->mHWVersion = Utf8StrFmt("%d", SchemaDefs::DefaultHardwareVersion);
10058
10059 data.strVersion = mHWData->mHWVersion;
10060 data.uuid = mHWData->mHardwareUUID;
10061
10062 // CPU
10063 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10064 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10065 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10066 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10067 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10068 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10069 data.fUseNativeApi = !!mHWData->mHWVirtExUseNativeApi;
10070 data.fPAE = !!mHWData->mPAEEnabled;
10071 data.enmLongMode = mHWData->mLongMode;
10072 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10073 data.fAPIC = !!mHWData->mAPIC;
10074 data.fX2APIC = !!mHWData->mX2APIC;
10075 data.fIBPBOnVMExit = !!mHWData->mIBPBOnVMExit;
10076 data.fIBPBOnVMEntry = !!mHWData->mIBPBOnVMEntry;
10077 data.fSpecCtrl = !!mHWData->mSpecCtrl;
10078 data.fSpecCtrlByHost = !!mHWData->mSpecCtrlByHost;
10079 data.fL1DFlushOnSched = !!mHWData->mL1DFlushOnSched;
10080 data.fL1DFlushOnVMEntry = !!mHWData->mL1DFlushOnVMEntry;
10081 data.fMDSClearOnSched = !!mHWData->mMDSClearOnSched;
10082 data.fMDSClearOnVMEntry = !!mHWData->mMDSClearOnVMEntry;
10083 data.fNestedHWVirt = !!mHWData->mNestedHWVirt;
10084 data.cCPUs = mHWData->mCPUCount;
10085 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10086 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10087 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10088 data.strCpuProfile = mHWData->mCpuProfile;
10089
10090 data.llCpus.clear();
10091 if (data.fCpuHotPlug)
10092 {
10093 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10094 {
10095 if (mHWData->mCPUAttached[idx])
10096 {
10097 settings::Cpu cpu;
10098 cpu.ulId = idx;
10099 data.llCpus.push_back(cpu);
10100 }
10101 }
10102 }
10103
10104 /* Standard and Extended CPUID leafs. */
10105 data.llCpuIdLeafs.clear();
10106 data.llCpuIdLeafs = mHWData->mCpuIdLeafList;
10107
10108 // memory
10109 data.ulMemorySizeMB = mHWData->mMemorySize;
10110 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10111
10112 // firmware
10113 data.firmwareType = mHWData->mFirmwareType;
10114
10115 // HID
10116 data.pointingHIDType = mHWData->mPointingHIDType;
10117 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10118
10119 // chipset
10120 data.chipsetType = mHWData->mChipsetType;
10121
10122 // paravirt
10123 data.paravirtProvider = mHWData->mParavirtProvider;
10124 data.strParavirtDebug = mHWData->mParavirtDebug;
10125
10126 // emulated USB card reader
10127 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10128
10129 // HPET
10130 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10131
10132 // boot order
10133 data.mapBootOrder.clear();
10134 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10135 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10136
10137 // display
10138 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10139 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10140 data.cMonitors = mHWData->mMonitorCount;
10141 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10142 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10143
10144 /* VRDEServer settings (optional) */
10145 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10146 if (FAILED(rc)) throw rc;
10147
10148 /* BIOS settings (required) */
10149 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10150 if (FAILED(rc)) throw rc;
10151
10152 /* Recording settings (required) */
10153 rc = mRecordingSettings->i_saveSettings(data.recordingSettings);
10154 if (FAILED(rc)) throw rc;
10155
10156 /* USB Controller (required) */
10157 data.usbSettings.llUSBControllers.clear();
10158 for (USBControllerList::const_iterator
10159 it = mUSBControllers->begin();
10160 it != mUSBControllers->end();
10161 ++it)
10162 {
10163 ComObjPtr<USBController> ctrl = *it;
10164 settings::USBController settingsCtrl;
10165
10166 settingsCtrl.strName = ctrl->i_getName();
10167 settingsCtrl.enmType = ctrl->i_getControllerType();
10168
10169 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10170 }
10171
10172 /* USB device filters (required) */
10173 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10174 if (FAILED(rc)) throw rc;
10175
10176 /* Network adapters (required) */
10177 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10178 data.llNetworkAdapters.clear();
10179 /* Write out only the nominal number of network adapters for this
10180 * chipset type. Since Machine::commit() hasn't been called there
10181 * may be extra NIC settings in the vector. */
10182 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10183 {
10184 settings::NetworkAdapter nic;
10185 nic.ulSlot = (uint32_t)slot;
10186 /* paranoia check... must not be NULL, but must not crash either. */
10187 if (mNetworkAdapters[slot])
10188 {
10189 if (mNetworkAdapters[slot]->i_hasDefaults())
10190 continue;
10191
10192 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10193 if (FAILED(rc)) throw rc;
10194
10195 data.llNetworkAdapters.push_back(nic);
10196 }
10197 }
10198
10199 /* Serial ports */
10200 data.llSerialPorts.clear();
10201 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10202 {
10203 if (mSerialPorts[slot]->i_hasDefaults())
10204 continue;
10205
10206 settings::SerialPort s;
10207 s.ulSlot = slot;
10208 rc = mSerialPorts[slot]->i_saveSettings(s);
10209 if (FAILED(rc)) return rc;
10210
10211 data.llSerialPorts.push_back(s);
10212 }
10213
10214 /* Parallel ports */
10215 data.llParallelPorts.clear();
10216 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10217 {
10218 if (mParallelPorts[slot]->i_hasDefaults())
10219 continue;
10220
10221 settings::ParallelPort p;
10222 p.ulSlot = slot;
10223 rc = mParallelPorts[slot]->i_saveSettings(p);
10224 if (FAILED(rc)) return rc;
10225
10226 data.llParallelPorts.push_back(p);
10227 }
10228
10229 /* Audio adapter */
10230 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10231 if (FAILED(rc)) return rc;
10232
10233 rc = i_saveStorageControllers(data.storage);
10234 if (FAILED(rc)) return rc;
10235
10236 /* Shared folders */
10237 data.llSharedFolders.clear();
10238 for (HWData::SharedFolderList::const_iterator
10239 it = mHWData->mSharedFolders.begin();
10240 it != mHWData->mSharedFolders.end();
10241 ++it)
10242 {
10243 SharedFolder *pSF = *it;
10244 AutoCaller sfCaller(pSF);
10245 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10246 settings::SharedFolder sf;
10247 sf.strName = pSF->i_getName();
10248 sf.strHostPath = pSF->i_getHostPath();
10249 sf.fWritable = !!pSF->i_isWritable();
10250 sf.fAutoMount = !!pSF->i_isAutoMounted();
10251 sf.strAutoMountPoint = pSF->i_getAutoMountPoint();
10252
10253 data.llSharedFolders.push_back(sf);
10254 }
10255
10256 // clipboard
10257 data.clipboardMode = mHWData->mClipboardMode;
10258
10259 // drag'n'drop
10260 data.dndMode = mHWData->mDnDMode;
10261
10262 /* Guest */
10263 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10264
10265 // IO settings
10266 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10267 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10268
10269 /* BandwidthControl (required) */
10270 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10271 if (FAILED(rc)) throw rc;
10272
10273 /* Host PCI devices */
10274 data.pciAttachments.clear();
10275 for (HWData::PCIDeviceAssignmentList::const_iterator
10276 it = mHWData->mPCIDeviceAssignments.begin();
10277 it != mHWData->mPCIDeviceAssignments.end();
10278 ++it)
10279 {
10280 ComObjPtr<PCIDeviceAttachment> pda = *it;
10281 settings::HostPCIDeviceAttachment hpda;
10282
10283 rc = pda->i_saveSettings(hpda);
10284 if (FAILED(rc)) throw rc;
10285
10286 data.pciAttachments.push_back(hpda);
10287 }
10288
10289 // guest properties
10290 data.llGuestProperties.clear();
10291#ifdef VBOX_WITH_GUEST_PROPS
10292 for (HWData::GuestPropertyMap::const_iterator
10293 it = mHWData->mGuestProperties.begin();
10294 it != mHWData->mGuestProperties.end();
10295 ++it)
10296 {
10297 HWData::GuestProperty property = it->second;
10298
10299 /* Remove transient guest properties at shutdown unless we
10300 * are saving state. Note that restoring snapshot intentionally
10301 * keeps them, they will be removed if appropriate once the final
10302 * machine state is set (as crashes etc. need to work). */
10303 if ( ( mData->mMachineState == MachineState_PoweredOff
10304 || mData->mMachineState == MachineState_Aborted
10305 || mData->mMachineState == MachineState_Teleported)
10306 && (property.mFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET)))
10307 continue;
10308 settings::GuestProperty prop; /// @todo r=bird: some excellent variable name choices here: 'prop' and 'property'; No 'const' clue either.
10309 prop.strName = it->first;
10310 prop.strValue = property.strValue;
10311 prop.timestamp = property.mTimestamp;
10312 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
10313 GuestPropWriteFlags(property.mFlags, szFlags);
10314 prop.strFlags = szFlags;
10315
10316 data.llGuestProperties.push_back(prop);
10317 }
10318
10319 /* I presume this doesn't require a backup(). */
10320 mData->mGuestPropertiesModified = FALSE;
10321#endif /* VBOX_WITH_GUEST_PROPS defined */
10322
10323 *pDbg = mHWData->mDebugging;
10324 *pAutostart = mHWData->mAutostart;
10325
10326 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10327 }
10328 catch (std::bad_alloc &)
10329 {
10330 return E_OUTOFMEMORY;
10331 }
10332
10333 AssertComRC(rc);
10334 return rc;
10335}
10336
10337/**
10338 * Saves the storage controller configuration.
10339 *
10340 * @param data storage settings.
10341 */
10342HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10343{
10344 data.llStorageControllers.clear();
10345
10346 for (StorageControllerList::const_iterator
10347 it = mStorageControllers->begin();
10348 it != mStorageControllers->end();
10349 ++it)
10350 {
10351 HRESULT rc;
10352 ComObjPtr<StorageController> pCtl = *it;
10353
10354 settings::StorageController ctl;
10355 ctl.strName = pCtl->i_getName();
10356 ctl.controllerType = pCtl->i_getControllerType();
10357 ctl.storageBus = pCtl->i_getStorageBus();
10358 ctl.ulInstance = pCtl->i_getInstance();
10359 ctl.fBootable = pCtl->i_getBootable();
10360
10361 /* Save the port count. */
10362 ULONG portCount;
10363 rc = pCtl->COMGETTER(PortCount)(&portCount);
10364 ComAssertComRCRet(rc, rc);
10365 ctl.ulPortCount = portCount;
10366
10367 /* Save fUseHostIOCache */
10368 BOOL fUseHostIOCache;
10369 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10370 ComAssertComRCRet(rc, rc);
10371 ctl.fUseHostIOCache = !!fUseHostIOCache;
10372
10373 /* save the devices now. */
10374 rc = i_saveStorageDevices(pCtl, ctl);
10375 ComAssertComRCRet(rc, rc);
10376
10377 data.llStorageControllers.push_back(ctl);
10378 }
10379
10380 return S_OK;
10381}
10382
10383/**
10384 * Saves the hard disk configuration.
10385 */
10386HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10387 settings::StorageController &data)
10388{
10389 MediumAttachmentList atts;
10390
10391 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10392 if (FAILED(rc)) return rc;
10393
10394 data.llAttachedDevices.clear();
10395 for (MediumAttachmentList::const_iterator
10396 it = atts.begin();
10397 it != atts.end();
10398 ++it)
10399 {
10400 settings::AttachedDevice dev;
10401 IMediumAttachment *iA = *it;
10402 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10403 Medium *pMedium = pAttach->i_getMedium();
10404
10405 dev.deviceType = pAttach->i_getType();
10406 dev.lPort = pAttach->i_getPort();
10407 dev.lDevice = pAttach->i_getDevice();
10408 dev.fPassThrough = pAttach->i_getPassthrough();
10409 dev.fHotPluggable = pAttach->i_getHotPluggable();
10410 if (pMedium)
10411 {
10412 if (pMedium->i_isHostDrive())
10413 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10414 else
10415 dev.uuid = pMedium->i_getId();
10416 dev.fTempEject = pAttach->i_getTempEject();
10417 dev.fNonRotational = pAttach->i_getNonRotational();
10418 dev.fDiscard = pAttach->i_getDiscard();
10419 }
10420
10421 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10422
10423 data.llAttachedDevices.push_back(dev);
10424 }
10425
10426 return S_OK;
10427}
10428
10429/**
10430 * Saves machine state settings as defined by aFlags
10431 * (SaveSTS_* values).
10432 *
10433 * @param aFlags Combination of SaveSTS_* flags.
10434 *
10435 * @note Locks objects for writing.
10436 */
10437HRESULT Machine::i_saveStateSettings(int aFlags)
10438{
10439 if (aFlags == 0)
10440 return S_OK;
10441
10442 AutoCaller autoCaller(this);
10443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10444
10445 /* This object's write lock is also necessary to serialize file access
10446 * (prevent concurrent reads and writes) */
10447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10448
10449 HRESULT rc = S_OK;
10450
10451 Assert(mData->pMachineConfigFile);
10452
10453 try
10454 {
10455 if (aFlags & SaveSTS_CurStateModified)
10456 mData->pMachineConfigFile->fCurrentStateModified = true;
10457
10458 if (aFlags & SaveSTS_StateFilePath)
10459 {
10460 if (!mSSData->strStateFilePath.isEmpty())
10461 /* try to make the file name relative to the settings file dir */
10462 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10463 else
10464 mData->pMachineConfigFile->strStateFile.setNull();
10465 }
10466
10467 if (aFlags & SaveSTS_StateTimeStamp)
10468 {
10469 Assert( mData->mMachineState != MachineState_Aborted
10470 || mSSData->strStateFilePath.isEmpty());
10471
10472 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10473
10474 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10475/// @todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10476 }
10477
10478 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10479 }
10480 catch (...)
10481 {
10482 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10483 }
10484
10485 return rc;
10486}
10487
10488/**
10489 * Ensures that the given medium is added to a media registry. If this machine
10490 * was created with 4.0 or later, then the machine registry is used. Otherwise
10491 * the global VirtualBox media registry is used.
10492 *
10493 * Caller must NOT hold machine lock, media tree or any medium locks!
10494 *
10495 * @param pMedium
10496 */
10497void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10498{
10499 /* Paranoia checks: do not hold machine or media tree locks. */
10500 AssertReturnVoid(!isWriteLockOnCurrentThread());
10501 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10502
10503 ComObjPtr<Medium> pBase;
10504 {
10505 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10506 pBase = pMedium->i_getBase();
10507 }
10508
10509 /* Paranoia checks: do not hold medium locks. */
10510 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10511 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10512
10513 // decide which medium registry to use now that the medium is attached:
10514 Guid uuid;
10515 bool fCanHaveOwnMediaRegistry = mData->pMachineConfigFile->canHaveOwnMediaRegistry();
10516 if (fCanHaveOwnMediaRegistry)
10517 // machine XML is VirtualBox 4.0 or higher:
10518 uuid = i_getId(); // machine UUID
10519 else
10520 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10521
10522 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistry(mParent->i_getGlobalRegistryId()))
10523 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10524 if (pMedium->i_addRegistry(uuid))
10525 mParent->i_markRegistryModified(uuid);
10526
10527 /* For more complex hard disk structures it can happen that the base
10528 * medium isn't yet associated with any medium registry. Do that now. */
10529 if (pMedium != pBase)
10530 {
10531 /* Tree lock needed by Medium::addRegistry when recursing. */
10532 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10533 if (fCanHaveOwnMediaRegistry && pMedium->i_removeRegistryRecursive(mParent->i_getGlobalRegistryId()))
10534 {
10535 treeLock.release();
10536 mParent->i_markRegistryModified(mParent->i_getGlobalRegistryId());
10537 treeLock.acquire();
10538 }
10539 if (pBase->i_addRegistryRecursive(uuid))
10540 {
10541 treeLock.release();
10542 mParent->i_markRegistryModified(uuid);
10543 }
10544 }
10545}
10546
10547/**
10548 * Creates differencing hard disks for all normal hard disks attached to this
10549 * machine and a new set of attachments to refer to created disks.
10550 *
10551 * Used when taking a snapshot or when deleting the current state. Gets called
10552 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10553 *
10554 * This method assumes that mMediumAttachments contains the original hard disk
10555 * attachments it needs to create diffs for. On success, these attachments will
10556 * be replaced with the created diffs.
10557 *
10558 * Attachments with non-normal hard disks are left as is.
10559 *
10560 * If @a aOnline is @c false then the original hard disks that require implicit
10561 * diffs will be locked for reading. Otherwise it is assumed that they are
10562 * already locked for writing (when the VM was started). Note that in the latter
10563 * case it is responsibility of the caller to lock the newly created diffs for
10564 * writing if this method succeeds.
10565 *
10566 * @param aProgress Progress object to run (must contain at least as
10567 * many operations left as the number of hard disks
10568 * attached).
10569 * @param aWeight Weight of this operation.
10570 * @param aOnline Whether the VM was online prior to this operation.
10571 *
10572 * @note The progress object is not marked as completed, neither on success nor
10573 * on failure. This is a responsibility of the caller.
10574 *
10575 * @note Locks this object and the media tree for writing.
10576 */
10577HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10578 ULONG aWeight,
10579 bool aOnline)
10580{
10581 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10582
10583 ComPtr<IInternalProgressControl> pProgressControl(aProgress);
10584 AssertReturn(!!pProgressControl, E_INVALIDARG);
10585
10586 AutoCaller autoCaller(this);
10587 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10588
10589 AutoMultiWriteLock2 alock(this->lockHandle(),
10590 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10591
10592 /* must be in a protective state because we release the lock below */
10593 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10594 || mData->mMachineState == MachineState_OnlineSnapshotting
10595 || mData->mMachineState == MachineState_LiveSnapshotting
10596 || mData->mMachineState == MachineState_RestoringSnapshot
10597 || mData->mMachineState == MachineState_DeletingSnapshot
10598 , E_FAIL);
10599
10600 HRESULT rc = S_OK;
10601
10602 // use appropriate locked media map (online or offline)
10603 MediumLockListMap lockedMediaOffline;
10604 MediumLockListMap *lockedMediaMap;
10605 if (aOnline)
10606 lockedMediaMap = &mData->mSession.mLockedMedia;
10607 else
10608 lockedMediaMap = &lockedMediaOffline;
10609
10610 try
10611 {
10612 if (!aOnline)
10613 {
10614 /* lock all attached hard disks early to detect "in use"
10615 * situations before creating actual diffs */
10616 for (MediumAttachmentList::const_iterator
10617 it = mMediumAttachments->begin();
10618 it != mMediumAttachments->end();
10619 ++it)
10620 {
10621 MediumAttachment *pAtt = *it;
10622 if (pAtt->i_getType() == DeviceType_HardDisk)
10623 {
10624 Medium *pMedium = pAtt->i_getMedium();
10625 Assert(pMedium);
10626
10627 MediumLockList *pMediumLockList(new MediumLockList());
10628 alock.release();
10629 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10630 NULL /* pToLockWrite */,
10631 false /* fMediumLockWriteAll */,
10632 NULL,
10633 *pMediumLockList);
10634 alock.acquire();
10635 if (FAILED(rc))
10636 {
10637 delete pMediumLockList;
10638 throw rc;
10639 }
10640 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10641 if (FAILED(rc))
10642 {
10643 throw setError(rc,
10644 tr("Collecting locking information for all attached media failed"));
10645 }
10646 }
10647 }
10648
10649 /* Now lock all media. If this fails, nothing is locked. */
10650 alock.release();
10651 rc = lockedMediaMap->Lock();
10652 alock.acquire();
10653 if (FAILED(rc))
10654 {
10655 throw setError(rc,
10656 tr("Locking of attached media failed"));
10657 }
10658 }
10659
10660 /* remember the current list (note that we don't use backup() since
10661 * mMediumAttachments may be already backed up) */
10662 MediumAttachmentList atts = *mMediumAttachments.data();
10663
10664 /* start from scratch */
10665 mMediumAttachments->clear();
10666
10667 /* go through remembered attachments and create diffs for normal hard
10668 * disks and attach them */
10669 for (MediumAttachmentList::const_iterator
10670 it = atts.begin();
10671 it != atts.end();
10672 ++it)
10673 {
10674 MediumAttachment *pAtt = *it;
10675
10676 DeviceType_T devType = pAtt->i_getType();
10677 Medium *pMedium = pAtt->i_getMedium();
10678
10679 if ( devType != DeviceType_HardDisk
10680 || pMedium == NULL
10681 || pMedium->i_getType() != MediumType_Normal)
10682 {
10683 /* copy the attachment as is */
10684
10685 /** @todo the progress object created in SessionMachine::TakeSnaphot
10686 * only expects operations for hard disks. Later other
10687 * device types need to show up in the progress as well. */
10688 if (devType == DeviceType_HardDisk)
10689 {
10690 if (pMedium == NULL)
10691 pProgressControl->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10692 aWeight); // weight
10693 else
10694 pProgressControl->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10695 pMedium->i_getBase()->i_getName().c_str()).raw(),
10696 aWeight); // weight
10697 }
10698
10699 mMediumAttachments->push_back(pAtt);
10700 continue;
10701 }
10702
10703 /* need a diff */
10704 pProgressControl->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10705 pMedium->i_getBase()->i_getName().c_str()).raw(),
10706 aWeight); // weight
10707
10708 Utf8Str strFullSnapshotFolder;
10709 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10710
10711 ComObjPtr<Medium> diff;
10712 diff.createObject();
10713 // store the diff in the same registry as the parent
10714 // (this cannot fail here because we can't create implicit diffs for
10715 // unregistered images)
10716 Guid uuidRegistryParent;
10717 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10718 Assert(fInRegistry); NOREF(fInRegistry);
10719 rc = diff->init(mParent,
10720 pMedium->i_getPreferredDiffFormat(),
10721 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10722 uuidRegistryParent,
10723 DeviceType_HardDisk);
10724 if (FAILED(rc)) throw rc;
10725
10726 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10727 * the push_back? Looks like we're going to release medium with the
10728 * wrong kind of lock (general issue with if we fail anywhere at all)
10729 * and an orphaned VDI in the snapshots folder. */
10730
10731 /* update the appropriate lock list */
10732 MediumLockList *pMediumLockList;
10733 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10734 AssertComRCThrowRC(rc);
10735 if (aOnline)
10736 {
10737 alock.release();
10738 /* The currently attached medium will be read-only, change
10739 * the lock type to read. */
10740 rc = pMediumLockList->Update(pMedium, false);
10741 alock.acquire();
10742 AssertComRCThrowRC(rc);
10743 }
10744
10745 /* release the locks before the potentially lengthy operation */
10746 alock.release();
10747 rc = pMedium->i_createDiffStorage(diff,
10748 pMedium->i_getPreferredDiffVariant(),
10749 pMediumLockList,
10750 NULL /* aProgress */,
10751 true /* aWait */,
10752 false /* aNotify */);
10753 alock.acquire();
10754 if (FAILED(rc)) throw rc;
10755
10756 /* actual lock list update is done in Machine::i_commitMedia */
10757
10758 rc = diff->i_addBackReference(mData->mUuid);
10759 AssertComRCThrowRC(rc);
10760
10761 /* add a new attachment */
10762 ComObjPtr<MediumAttachment> attachment;
10763 attachment.createObject();
10764 rc = attachment->init(this,
10765 diff,
10766 pAtt->i_getControllerName(),
10767 pAtt->i_getPort(),
10768 pAtt->i_getDevice(),
10769 DeviceType_HardDisk,
10770 true /* aImplicit */,
10771 false /* aPassthrough */,
10772 false /* aTempEject */,
10773 pAtt->i_getNonRotational(),
10774 pAtt->i_getDiscard(),
10775 pAtt->i_getHotPluggable(),
10776 pAtt->i_getBandwidthGroup());
10777 if (FAILED(rc)) throw rc;
10778
10779 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10780 AssertComRCThrowRC(rc);
10781 mMediumAttachments->push_back(attachment);
10782 }
10783 }
10784 catch (HRESULT aRC) { rc = aRC; }
10785
10786 /* unlock all hard disks we locked when there is no VM */
10787 if (!aOnline)
10788 {
10789 ErrorInfoKeeper eik;
10790
10791 HRESULT rc1 = lockedMediaMap->Clear();
10792 AssertComRC(rc1);
10793 }
10794
10795 return rc;
10796}
10797
10798/**
10799 * Deletes implicit differencing hard disks created either by
10800 * #i_createImplicitDiffs() or by #attachDevice() and rolls back
10801 * mMediumAttachments.
10802 *
10803 * Note that to delete hard disks created by #attachDevice() this method is
10804 * called from #i_rollbackMedia() when the changes are rolled back.
10805 *
10806 * @note Locks this object and the media tree for writing.
10807 */
10808HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10809{
10810 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10811
10812 AutoCaller autoCaller(this);
10813 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10814
10815 AutoMultiWriteLock2 alock(this->lockHandle(),
10816 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10817
10818 /* We absolutely must have backed up state. */
10819 AssertReturn(mMediumAttachments.isBackedUp(), E_FAIL);
10820
10821 /* Check if there are any implicitly created diff images. */
10822 bool fImplicitDiffs = false;
10823 for (MediumAttachmentList::const_iterator
10824 it = mMediumAttachments->begin();
10825 it != mMediumAttachments->end();
10826 ++it)
10827 {
10828 const ComObjPtr<MediumAttachment> &pAtt = *it;
10829 if (pAtt->i_isImplicit())
10830 {
10831 fImplicitDiffs = true;
10832 break;
10833 }
10834 }
10835 /* If there is nothing to do, leave early. This saves lots of image locking
10836 * effort. It also avoids a MachineStateChanged event without real reason.
10837 * This is important e.g. when loading a VM config, because there should be
10838 * no events. Otherwise API clients can become thoroughly confused for
10839 * inaccessible VMs (the code for loading VM configs uses this method for
10840 * cleanup if the config makes no sense), as they take such events as an
10841 * indication that the VM is alive, and they would force the VM config to
10842 * be reread, leading to an endless loop. */
10843 if (!fImplicitDiffs)
10844 return S_OK;
10845
10846 HRESULT rc = S_OK;
10847 MachineState_T oldState = mData->mMachineState;
10848
10849 /* will release the lock before the potentially lengthy operation,
10850 * so protect with the special state (unless already protected) */
10851 if ( oldState != MachineState_Snapshotting
10852 && oldState != MachineState_OnlineSnapshotting
10853 && oldState != MachineState_LiveSnapshotting
10854 && oldState != MachineState_RestoringSnapshot
10855 && oldState != MachineState_DeletingSnapshot
10856 && oldState != MachineState_DeletingSnapshotOnline
10857 && oldState != MachineState_DeletingSnapshotPaused
10858 )
10859 i_setMachineState(MachineState_SettingUp);
10860
10861 // use appropriate locked media map (online or offline)
10862 MediumLockListMap lockedMediaOffline;
10863 MediumLockListMap *lockedMediaMap;
10864 if (aOnline)
10865 lockedMediaMap = &mData->mSession.mLockedMedia;
10866 else
10867 lockedMediaMap = &lockedMediaOffline;
10868
10869 try
10870 {
10871 if (!aOnline)
10872 {
10873 /* lock all attached hard disks early to detect "in use"
10874 * situations before deleting actual diffs */
10875 for (MediumAttachmentList::const_iterator
10876 it = mMediumAttachments->begin();
10877 it != mMediumAttachments->end();
10878 ++it)
10879 {
10880 MediumAttachment *pAtt = *it;
10881 if (pAtt->i_getType() == DeviceType_HardDisk)
10882 {
10883 Medium *pMedium = pAtt->i_getMedium();
10884 Assert(pMedium);
10885
10886 MediumLockList *pMediumLockList(new MediumLockList());
10887 alock.release();
10888 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10889 NULL /* pToLockWrite */,
10890 false /* fMediumLockWriteAll */,
10891 NULL,
10892 *pMediumLockList);
10893 alock.acquire();
10894
10895 if (FAILED(rc))
10896 {
10897 delete pMediumLockList;
10898 throw rc;
10899 }
10900
10901 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10902 if (FAILED(rc))
10903 throw rc;
10904 }
10905 }
10906
10907 if (FAILED(rc))
10908 throw rc;
10909 } // end of offline
10910
10911 /* Lock lists are now up to date and include implicitly created media */
10912
10913 /* Go through remembered attachments and delete all implicitly created
10914 * diffs and fix up the attachment information */
10915 const MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
10916 MediumAttachmentList implicitAtts;
10917 for (MediumAttachmentList::const_iterator
10918 it = mMediumAttachments->begin();
10919 it != mMediumAttachments->end();
10920 ++it)
10921 {
10922 ComObjPtr<MediumAttachment> pAtt = *it;
10923 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10924 if (pMedium.isNull())
10925 continue;
10926
10927 // Implicit attachments go on the list for deletion and back references are removed.
10928 if (pAtt->i_isImplicit())
10929 {
10930 /* Deassociate and mark for deletion */
10931 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10932 rc = pMedium->i_removeBackReference(mData->mUuid);
10933 if (FAILED(rc))
10934 throw rc;
10935 implicitAtts.push_back(pAtt);
10936 continue;
10937 }
10938
10939 /* Was this medium attached before? */
10940 if (!i_findAttachment(oldAtts, pMedium))
10941 {
10942 /* no: de-associate */
10943 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10944 rc = pMedium->i_removeBackReference(mData->mUuid);
10945 if (FAILED(rc))
10946 throw rc;
10947 continue;
10948 }
10949 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10950 }
10951
10952 /* If there are implicit attachments to delete, throw away the lock
10953 * map contents (which will unlock all media) since the medium
10954 * attachments will be rolled back. Below we need to completely
10955 * recreate the lock map anyway since it is infinitely complex to
10956 * do this incrementally (would need reconstructing each attachment
10957 * change, which would be extremely hairy). */
10958 if (implicitAtts.size() != 0)
10959 {
10960 ErrorInfoKeeper eik;
10961
10962 HRESULT rc1 = lockedMediaMap->Clear();
10963 AssertComRC(rc1);
10964 }
10965
10966 /* rollback hard disk changes */
10967 mMediumAttachments.rollback();
10968
10969 MultiResult mrc(S_OK);
10970
10971 // Delete unused implicit diffs.
10972 if (implicitAtts.size() != 0)
10973 {
10974 alock.release();
10975
10976 for (MediumAttachmentList::const_iterator
10977 it = implicitAtts.begin();
10978 it != implicitAtts.end();
10979 ++it)
10980 {
10981 // Remove medium associated with this attachment.
10982 ComObjPtr<MediumAttachment> pAtt = *it;
10983 Assert(pAtt);
10984 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10985 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10986 Assert(pMedium);
10987
10988 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/, false /*aNotify*/);
10989 // continue on delete failure, just collect error messages
10990 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
10991 pMedium->i_getLocationFull().c_str() ));
10992 mrc = rc;
10993 }
10994 // Clear the list of deleted implicit attachments now, while not
10995 // holding the lock, as it will ultimately trigger Medium::uninit()
10996 // calls which assume that the media tree lock isn't held.
10997 implicitAtts.clear();
10998
10999 alock.acquire();
11000
11001 /* if there is a VM recreate media lock map as mentioned above,
11002 * otherwise it is a waste of time and we leave things unlocked */
11003 if (aOnline)
11004 {
11005 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11006 /* must never be NULL, but better safe than sorry */
11007 if (!pMachine.isNull())
11008 {
11009 alock.release();
11010 rc = mData->mSession.mMachine->i_lockMedia();
11011 alock.acquire();
11012 if (FAILED(rc))
11013 throw rc;
11014 }
11015 }
11016 }
11017 }
11018 catch (HRESULT aRC) {rc = aRC;}
11019
11020 if (mData->mMachineState == MachineState_SettingUp)
11021 i_setMachineState(oldState);
11022
11023 /* unlock all hard disks we locked when there is no VM */
11024 if (!aOnline)
11025 {
11026 ErrorInfoKeeper eik;
11027
11028 HRESULT rc1 = lockedMediaMap->Clear();
11029 AssertComRC(rc1);
11030 }
11031
11032 return rc;
11033}
11034
11035
11036/**
11037 * Looks through the given list of media attachments for one with the given parameters
11038 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11039 * can be searched as well if needed.
11040 *
11041 * @param ll
11042 * @param aControllerName
11043 * @param aControllerPort
11044 * @param aDevice
11045 * @return
11046 */
11047MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11048 const Utf8Str &aControllerName,
11049 LONG aControllerPort,
11050 LONG aDevice)
11051{
11052 for (MediumAttachmentList::const_iterator
11053 it = ll.begin();
11054 it != ll.end();
11055 ++it)
11056 {
11057 MediumAttachment *pAttach = *it;
11058 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11059 return pAttach;
11060 }
11061
11062 return NULL;
11063}
11064
11065/**
11066 * Looks through the given list of media attachments for one with the given parameters
11067 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11068 * can be searched as well if needed.
11069 *
11070 * @param ll
11071 * @param pMedium
11072 * @return
11073 */
11074MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11075 ComObjPtr<Medium> pMedium)
11076{
11077 for (MediumAttachmentList::const_iterator
11078 it = ll.begin();
11079 it != ll.end();
11080 ++it)
11081 {
11082 MediumAttachment *pAttach = *it;
11083 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11084 if (pMediumThis == pMedium)
11085 return pAttach;
11086 }
11087
11088 return NULL;
11089}
11090
11091/**
11092 * Looks through the given list of media attachments for one with the given parameters
11093 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11094 * can be searched as well if needed.
11095 *
11096 * @param ll
11097 * @param id
11098 * @return
11099 */
11100MediumAttachment *Machine::i_findAttachment(const MediumAttachmentList &ll,
11101 Guid &id)
11102{
11103 for (MediumAttachmentList::const_iterator
11104 it = ll.begin();
11105 it != ll.end();
11106 ++it)
11107 {
11108 MediumAttachment *pAttach = *it;
11109 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11110 if (pMediumThis->i_getId() == id)
11111 return pAttach;
11112 }
11113
11114 return NULL;
11115}
11116
11117/**
11118 * Main implementation for Machine::DetachDevice. This also gets called
11119 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11120 *
11121 * @param pAttach Medium attachment to detach.
11122 * @param writeLock Machine write lock which the caller must have locked once.
11123 * This may be released temporarily in here.
11124 * @param pSnapshot If NULL, then the detachment is for the current machine.
11125 * Otherwise this is for a SnapshotMachine, and this must be
11126 * its snapshot.
11127 * @return
11128 */
11129HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11130 AutoWriteLock &writeLock,
11131 Snapshot *pSnapshot)
11132{
11133 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11134 DeviceType_T mediumType = pAttach->i_getType();
11135
11136 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11137
11138 if (pAttach->i_isImplicit())
11139 {
11140 /* attempt to implicitly delete the implicitly created diff */
11141
11142 /// @todo move the implicit flag from MediumAttachment to Medium
11143 /// and forbid any hard disk operation when it is implicit. Or maybe
11144 /// a special media state for it to make it even more simple.
11145
11146 Assert(mMediumAttachments.isBackedUp());
11147
11148 /* will release the lock before the potentially lengthy operation, so
11149 * protect with the special state */
11150 MachineState_T oldState = mData->mMachineState;
11151 i_setMachineState(MachineState_SettingUp);
11152
11153 writeLock.release();
11154
11155 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11156 true /*aWait*/,
11157 false /*aNotify*/);
11158
11159 writeLock.acquire();
11160
11161 i_setMachineState(oldState);
11162
11163 if (FAILED(rc)) return rc;
11164 }
11165
11166 i_setModified(IsModified_Storage);
11167 mMediumAttachments.backup();
11168 mMediumAttachments->remove(pAttach);
11169
11170 if (!oldmedium.isNull())
11171 {
11172 // if this is from a snapshot, do not defer detachment to i_commitMedia()
11173 if (pSnapshot)
11174 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11175 // else if non-hard disk media, do not defer detachment to i_commitMedia() either
11176 else if (mediumType != DeviceType_HardDisk)
11177 oldmedium->i_removeBackReference(mData->mUuid);
11178 }
11179
11180 return S_OK;
11181}
11182
11183/**
11184 * Goes thru all media of the given list and
11185 *
11186 * 1) calls i_detachDevice() on each of them for this machine and
11187 * 2) adds all Medium objects found in the process to the given list,
11188 * depending on cleanupMode.
11189 *
11190 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11191 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11192 * media to the list.
11193 *
11194 * This gets called from Machine::Unregister, both for the actual Machine and
11195 * the SnapshotMachine objects that might be found in the snapshots.
11196 *
11197 * Requires caller and locking. The machine lock must be passed in because it
11198 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11199 *
11200 * @param writeLock Machine lock from top-level caller; this gets passed to
11201 * i_detachDevice.
11202 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot
11203 * object if called for a SnapshotMachine.
11204 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get
11205 * added to llMedia; if Full, then all media get added;
11206 * otherwise no media get added.
11207 * @param llMedia Caller's list to receive Medium objects which got detached so
11208 * caller can close() them, depending on cleanupMode.
11209 * @return
11210 */
11211HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11212 Snapshot *pSnapshot,
11213 CleanupMode_T cleanupMode,
11214 MediaList &llMedia)
11215{
11216 Assert(isWriteLockOnCurrentThread());
11217
11218 HRESULT rc;
11219
11220 // make a temporary list because i_detachDevice invalidates iterators into
11221 // mMediumAttachments
11222 MediumAttachmentList llAttachments2 = *mMediumAttachments.data();
11223
11224 for (MediumAttachmentList::iterator
11225 it = llAttachments2.begin();
11226 it != llAttachments2.end();
11227 ++it)
11228 {
11229 ComObjPtr<MediumAttachment> &pAttach = *it;
11230 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11231
11232 if (!pMedium.isNull())
11233 {
11234 AutoCaller mac(pMedium);
11235 if (FAILED(mac.rc())) return mac.rc();
11236 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11237 DeviceType_T devType = pMedium->i_getDeviceType();
11238 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11239 && devType == DeviceType_HardDisk)
11240 || (cleanupMode == CleanupMode_Full)
11241 )
11242 {
11243 llMedia.push_back(pMedium);
11244 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11245 /* Not allowed to keep this lock as below we need the parent
11246 * medium lock, and the lock order is parent to child. */
11247 lock.release();
11248 /*
11249 * Search for medias which are not attached to any machine, but
11250 * in the chain to an attached disk. Mediums are only consided
11251 * if they are:
11252 * - have only one child
11253 * - no references to any machines
11254 * - are of normal medium type
11255 */
11256 while (!pParent.isNull())
11257 {
11258 AutoCaller mac1(pParent);
11259 if (FAILED(mac1.rc())) return mac1.rc();
11260 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11261 if (pParent->i_getChildren().size() == 1)
11262 {
11263 if ( pParent->i_getMachineBackRefCount() == 0
11264 && pParent->i_getType() == MediumType_Normal
11265 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11266 llMedia.push_back(pParent);
11267 }
11268 else
11269 break;
11270 pParent = pParent->i_getParent();
11271 }
11272 }
11273 }
11274
11275 // real machine: then we need to use the proper method
11276 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11277
11278 if (FAILED(rc))
11279 return rc;
11280 }
11281
11282 return S_OK;
11283}
11284
11285/**
11286 * Perform deferred hard disk detachments.
11287 *
11288 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11289 * changed (not backed up).
11290 *
11291 * If @a aOnline is @c true then this method will also unlock the old hard
11292 * disks for which the new implicit diffs were created and will lock these new
11293 * diffs for writing.
11294 *
11295 * @param aOnline Whether the VM was online prior to this operation.
11296 *
11297 * @note Locks this object for writing!
11298 */
11299void Machine::i_commitMedia(bool aOnline /*= false*/)
11300{
11301 AutoCaller autoCaller(this);
11302 AssertComRCReturnVoid(autoCaller.rc());
11303
11304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11305
11306 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11307
11308 HRESULT rc = S_OK;
11309
11310 /* no attach/detach operations -- nothing to do */
11311 if (!mMediumAttachments.isBackedUp())
11312 return;
11313
11314 MediumAttachmentList &oldAtts = *mMediumAttachments.backedUpData();
11315 bool fMediaNeedsLocking = false;
11316
11317 /* enumerate new attachments */
11318 for (MediumAttachmentList::const_iterator
11319 it = mMediumAttachments->begin();
11320 it != mMediumAttachments->end();
11321 ++it)
11322 {
11323 MediumAttachment *pAttach = *it;
11324
11325 pAttach->i_commit();
11326
11327 Medium *pMedium = pAttach->i_getMedium();
11328 bool fImplicit = pAttach->i_isImplicit();
11329
11330 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11331 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11332 fImplicit));
11333
11334 /** @todo convert all this Machine-based voodoo to MediumAttachment
11335 * based commit logic. */
11336 if (fImplicit)
11337 {
11338 /* convert implicit attachment to normal */
11339 pAttach->i_setImplicit(false);
11340
11341 if ( aOnline
11342 && pMedium
11343 && pAttach->i_getType() == DeviceType_HardDisk
11344 )
11345 {
11346 /* update the appropriate lock list */
11347 MediumLockList *pMediumLockList;
11348 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11349 AssertComRC(rc);
11350 if (pMediumLockList)
11351 {
11352 /* unlock if there's a need to change the locking */
11353 if (!fMediaNeedsLocking)
11354 {
11355 rc = mData->mSession.mLockedMedia.Unlock();
11356 AssertComRC(rc);
11357 fMediaNeedsLocking = true;
11358 }
11359 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11360 AssertComRC(rc);
11361 rc = pMediumLockList->Append(pMedium, true);
11362 AssertComRC(rc);
11363 }
11364 }
11365
11366 continue;
11367 }
11368
11369 if (pMedium)
11370 {
11371 /* was this medium attached before? */
11372 for (MediumAttachmentList::iterator
11373 oldIt = oldAtts.begin();
11374 oldIt != oldAtts.end();
11375 ++oldIt)
11376 {
11377 MediumAttachment *pOldAttach = *oldIt;
11378 if (pOldAttach->i_getMedium() == pMedium)
11379 {
11380 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11381
11382 /* yes: remove from old to avoid de-association */
11383 oldAtts.erase(oldIt);
11384 break;
11385 }
11386 }
11387 }
11388 }
11389
11390 /* enumerate remaining old attachments and de-associate from the
11391 * current machine state */
11392 for (MediumAttachmentList::const_iterator
11393 it = oldAtts.begin();
11394 it != oldAtts.end();
11395 ++it)
11396 {
11397 MediumAttachment *pAttach = *it;
11398 Medium *pMedium = pAttach->i_getMedium();
11399
11400 /* Detach only hard disks, since DVD/floppy media is detached
11401 * instantly in MountMedium. */
11402 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11403 {
11404 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11405
11406 /* now de-associate from the current machine state */
11407 rc = pMedium->i_removeBackReference(mData->mUuid);
11408 AssertComRC(rc);
11409
11410 if (aOnline)
11411 {
11412 /* unlock since medium is not used anymore */
11413 MediumLockList *pMediumLockList;
11414 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11415 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11416 {
11417 /* this happens for online snapshots, there the attachment
11418 * is changing, but only to a diff image created under
11419 * the old one, so there is no separate lock list */
11420 Assert(!pMediumLockList);
11421 }
11422 else
11423 {
11424 AssertComRC(rc);
11425 if (pMediumLockList)
11426 {
11427 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11428 AssertComRC(rc);
11429 }
11430 }
11431 }
11432 }
11433 }
11434
11435 /* take media locks again so that the locking state is consistent */
11436 if (fMediaNeedsLocking)
11437 {
11438 Assert(aOnline);
11439 rc = mData->mSession.mLockedMedia.Lock();
11440 AssertComRC(rc);
11441 }
11442
11443 /* commit the hard disk changes */
11444 mMediumAttachments.commit();
11445
11446 if (i_isSessionMachine())
11447 {
11448 /*
11449 * Update the parent machine to point to the new owner.
11450 * This is necessary because the stored parent will point to the
11451 * session machine otherwise and cause crashes or errors later
11452 * when the session machine gets invalid.
11453 */
11454 /** @todo Change the MediumAttachment class to behave like any other
11455 * class in this regard by creating peer MediumAttachment
11456 * objects for session machines and share the data with the peer
11457 * machine.
11458 */
11459 for (MediumAttachmentList::const_iterator
11460 it = mMediumAttachments->begin();
11461 it != mMediumAttachments->end();
11462 ++it)
11463 (*it)->i_updateParentMachine(mPeer);
11464
11465 /* attach new data to the primary machine and reshare it */
11466 mPeer->mMediumAttachments.attach(mMediumAttachments);
11467 }
11468
11469 return;
11470}
11471
11472/**
11473 * Perform deferred deletion of implicitly created diffs.
11474 *
11475 * Does nothing if the hard disk attachment data (mMediumAttachments) is not
11476 * changed (not backed up).
11477 *
11478 * @note Locks this object for writing!
11479 */
11480void Machine::i_rollbackMedia()
11481{
11482 AutoCaller autoCaller(this);
11483 AssertComRCReturnVoid(autoCaller.rc());
11484
11485 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11486 LogFlowThisFunc(("Entering rollbackMedia\n"));
11487
11488 HRESULT rc = S_OK;
11489
11490 /* no attach/detach operations -- nothing to do */
11491 if (!mMediumAttachments.isBackedUp())
11492 return;
11493
11494 /* enumerate new attachments */
11495 for (MediumAttachmentList::const_iterator
11496 it = mMediumAttachments->begin();
11497 it != mMediumAttachments->end();
11498 ++it)
11499 {
11500 MediumAttachment *pAttach = *it;
11501 /* Fix up the backrefs for DVD/floppy media. */
11502 if (pAttach->i_getType() != DeviceType_HardDisk)
11503 {
11504 Medium *pMedium = pAttach->i_getMedium();
11505 if (pMedium)
11506 {
11507 rc = pMedium->i_removeBackReference(mData->mUuid);
11508 AssertComRC(rc);
11509 }
11510 }
11511
11512 (*it)->i_rollback();
11513
11514 pAttach = *it;
11515 /* Fix up the backrefs for DVD/floppy media. */
11516 if (pAttach->i_getType() != DeviceType_HardDisk)
11517 {
11518 Medium *pMedium = pAttach->i_getMedium();
11519 if (pMedium)
11520 {
11521 rc = pMedium->i_addBackReference(mData->mUuid);
11522 AssertComRC(rc);
11523 }
11524 }
11525 }
11526
11527 /** @todo convert all this Machine-based voodoo to MediumAttachment
11528 * based rollback logic. */
11529 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11530
11531 return;
11532}
11533
11534/**
11535 * Returns true if the settings file is located in the directory named exactly
11536 * as the machine; this means, among other things, that the machine directory
11537 * should be auto-renamed.
11538 *
11539 * @param aSettingsDir if not NULL, the full machine settings file directory
11540 * name will be assigned there.
11541 *
11542 * @note Doesn't lock anything.
11543 * @note Not thread safe (must be called from this object's lock).
11544 */
11545bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11546{
11547 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11548 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11549 if (aSettingsDir)
11550 *aSettingsDir = strMachineDirName;
11551 strMachineDirName.stripPath(); // vmname
11552 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11553 strConfigFileOnly.stripPath() // vmname.vbox
11554 .stripSuffix(); // vmname
11555 /** @todo hack, make somehow use of ComposeMachineFilename */
11556 if (mUserData->s.fDirectoryIncludesUUID)
11557 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11558
11559 AssertReturn(!strMachineDirName.isEmpty(), false);
11560 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11561
11562 return strMachineDirName == strConfigFileOnly;
11563}
11564
11565/**
11566 * Discards all changes to machine settings.
11567 *
11568 * @param aNotify Whether to notify the direct session about changes or not.
11569 *
11570 * @note Locks objects for writing!
11571 */
11572void Machine::i_rollback(bool aNotify)
11573{
11574 AutoCaller autoCaller(this);
11575 AssertComRCReturn(autoCaller.rc(), (void)0);
11576
11577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11578
11579 if (!mStorageControllers.isNull())
11580 {
11581 if (mStorageControllers.isBackedUp())
11582 {
11583 /* unitialize all new devices (absent in the backed up list). */
11584 StorageControllerList *backedList = mStorageControllers.backedUpData();
11585 for (StorageControllerList::const_iterator
11586 it = mStorageControllers->begin();
11587 it != mStorageControllers->end();
11588 ++it)
11589 {
11590 if ( std::find(backedList->begin(), backedList->end(), *it)
11591 == backedList->end()
11592 )
11593 {
11594 (*it)->uninit();
11595 }
11596 }
11597
11598 /* restore the list */
11599 mStorageControllers.rollback();
11600 }
11601
11602 /* rollback any changes to devices after restoring the list */
11603 if (mData->flModifications & IsModified_Storage)
11604 {
11605 for (StorageControllerList::const_iterator
11606 it = mStorageControllers->begin();
11607 it != mStorageControllers->end();
11608 ++it)
11609 {
11610 (*it)->i_rollback();
11611 }
11612 }
11613 }
11614
11615 if (!mUSBControllers.isNull())
11616 {
11617 if (mUSBControllers.isBackedUp())
11618 {
11619 /* unitialize all new devices (absent in the backed up list). */
11620 USBControllerList *backedList = mUSBControllers.backedUpData();
11621 for (USBControllerList::const_iterator
11622 it = mUSBControllers->begin();
11623 it != mUSBControllers->end();
11624 ++it)
11625 {
11626 if ( std::find(backedList->begin(), backedList->end(), *it)
11627 == backedList->end()
11628 )
11629 {
11630 (*it)->uninit();
11631 }
11632 }
11633
11634 /* restore the list */
11635 mUSBControllers.rollback();
11636 }
11637
11638 /* rollback any changes to devices after restoring the list */
11639 if (mData->flModifications & IsModified_USB)
11640 {
11641 for (USBControllerList::const_iterator
11642 it = mUSBControllers->begin();
11643 it != mUSBControllers->end();
11644 ++it)
11645 {
11646 (*it)->i_rollback();
11647 }
11648 }
11649 }
11650
11651 mUserData.rollback();
11652
11653 mHWData.rollback();
11654
11655 if (mData->flModifications & IsModified_Storage)
11656 i_rollbackMedia();
11657
11658 if (mBIOSSettings)
11659 mBIOSSettings->i_rollback();
11660
11661 if (mRecordingSettings && (mData->flModifications & IsModified_Recording))
11662 mRecordingSettings->i_rollback();
11663
11664 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11665 mVRDEServer->i_rollback();
11666
11667 if (mAudioAdapter)
11668 mAudioAdapter->i_rollback();
11669
11670 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11671 mUSBDeviceFilters->i_rollback();
11672
11673 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11674 mBandwidthControl->i_rollback();
11675
11676 if (!mHWData.isNull())
11677 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11678 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11679 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11680 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11681
11682 if (mData->flModifications & IsModified_NetworkAdapters)
11683 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11684 if ( mNetworkAdapters[slot]
11685 && mNetworkAdapters[slot]->i_isModified())
11686 {
11687 mNetworkAdapters[slot]->i_rollback();
11688 networkAdapters[slot] = mNetworkAdapters[slot];
11689 }
11690
11691 if (mData->flModifications & IsModified_SerialPorts)
11692 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11693 if ( mSerialPorts[slot]
11694 && mSerialPorts[slot]->i_isModified())
11695 {
11696 mSerialPorts[slot]->i_rollback();
11697 serialPorts[slot] = mSerialPorts[slot];
11698 }
11699
11700 if (mData->flModifications & IsModified_ParallelPorts)
11701 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11702 if ( mParallelPorts[slot]
11703 && mParallelPorts[slot]->i_isModified())
11704 {
11705 mParallelPorts[slot]->i_rollback();
11706 parallelPorts[slot] = mParallelPorts[slot];
11707 }
11708
11709 if (aNotify)
11710 {
11711 /* inform the direct session about changes */
11712
11713 ComObjPtr<Machine> that = this;
11714 uint32_t flModifications = mData->flModifications;
11715 alock.release();
11716
11717 if (flModifications & IsModified_SharedFolders)
11718 that->i_onSharedFolderChange();
11719
11720 if (flModifications & IsModified_VRDEServer)
11721 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11722 if (flModifications & IsModified_USB)
11723 that->i_onUSBControllerChange();
11724
11725 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11726 if (networkAdapters[slot])
11727 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11728 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11729 if (serialPorts[slot])
11730 that->i_onSerialPortChange(serialPorts[slot]);
11731 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11732 if (parallelPorts[slot])
11733 that->i_onParallelPortChange(parallelPorts[slot]);
11734
11735 if (flModifications & IsModified_Storage)
11736 {
11737 for (StorageControllerList::const_iterator
11738 it = mStorageControllers->begin();
11739 it != mStorageControllers->end();
11740 ++it)
11741 {
11742 that->i_onStorageControllerChange(that->i_getId(), (*it)->i_getName());
11743 }
11744 }
11745
11746
11747#if 0
11748 if (flModifications & IsModified_BandwidthControl)
11749 that->onBandwidthControlChange();
11750#endif
11751 }
11752}
11753
11754/**
11755 * Commits all the changes to machine settings.
11756 *
11757 * Note that this operation is supposed to never fail.
11758 *
11759 * @note Locks this object and children for writing.
11760 */
11761void Machine::i_commit()
11762{
11763 AutoCaller autoCaller(this);
11764 AssertComRCReturnVoid(autoCaller.rc());
11765
11766 AutoCaller peerCaller(mPeer);
11767 AssertComRCReturnVoid(peerCaller.rc());
11768
11769 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11770
11771 /*
11772 * use safe commit to ensure Snapshot machines (that share mUserData)
11773 * will still refer to a valid memory location
11774 */
11775 mUserData.commitCopy();
11776
11777 mHWData.commit();
11778
11779 if (mMediumAttachments.isBackedUp())
11780 i_commitMedia(Global::IsOnline(mData->mMachineState));
11781
11782 mBIOSSettings->i_commit();
11783 mRecordingSettings->i_commit();
11784 mVRDEServer->i_commit();
11785 mAudioAdapter->i_commit();
11786 mUSBDeviceFilters->i_commit();
11787 mBandwidthControl->i_commit();
11788
11789 /* Since mNetworkAdapters is a list which might have been changed (resized)
11790 * without using the Backupable<> template we need to handle the copying
11791 * of the list entries manually, including the creation of peers for the
11792 * new objects. */
11793 bool commitNetworkAdapters = false;
11794 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11795 if (mPeer)
11796 {
11797 /* commit everything, even the ones which will go away */
11798 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11799 mNetworkAdapters[slot]->i_commit();
11800 /* copy over the new entries, creating a peer and uninit the original */
11801 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11802 for (size_t slot = 0; slot < newSize; slot++)
11803 {
11804 /* look if this adapter has a peer device */
11805 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11806 if (!peer)
11807 {
11808 /* no peer means the adapter is a newly created one;
11809 * create a peer owning data this data share it with */
11810 peer.createObject();
11811 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11812 }
11813 mPeer->mNetworkAdapters[slot] = peer;
11814 }
11815 /* uninit any no longer needed network adapters */
11816 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11817 mNetworkAdapters[slot]->uninit();
11818 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11819 {
11820 if (mPeer->mNetworkAdapters[slot])
11821 mPeer->mNetworkAdapters[slot]->uninit();
11822 }
11823 /* Keep the original network adapter count until this point, so that
11824 * discarding a chipset type change will not lose settings. */
11825 mNetworkAdapters.resize(newSize);
11826 mPeer->mNetworkAdapters.resize(newSize);
11827 }
11828 else
11829 {
11830 /* we have no peer (our parent is the newly created machine);
11831 * just commit changes to the network adapters */
11832 commitNetworkAdapters = true;
11833 }
11834 if (commitNetworkAdapters)
11835 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11836 mNetworkAdapters[slot]->i_commit();
11837
11838 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11839 mSerialPorts[slot]->i_commit();
11840 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11841 mParallelPorts[slot]->i_commit();
11842
11843 bool commitStorageControllers = false;
11844
11845 if (mStorageControllers.isBackedUp())
11846 {
11847 mStorageControllers.commit();
11848
11849 if (mPeer)
11850 {
11851 /* Commit all changes to new controllers (this will reshare data with
11852 * peers for those who have peers) */
11853 StorageControllerList *newList = new StorageControllerList();
11854 for (StorageControllerList::const_iterator
11855 it = mStorageControllers->begin();
11856 it != mStorageControllers->end();
11857 ++it)
11858 {
11859 (*it)->i_commit();
11860
11861 /* look if this controller has a peer device */
11862 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11863 if (!peer)
11864 {
11865 /* no peer means the device is a newly created one;
11866 * create a peer owning data this device share it with */
11867 peer.createObject();
11868 peer->init(mPeer, *it, true /* aReshare */);
11869 }
11870 else
11871 {
11872 /* remove peer from the old list */
11873 mPeer->mStorageControllers->remove(peer);
11874 }
11875 /* and add it to the new list */
11876 newList->push_back(peer);
11877 }
11878
11879 /* uninit old peer's controllers that are left */
11880 for (StorageControllerList::const_iterator
11881 it = mPeer->mStorageControllers->begin();
11882 it != mPeer->mStorageControllers->end();
11883 ++it)
11884 {
11885 (*it)->uninit();
11886 }
11887
11888 /* attach new list of controllers to our peer */
11889 mPeer->mStorageControllers.attach(newList);
11890 }
11891 else
11892 {
11893 /* we have no peer (our parent is the newly created machine);
11894 * just commit changes to devices */
11895 commitStorageControllers = true;
11896 }
11897 }
11898 else
11899 {
11900 /* the list of controllers itself is not changed,
11901 * just commit changes to controllers themselves */
11902 commitStorageControllers = true;
11903 }
11904
11905 if (commitStorageControllers)
11906 {
11907 for (StorageControllerList::const_iterator
11908 it = mStorageControllers->begin();
11909 it != mStorageControllers->end();
11910 ++it)
11911 {
11912 (*it)->i_commit();
11913 }
11914 }
11915
11916 bool commitUSBControllers = false;
11917
11918 if (mUSBControllers.isBackedUp())
11919 {
11920 mUSBControllers.commit();
11921
11922 if (mPeer)
11923 {
11924 /* Commit all changes to new controllers (this will reshare data with
11925 * peers for those who have peers) */
11926 USBControllerList *newList = new USBControllerList();
11927 for (USBControllerList::const_iterator
11928 it = mUSBControllers->begin();
11929 it != mUSBControllers->end();
11930 ++it)
11931 {
11932 (*it)->i_commit();
11933
11934 /* look if this controller has a peer device */
11935 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11936 if (!peer)
11937 {
11938 /* no peer means the device is a newly created one;
11939 * create a peer owning data this device share it with */
11940 peer.createObject();
11941 peer->init(mPeer, *it, true /* aReshare */);
11942 }
11943 else
11944 {
11945 /* remove peer from the old list */
11946 mPeer->mUSBControllers->remove(peer);
11947 }
11948 /* and add it to the new list */
11949 newList->push_back(peer);
11950 }
11951
11952 /* uninit old peer's controllers that are left */
11953 for (USBControllerList::const_iterator
11954 it = mPeer->mUSBControllers->begin();
11955 it != mPeer->mUSBControllers->end();
11956 ++it)
11957 {
11958 (*it)->uninit();
11959 }
11960
11961 /* attach new list of controllers to our peer */
11962 mPeer->mUSBControllers.attach(newList);
11963 }
11964 else
11965 {
11966 /* we have no peer (our parent is the newly created machine);
11967 * just commit changes to devices */
11968 commitUSBControllers = true;
11969 }
11970 }
11971 else
11972 {
11973 /* the list of controllers itself is not changed,
11974 * just commit changes to controllers themselves */
11975 commitUSBControllers = true;
11976 }
11977
11978 if (commitUSBControllers)
11979 {
11980 for (USBControllerList::const_iterator
11981 it = mUSBControllers->begin();
11982 it != mUSBControllers->end();
11983 ++it)
11984 {
11985 (*it)->i_commit();
11986 }
11987 }
11988
11989 if (i_isSessionMachine())
11990 {
11991 /* attach new data to the primary machine and reshare it */
11992 mPeer->mUserData.attach(mUserData);
11993 mPeer->mHWData.attach(mHWData);
11994 /* mmMediumAttachments is reshared by fixupMedia */
11995 // mPeer->mMediumAttachments.attach(mMediumAttachments);
11996 Assert(mPeer->mMediumAttachments.data() == mMediumAttachments.data());
11997 }
11998}
11999
12000/**
12001 * Copies all the hardware data from the given machine.
12002 *
12003 * Currently, only called when the VM is being restored from a snapshot. In
12004 * particular, this implies that the VM is not running during this method's
12005 * call.
12006 *
12007 * @note This method must be called from under this object's lock.
12008 *
12009 * @note This method doesn't call #i_commit(), so all data remains backed up and
12010 * unsaved.
12011 */
12012void Machine::i_copyFrom(Machine *aThat)
12013{
12014 AssertReturnVoid(!i_isSnapshotMachine());
12015 AssertReturnVoid(aThat->i_isSnapshotMachine());
12016
12017 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12018
12019 mHWData.assignCopy(aThat->mHWData);
12020
12021 // create copies of all shared folders (mHWData after attaching a copy
12022 // contains just references to original objects)
12023 for (HWData::SharedFolderList::iterator
12024 it = mHWData->mSharedFolders.begin();
12025 it != mHWData->mSharedFolders.end();
12026 ++it)
12027 {
12028 ComObjPtr<SharedFolder> folder;
12029 folder.createObject();
12030 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12031 AssertComRC(rc);
12032 *it = folder;
12033 }
12034
12035 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12036 mRecordingSettings->i_copyFrom(aThat->mRecordingSettings);
12037 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12038 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12039 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12040 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12041
12042 /* create private copies of all controllers */
12043 mStorageControllers.backup();
12044 mStorageControllers->clear();
12045 for (StorageControllerList::const_iterator
12046 it = aThat->mStorageControllers->begin();
12047 it != aThat->mStorageControllers->end();
12048 ++it)
12049 {
12050 ComObjPtr<StorageController> ctrl;
12051 ctrl.createObject();
12052 ctrl->initCopy(this, *it);
12053 mStorageControllers->push_back(ctrl);
12054 }
12055
12056 /* create private copies of all USB controllers */
12057 mUSBControllers.backup();
12058 mUSBControllers->clear();
12059 for (USBControllerList::const_iterator
12060 it = aThat->mUSBControllers->begin();
12061 it != aThat->mUSBControllers->end();
12062 ++it)
12063 {
12064 ComObjPtr<USBController> ctrl;
12065 ctrl.createObject();
12066 ctrl->initCopy(this, *it);
12067 mUSBControllers->push_back(ctrl);
12068 }
12069
12070 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12071 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12072 {
12073 if (mNetworkAdapters[slot].isNotNull())
12074 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12075 else
12076 {
12077 unconst(mNetworkAdapters[slot]).createObject();
12078 mNetworkAdapters[slot]->initCopy(this, aThat->mNetworkAdapters[slot]);
12079 }
12080 }
12081 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12082 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12083 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12084 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12085}
12086
12087/**
12088 * Returns whether the given storage controller is hotplug capable.
12089 *
12090 * @returns true if the controller supports hotplugging
12091 * false otherwise.
12092 * @param enmCtrlType The controller type to check for.
12093 */
12094bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12095{
12096 ComPtr<ISystemProperties> systemProperties;
12097 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12098 if (FAILED(rc))
12099 return false;
12100
12101 BOOL aHotplugCapable = FALSE;
12102 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12103
12104 return RT_BOOL(aHotplugCapable);
12105}
12106
12107#ifdef VBOX_WITH_RESOURCE_USAGE_API
12108
12109void Machine::i_getDiskList(MediaList &list)
12110{
12111 for (MediumAttachmentList::const_iterator
12112 it = mMediumAttachments->begin();
12113 it != mMediumAttachments->end();
12114 ++it)
12115 {
12116 MediumAttachment *pAttach = *it;
12117 /* just in case */
12118 AssertContinue(pAttach);
12119
12120 AutoCaller localAutoCallerA(pAttach);
12121 if (FAILED(localAutoCallerA.rc())) continue;
12122
12123 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12124
12125 if (pAttach->i_getType() == DeviceType_HardDisk)
12126 list.push_back(pAttach->i_getMedium());
12127 }
12128}
12129
12130void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12131{
12132 AssertReturnVoid(isWriteLockOnCurrentThread());
12133 AssertPtrReturnVoid(aCollector);
12134
12135 pm::CollectorHAL *hal = aCollector->getHAL();
12136 /* Create sub metrics */
12137 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12138 "Percentage of processor time spent in user mode by the VM process.");
12139 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12140 "Percentage of processor time spent in kernel mode by the VM process.");
12141 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12142 "Size of resident portion of VM process in memory.");
12143 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12144 "Actual size of all VM disks combined.");
12145 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12146 "Network receive rate.");
12147 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12148 "Network transmit rate.");
12149 /* Create and register base metrics */
12150 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12151 cpuLoadUser, cpuLoadKernel);
12152 aCollector->registerBaseMetric(cpuLoad);
12153 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12154 ramUsageUsed);
12155 aCollector->registerBaseMetric(ramUsage);
12156 MediaList disks;
12157 i_getDiskList(disks);
12158 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12159 diskUsageUsed);
12160 aCollector->registerBaseMetric(diskUsage);
12161
12162 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12163 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12164 new pm::AggregateAvg()));
12165 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12166 new pm::AggregateMin()));
12167 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12168 new pm::AggregateMax()));
12169 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12170 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12171 new pm::AggregateAvg()));
12172 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12173 new pm::AggregateMin()));
12174 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12175 new pm::AggregateMax()));
12176
12177 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12178 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12179 new pm::AggregateAvg()));
12180 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12181 new pm::AggregateMin()));
12182 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12183 new pm::AggregateMax()));
12184
12185 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12186 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12187 new pm::AggregateAvg()));
12188 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12189 new pm::AggregateMin()));
12190 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12191 new pm::AggregateMax()));
12192
12193
12194 /* Guest metrics collector */
12195 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12196 aCollector->registerGuest(mCollectorGuest);
12197 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12198
12199 /* Create sub metrics */
12200 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12201 "Percentage of processor time spent in user mode as seen by the guest.");
12202 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12203 "Percentage of processor time spent in kernel mode as seen by the guest.");
12204 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12205 "Percentage of processor time spent idling as seen by the guest.");
12206
12207 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12208 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12209 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12210 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12211 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12212 pm::SubMetric *guestMemCache = new pm::SubMetric(
12213 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12214
12215 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12216 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12217
12218 /* Create and register base metrics */
12219 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12220 machineNetRx, machineNetTx);
12221 aCollector->registerBaseMetric(machineNetRate);
12222
12223 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12224 guestLoadUser, guestLoadKernel, guestLoadIdle);
12225 aCollector->registerBaseMetric(guestCpuLoad);
12226
12227 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12228 guestMemTotal, guestMemFree,
12229 guestMemBalloon, guestMemShared,
12230 guestMemCache, guestPagedTotal);
12231 aCollector->registerBaseMetric(guestCpuMem);
12232
12233 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12234 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12235 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12236 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12237
12238 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12239 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12240 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12241 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12242
12243 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12244 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12246 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12247
12248 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12249 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12251 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12252
12253 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12254 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12256 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12257
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12261 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12262
12263 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12264 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12265 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12266 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12267
12268 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12269 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12270 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12271 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12272
12273 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12274 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12275 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12276 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12277
12278 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12279 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12280 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12281 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12282
12283 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12284 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12285 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12286 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12287}
12288
12289void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12290{
12291 AssertReturnVoid(isWriteLockOnCurrentThread());
12292
12293 if (aCollector)
12294 {
12295 aCollector->unregisterMetricsFor(aMachine);
12296 aCollector->unregisterBaseMetricsFor(aMachine);
12297 }
12298}
12299
12300#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12301
12302
12303////////////////////////////////////////////////////////////////////////////////
12304
12305DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12306
12307HRESULT SessionMachine::FinalConstruct()
12308{
12309 LogFlowThisFunc(("\n"));
12310
12311 mClientToken = NULL;
12312
12313 return BaseFinalConstruct();
12314}
12315
12316void SessionMachine::FinalRelease()
12317{
12318 LogFlowThisFunc(("\n"));
12319
12320 Assert(!mClientToken);
12321 /* paranoia, should not hang around any more */
12322 if (mClientToken)
12323 {
12324 delete mClientToken;
12325 mClientToken = NULL;
12326 }
12327
12328 uninit(Uninit::Unexpected);
12329
12330 BaseFinalRelease();
12331}
12332
12333/**
12334 * @note Must be called only by Machine::LockMachine() from its own write lock.
12335 */
12336HRESULT SessionMachine::init(Machine *aMachine)
12337{
12338 LogFlowThisFuncEnter();
12339 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12340
12341 AssertReturn(aMachine, E_INVALIDARG);
12342
12343 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12344
12345 /* Enclose the state transition NotReady->InInit->Ready */
12346 AutoInitSpan autoInitSpan(this);
12347 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12348
12349 HRESULT rc = S_OK;
12350
12351 RT_ZERO(mAuthLibCtx);
12352
12353 /* create the machine client token */
12354 try
12355 {
12356 mClientToken = new ClientToken(aMachine, this);
12357 if (!mClientToken->isReady())
12358 {
12359 delete mClientToken;
12360 mClientToken = NULL;
12361 rc = E_FAIL;
12362 }
12363 }
12364 catch (std::bad_alloc &)
12365 {
12366 rc = E_OUTOFMEMORY;
12367 }
12368 if (FAILED(rc))
12369 return rc;
12370
12371 /* memorize the peer Machine */
12372 unconst(mPeer) = aMachine;
12373 /* share the parent pointer */
12374 unconst(mParent) = aMachine->mParent;
12375
12376 /* take the pointers to data to share */
12377 mData.share(aMachine->mData);
12378 mSSData.share(aMachine->mSSData);
12379
12380 mUserData.share(aMachine->mUserData);
12381 mHWData.share(aMachine->mHWData);
12382 mMediumAttachments.share(aMachine->mMediumAttachments);
12383
12384 mStorageControllers.allocate();
12385 for (StorageControllerList::const_iterator
12386 it = aMachine->mStorageControllers->begin();
12387 it != aMachine->mStorageControllers->end();
12388 ++it)
12389 {
12390 ComObjPtr<StorageController> ctl;
12391 ctl.createObject();
12392 ctl->init(this, *it);
12393 mStorageControllers->push_back(ctl);
12394 }
12395
12396 mUSBControllers.allocate();
12397 for (USBControllerList::const_iterator
12398 it = aMachine->mUSBControllers->begin();
12399 it != aMachine->mUSBControllers->end();
12400 ++it)
12401 {
12402 ComObjPtr<USBController> ctl;
12403 ctl.createObject();
12404 ctl->init(this, *it);
12405 mUSBControllers->push_back(ctl);
12406 }
12407
12408 unconst(mBIOSSettings).createObject();
12409 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12410 unconst(mRecordingSettings).createObject();
12411 mRecordingSettings->init(this, aMachine->mRecordingSettings);
12412 /* create another VRDEServer object that will be mutable */
12413 unconst(mVRDEServer).createObject();
12414 mVRDEServer->init(this, aMachine->mVRDEServer);
12415 /* create another audio adapter object that will be mutable */
12416 unconst(mAudioAdapter).createObject();
12417 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12418 /* create a list of serial ports that will be mutable */
12419 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12420 {
12421 unconst(mSerialPorts[slot]).createObject();
12422 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12423 }
12424 /* create a list of parallel ports that will be mutable */
12425 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12426 {
12427 unconst(mParallelPorts[slot]).createObject();
12428 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12429 }
12430
12431 /* create another USB device filters object that will be mutable */
12432 unconst(mUSBDeviceFilters).createObject();
12433 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12434
12435 /* create a list of network adapters that will be mutable */
12436 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12437 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12438 {
12439 unconst(mNetworkAdapters[slot]).createObject();
12440 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12441 }
12442
12443 /* create another bandwidth control object that will be mutable */
12444 unconst(mBandwidthControl).createObject();
12445 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12446
12447 /* default is to delete saved state on Saved -> PoweredOff transition */
12448 mRemoveSavedState = true;
12449
12450 /* Confirm a successful initialization when it's the case */
12451 autoInitSpan.setSucceeded();
12452
12453 miNATNetworksStarted = 0;
12454
12455 LogFlowThisFuncLeave();
12456 return rc;
12457}
12458
12459/**
12460 * Uninitializes this session object. If the reason is other than
12461 * Uninit::Unexpected, then this method MUST be called from #i_checkForDeath()
12462 * or the client watcher code.
12463 *
12464 * @param aReason uninitialization reason
12465 *
12466 * @note Locks mParent + this object for writing.
12467 */
12468void SessionMachine::uninit(Uninit::Reason aReason)
12469{
12470 LogFlowThisFuncEnter();
12471 LogFlowThisFunc(("reason=%d\n", aReason));
12472
12473 /*
12474 * Strongly reference ourselves to prevent this object deletion after
12475 * mData->mSession.mMachine.setNull() below (which can release the last
12476 * reference and call the destructor). Important: this must be done before
12477 * accessing any members (and before AutoUninitSpan that does it as well).
12478 * This self reference will be released as the very last step on return.
12479 */
12480 ComObjPtr<SessionMachine> selfRef;
12481 if (aReason != Uninit::Unexpected)
12482 selfRef = this;
12483
12484 /* Enclose the state transition Ready->InUninit->NotReady */
12485 AutoUninitSpan autoUninitSpan(this);
12486 if (autoUninitSpan.uninitDone())
12487 {
12488 LogFlowThisFunc(("Already uninitialized\n"));
12489 LogFlowThisFuncLeave();
12490 return;
12491 }
12492
12493 if (autoUninitSpan.initFailed())
12494 {
12495 /* We've been called by init() because it's failed. It's not really
12496 * necessary (nor it's safe) to perform the regular uninit sequence
12497 * below, the following is enough.
12498 */
12499 LogFlowThisFunc(("Initialization failed.\n"));
12500 /* destroy the machine client token */
12501 if (mClientToken)
12502 {
12503 delete mClientToken;
12504 mClientToken = NULL;
12505 }
12506 uninitDataAndChildObjects();
12507 mData.free();
12508 unconst(mParent) = NULL;
12509 unconst(mPeer) = NULL;
12510 LogFlowThisFuncLeave();
12511 return;
12512 }
12513
12514 MachineState_T lastState;
12515 {
12516 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12517 lastState = mData->mMachineState;
12518 }
12519 NOREF(lastState);
12520
12521#ifdef VBOX_WITH_USB
12522 // release all captured USB devices, but do this before requesting the locks below
12523 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12524 {
12525 /* Console::captureUSBDevices() is called in the VM process only after
12526 * setting the machine state to Starting or Restoring.
12527 * Console::detachAllUSBDevices() will be called upon successful
12528 * termination. So, we need to release USB devices only if there was
12529 * an abnormal termination of a running VM.
12530 *
12531 * This is identical to SessionMachine::DetachAllUSBDevices except
12532 * for the aAbnormal argument. */
12533 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12534 AssertComRC(rc);
12535 NOREF(rc);
12536
12537 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12538 if (service)
12539 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12540 }
12541#endif /* VBOX_WITH_USB */
12542
12543 // we need to lock this object in uninit() because the lock is shared
12544 // with mPeer (as well as data we modify below). mParent lock is needed
12545 // by several calls to it.
12546 AutoMultiWriteLock2 multilock(mParent, this COMMA_LOCKVAL_SRC_POS);
12547
12548#ifdef VBOX_WITH_RESOURCE_USAGE_API
12549 /*
12550 * It is safe to call Machine::i_unregisterMetrics() here because
12551 * PerformanceCollector::samplerCallback no longer accesses guest methods
12552 * holding the lock.
12553 */
12554 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12555 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12556 Log7Func(("{%p}: mCollectorGuest=%p\n", this, mCollectorGuest));
12557 if (mCollectorGuest)
12558 {
12559 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12560 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12561 mCollectorGuest = NULL;
12562 }
12563#endif
12564
12565 if (aReason == Uninit::Abnormal)
12566 {
12567 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12568
12569 /* reset the state to Aborted */
12570 if (mData->mMachineState != MachineState_Aborted)
12571 i_setMachineState(MachineState_Aborted);
12572 }
12573
12574 // any machine settings modified?
12575 if (mData->flModifications)
12576 {
12577 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12578 i_rollback(false /* aNotify */);
12579 }
12580
12581 mData->mSession.mPID = NIL_RTPROCESS;
12582
12583 if (aReason == Uninit::Unexpected)
12584 {
12585 /* Uninitialization didn't come from #i_checkForDeath(), so tell the
12586 * client watcher thread to update the set of machines that have open
12587 * sessions. */
12588 mParent->i_updateClientWatcher();
12589 }
12590
12591 /* uninitialize all remote controls */
12592 if (mData->mSession.mRemoteControls.size())
12593 {
12594 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12595 mData->mSession.mRemoteControls.size()));
12596
12597 /* Always restart a the beginning, since the iterator is invalidated
12598 * by using erase(). */
12599 for (Data::Session::RemoteControlList::iterator
12600 it = mData->mSession.mRemoteControls.begin();
12601 it != mData->mSession.mRemoteControls.end();
12602 it = mData->mSession.mRemoteControls.begin())
12603 {
12604 ComPtr<IInternalSessionControl> pControl = *it;
12605 mData->mSession.mRemoteControls.erase(it);
12606 multilock.release();
12607 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12608 HRESULT rc = pControl->Uninitialize();
12609 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12610 if (FAILED(rc))
12611 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12612 multilock.acquire();
12613 }
12614 mData->mSession.mRemoteControls.clear();
12615 }
12616
12617 /* Remove all references to the NAT network service. The service will stop
12618 * if all references (also from other VMs) are removed. */
12619 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12620 {
12621 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12622 {
12623 BOOL enabled;
12624 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
12625 if ( FAILED(hrc)
12626 || !enabled)
12627 continue;
12628
12629 NetworkAttachmentType_T type;
12630 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12631 if ( SUCCEEDED(hrc)
12632 && type == NetworkAttachmentType_NATNetwork)
12633 {
12634 Bstr name;
12635 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12636 if (SUCCEEDED(hrc))
12637 {
12638 multilock.release();
12639 Utf8Str strName(name);
12640 LogRel(("VM '%s' stops using NAT network '%s'\n",
12641 mUserData->s.strName.c_str(), strName.c_str()));
12642 mParent->i_natNetworkRefDec(strName);
12643 multilock.acquire();
12644 }
12645 }
12646 }
12647 }
12648
12649 /*
12650 * An expected uninitialization can come only from #i_checkForDeath().
12651 * Otherwise it means that something's gone really wrong (for example,
12652 * the Session implementation has released the VirtualBox reference
12653 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12654 * etc). However, it's also possible, that the client releases the IPC
12655 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12656 * but the VirtualBox release event comes first to the server process.
12657 * This case is practically possible, so we should not assert on an
12658 * unexpected uninit, just log a warning.
12659 */
12660
12661 if (aReason == Uninit::Unexpected)
12662 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12663
12664 if (aReason != Uninit::Normal)
12665 {
12666 mData->mSession.mDirectControl.setNull();
12667 }
12668 else
12669 {
12670 /* this must be null here (see #OnSessionEnd()) */
12671 Assert(mData->mSession.mDirectControl.isNull());
12672 Assert(mData->mSession.mState == SessionState_Unlocking);
12673 Assert(!mData->mSession.mProgress.isNull());
12674 }
12675 if (mData->mSession.mProgress)
12676 {
12677 if (aReason == Uninit::Normal)
12678 mData->mSession.mProgress->i_notifyComplete(S_OK);
12679 else
12680 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12681 COM_IIDOF(ISession),
12682 getComponentName(),
12683 tr("The VM session was aborted"));
12684 mData->mSession.mProgress.setNull();
12685 }
12686
12687 if (mConsoleTaskData.mProgress)
12688 {
12689 Assert(aReason == Uninit::Abnormal);
12690 mConsoleTaskData.mProgress->i_notifyComplete(E_FAIL,
12691 COM_IIDOF(ISession),
12692 getComponentName(),
12693 tr("The VM session was aborted"));
12694 mConsoleTaskData.mProgress.setNull();
12695 }
12696
12697 /* remove the association between the peer machine and this session machine */
12698 Assert( (SessionMachine*)mData->mSession.mMachine == this
12699 || aReason == Uninit::Unexpected);
12700
12701 /* reset the rest of session data */
12702 mData->mSession.mLockType = LockType_Null;
12703 mData->mSession.mMachine.setNull();
12704 mData->mSession.mState = SessionState_Unlocked;
12705 mData->mSession.mName.setNull();
12706
12707 /* destroy the machine client token before leaving the exclusive lock */
12708 if (mClientToken)
12709 {
12710 delete mClientToken;
12711 mClientToken = NULL;
12712 }
12713
12714 /* fire an event */
12715 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12716
12717 uninitDataAndChildObjects();
12718
12719 /* free the essential data structure last */
12720 mData.free();
12721
12722 /* release the exclusive lock before setting the below two to NULL */
12723 multilock.release();
12724
12725 unconst(mParent) = NULL;
12726 unconst(mPeer) = NULL;
12727
12728 AuthLibUnload(&mAuthLibCtx);
12729
12730 LogFlowThisFuncLeave();
12731}
12732
12733// util::Lockable interface
12734////////////////////////////////////////////////////////////////////////////////
12735
12736/**
12737 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12738 * with the primary Machine instance (mPeer).
12739 */
12740RWLockHandle *SessionMachine::lockHandle() const
12741{
12742 AssertReturn(mPeer != NULL, NULL);
12743 return mPeer->lockHandle();
12744}
12745
12746// IInternalMachineControl methods
12747////////////////////////////////////////////////////////////////////////////////
12748
12749/**
12750 * Passes collected guest statistics to performance collector object
12751 */
12752HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12753 ULONG aCpuKernel, ULONG aCpuIdle,
12754 ULONG aMemTotal, ULONG aMemFree,
12755 ULONG aMemBalloon, ULONG aMemShared,
12756 ULONG aMemCache, ULONG aPageTotal,
12757 ULONG aAllocVMM, ULONG aFreeVMM,
12758 ULONG aBalloonedVMM, ULONG aSharedVMM,
12759 ULONG aVmNetRx, ULONG aVmNetTx)
12760{
12761#ifdef VBOX_WITH_RESOURCE_USAGE_API
12762 if (mCollectorGuest)
12763 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12764 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12765 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12766 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12767
12768 return S_OK;
12769#else
12770 NOREF(aValidStats);
12771 NOREF(aCpuUser);
12772 NOREF(aCpuKernel);
12773 NOREF(aCpuIdle);
12774 NOREF(aMemTotal);
12775 NOREF(aMemFree);
12776 NOREF(aMemBalloon);
12777 NOREF(aMemShared);
12778 NOREF(aMemCache);
12779 NOREF(aPageTotal);
12780 NOREF(aAllocVMM);
12781 NOREF(aFreeVMM);
12782 NOREF(aBalloonedVMM);
12783 NOREF(aSharedVMM);
12784 NOREF(aVmNetRx);
12785 NOREF(aVmNetTx);
12786 return E_NOTIMPL;
12787#endif
12788}
12789
12790////////////////////////////////////////////////////////////////////////////////
12791//
12792// SessionMachine task records
12793//
12794////////////////////////////////////////////////////////////////////////////////
12795
12796/**
12797 * Task record for saving the machine state.
12798 */
12799class SessionMachine::SaveStateTask
12800 : public Machine::Task
12801{
12802public:
12803 SaveStateTask(SessionMachine *m,
12804 Progress *p,
12805 const Utf8Str &t,
12806 Reason_T enmReason,
12807 const Utf8Str &strStateFilePath)
12808 : Task(m, p, t),
12809 m_enmReason(enmReason),
12810 m_strStateFilePath(strStateFilePath)
12811 {}
12812
12813private:
12814 void handler()
12815 {
12816 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12817 }
12818
12819 Reason_T m_enmReason;
12820 Utf8Str m_strStateFilePath;
12821
12822 friend class SessionMachine;
12823};
12824
12825/**
12826 * Task thread implementation for SessionMachine::SaveState(), called from
12827 * SessionMachine::taskHandler().
12828 *
12829 * @note Locks this object for writing.
12830 *
12831 * @param task
12832 * @return
12833 */
12834void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12835{
12836 LogFlowThisFuncEnter();
12837
12838 AutoCaller autoCaller(this);
12839 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12840 if (FAILED(autoCaller.rc()))
12841 {
12842 /* we might have been uninitialized because the session was accidentally
12843 * closed by the client, so don't assert */
12844 HRESULT rc = setError(E_FAIL,
12845 tr("The session has been accidentally closed"));
12846 task.m_pProgress->i_notifyComplete(rc);
12847 LogFlowThisFuncLeave();
12848 return;
12849 }
12850
12851 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12852
12853 HRESULT rc = S_OK;
12854
12855 try
12856 {
12857 ComPtr<IInternalSessionControl> directControl;
12858 if (mData->mSession.mLockType == LockType_VM)
12859 directControl = mData->mSession.mDirectControl;
12860 if (directControl.isNull())
12861 throw setError(VBOX_E_INVALID_VM_STATE,
12862 tr("Trying to save state without a running VM"));
12863 alock.release();
12864 BOOL fSuspendedBySave;
12865 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, NULL, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12866 Assert(!fSuspendedBySave);
12867 alock.acquire();
12868
12869 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12870 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12871 throw E_FAIL);
12872
12873 if (SUCCEEDED(rc))
12874 {
12875 mSSData->strStateFilePath = task.m_strStateFilePath;
12876
12877 /* save all VM settings */
12878 rc = i_saveSettings(NULL);
12879 // no need to check whether VirtualBox.xml needs saving also since
12880 // we can't have a name change pending at this point
12881 }
12882 else
12883 {
12884 // On failure, set the state to the state we had at the beginning.
12885 i_setMachineState(task.m_machineStateBackup);
12886 i_updateMachineStateOnClient();
12887
12888 // Delete the saved state file (might have been already created).
12889 // No need to check whether this is shared with a snapshot here
12890 // because we certainly created a fresh saved state file here.
12891 RTFileDelete(task.m_strStateFilePath.c_str());
12892 }
12893 }
12894 catch (HRESULT aRC) { rc = aRC; }
12895
12896 task.m_pProgress->i_notifyComplete(rc);
12897
12898 LogFlowThisFuncLeave();
12899}
12900
12901/**
12902 * @note Locks this object for writing.
12903 */
12904HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12905{
12906 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12907}
12908
12909HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12910{
12911 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12912
12913 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12914 if (FAILED(rc)) return rc;
12915
12916 if ( mData->mMachineState != MachineState_Running
12917 && mData->mMachineState != MachineState_Paused
12918 )
12919 return setError(VBOX_E_INVALID_VM_STATE,
12920 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12921 Global::stringifyMachineState(mData->mMachineState));
12922
12923 ComObjPtr<Progress> pProgress;
12924 pProgress.createObject();
12925 rc = pProgress->init(i_getVirtualBox(),
12926 static_cast<IMachine *>(this) /* aInitiator */,
12927 tr("Saving the execution state of the virtual machine"),
12928 FALSE /* aCancelable */);
12929 if (FAILED(rc))
12930 return rc;
12931
12932 Utf8Str strStateFilePath;
12933 i_composeSavedStateFilename(strStateFilePath);
12934
12935 /* create and start the task on a separate thread (note that it will not
12936 * start working until we release alock) */
12937 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12938 rc = pTask->createThread();
12939 if (FAILED(rc))
12940 return rc;
12941
12942 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12943 i_setMachineState(MachineState_Saving);
12944 i_updateMachineStateOnClient();
12945
12946 pProgress.queryInterfaceTo(aProgress.asOutParam());
12947
12948 return S_OK;
12949}
12950
12951/**
12952 * @note Locks this object for writing.
12953 */
12954HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12955{
12956 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12957
12958 HRESULT rc = i_checkStateDependency(MutableStateDep);
12959 if (FAILED(rc)) return rc;
12960
12961 if ( mData->mMachineState != MachineState_PoweredOff
12962 && mData->mMachineState != MachineState_Teleported
12963 && mData->mMachineState != MachineState_Aborted
12964 )
12965 return setError(VBOX_E_INVALID_VM_STATE,
12966 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12967 Global::stringifyMachineState(mData->mMachineState));
12968
12969 com::Utf8Str stateFilePathFull;
12970 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12971 if (RT_FAILURE(vrc))
12972 return setErrorBoth(VBOX_E_FILE_ERROR, vrc,
12973 tr("Invalid saved state file path '%s' (%Rrc)"),
12974 aSavedStateFile.c_str(),
12975 vrc);
12976
12977 mSSData->strStateFilePath = stateFilePathFull;
12978
12979 /* The below i_setMachineState() will detect the state transition and will
12980 * update the settings file */
12981
12982 return i_setMachineState(MachineState_Saved);
12983}
12984
12985/**
12986 * @note Locks this object for writing.
12987 */
12988HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12989{
12990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12991
12992 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12993 if (FAILED(rc)) return rc;
12994
12995 if (mData->mMachineState != MachineState_Saved)
12996 return setError(VBOX_E_INVALID_VM_STATE,
12997 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12998 Global::stringifyMachineState(mData->mMachineState));
12999
13000 mRemoveSavedState = RT_BOOL(aFRemoveFile);
13001
13002 /*
13003 * Saved -> PoweredOff transition will be detected in the SessionMachine
13004 * and properly handled.
13005 */
13006 rc = i_setMachineState(MachineState_PoweredOff);
13007 return rc;
13008}
13009
13010
13011/**
13012 * @note Locks the same as #i_setMachineState() does.
13013 */
13014HRESULT SessionMachine::updateState(MachineState_T aState)
13015{
13016 return i_setMachineState(aState);
13017}
13018
13019/**
13020 * @note Locks this object for writing.
13021 */
13022HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
13023{
13024 IProgress *pProgress(aProgress);
13025
13026 LogFlowThisFunc(("aProgress=%p\n", pProgress));
13027
13028 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13029
13030 if (mData->mSession.mState != SessionState_Locked)
13031 return VBOX_E_INVALID_OBJECT_STATE;
13032
13033 if (!mData->mSession.mProgress.isNull())
13034 mData->mSession.mProgress->setOtherProgressObject(pProgress);
13035
13036 /* If we didn't reference the NAT network service yet, add a reference to
13037 * force a start */
13038 if (miNATNetworksStarted < 1)
13039 {
13040 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
13041 {
13042 BOOL enabled;
13043 HRESULT hrc = mNetworkAdapters[slot]->COMGETTER(Enabled)(&enabled);
13044 if ( FAILED(hrc)
13045 || !enabled)
13046 continue;
13047
13048 NetworkAttachmentType_T type;
13049 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
13050 if ( SUCCEEDED(hrc)
13051 && type == NetworkAttachmentType_NATNetwork)
13052 {
13053 Bstr name;
13054 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
13055 if (SUCCEEDED(hrc))
13056 {
13057 Utf8Str strName(name);
13058 LogRel(("VM '%s' starts using NAT network '%s'\n",
13059 mUserData->s.strName.c_str(), strName.c_str()));
13060 mPeer->lockHandle()->unlockWrite();
13061 mParent->i_natNetworkRefInc(strName);
13062#ifdef RT_LOCK_STRICT
13063 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
13064#else
13065 mPeer->lockHandle()->lockWrite();
13066#endif
13067 }
13068 }
13069 }
13070 miNATNetworksStarted++;
13071 }
13072
13073 LogFlowThisFunc(("returns S_OK.\n"));
13074 return S_OK;
13075}
13076
13077/**
13078 * @note Locks this object for writing.
13079 */
13080HRESULT SessionMachine::endPowerUp(LONG aResult)
13081{
13082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13083
13084 if (mData->mSession.mState != SessionState_Locked)
13085 return VBOX_E_INVALID_OBJECT_STATE;
13086
13087 /* Finalize the LaunchVMProcess progress object. */
13088 if (mData->mSession.mProgress)
13089 {
13090 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13091 mData->mSession.mProgress.setNull();
13092 }
13093
13094 if (SUCCEEDED((HRESULT)aResult))
13095 {
13096#ifdef VBOX_WITH_RESOURCE_USAGE_API
13097 /* The VM has been powered up successfully, so it makes sense
13098 * now to offer the performance metrics for a running machine
13099 * object. Doing it earlier wouldn't be safe. */
13100 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13101 mData->mSession.mPID);
13102#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13103 }
13104
13105 return S_OK;
13106}
13107
13108/**
13109 * @note Locks this object for writing.
13110 */
13111HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13112{
13113 LogFlowThisFuncEnter();
13114
13115 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13116
13117 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13118 E_FAIL);
13119
13120 /* create a progress object to track operation completion */
13121 ComObjPtr<Progress> pProgress;
13122 pProgress.createObject();
13123 pProgress->init(i_getVirtualBox(),
13124 static_cast<IMachine *>(this) /* aInitiator */,
13125 tr("Stopping the virtual machine"),
13126 FALSE /* aCancelable */);
13127
13128 /* fill in the console task data */
13129 mConsoleTaskData.mLastState = mData->mMachineState;
13130 mConsoleTaskData.mProgress = pProgress;
13131
13132 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13133 i_setMachineState(MachineState_Stopping);
13134
13135 pProgress.queryInterfaceTo(aProgress.asOutParam());
13136
13137 return S_OK;
13138}
13139
13140/**
13141 * @note Locks this object for writing.
13142 */
13143HRESULT SessionMachine::endPoweringDown(LONG aResult,
13144 const com::Utf8Str &aErrMsg)
13145{
13146 LogFlowThisFuncEnter();
13147
13148 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13149
13150 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13151 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13152 && mConsoleTaskData.mLastState != MachineState_Null,
13153 E_FAIL);
13154
13155 /*
13156 * On failure, set the state to the state we had when BeginPoweringDown()
13157 * was called (this is expected by Console::PowerDown() and the associated
13158 * task). On success the VM process already changed the state to
13159 * MachineState_PoweredOff, so no need to do anything.
13160 */
13161 if (FAILED(aResult))
13162 i_setMachineState(mConsoleTaskData.mLastState);
13163
13164 /* notify the progress object about operation completion */
13165 Assert(mConsoleTaskData.mProgress);
13166 if (SUCCEEDED(aResult))
13167 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13168 else
13169 {
13170 if (aErrMsg.length())
13171 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13172 COM_IIDOF(ISession),
13173 getComponentName(),
13174 aErrMsg.c_str());
13175 else
13176 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13177 }
13178
13179 /* clear out the temporary saved state data */
13180 mConsoleTaskData.mLastState = MachineState_Null;
13181 mConsoleTaskData.mProgress.setNull();
13182
13183 LogFlowThisFuncLeave();
13184 return S_OK;
13185}
13186
13187
13188/**
13189 * Goes through the USB filters of the given machine to see if the given
13190 * device matches any filter or not.
13191 *
13192 * @note Locks the same as USBController::hasMatchingFilter() does.
13193 */
13194HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13195 BOOL *aMatched,
13196 ULONG *aMaskedInterfaces)
13197{
13198 LogFlowThisFunc(("\n"));
13199
13200#ifdef VBOX_WITH_USB
13201 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13202#else
13203 NOREF(aDevice);
13204 NOREF(aMaskedInterfaces);
13205 *aMatched = FALSE;
13206#endif
13207
13208 return S_OK;
13209}
13210
13211/**
13212 * @note Locks the same as Host::captureUSBDevice() does.
13213 */
13214HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13215{
13216 LogFlowThisFunc(("\n"));
13217
13218#ifdef VBOX_WITH_USB
13219 /* if captureDeviceForVM() fails, it must have set extended error info */
13220 clearError();
13221 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13222 if (FAILED(rc)) return rc;
13223
13224 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13225 AssertReturn(service, E_FAIL);
13226 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13227#else
13228 NOREF(aId);
13229 return E_NOTIMPL;
13230#endif
13231}
13232
13233/**
13234 * @note Locks the same as Host::detachUSBDevice() does.
13235 */
13236HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13237 BOOL aDone)
13238{
13239 LogFlowThisFunc(("\n"));
13240
13241#ifdef VBOX_WITH_USB
13242 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13243 AssertReturn(service, E_FAIL);
13244 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13245#else
13246 NOREF(aId);
13247 NOREF(aDone);
13248 return E_NOTIMPL;
13249#endif
13250}
13251
13252/**
13253 * Inserts all machine filters to the USB proxy service and then calls
13254 * Host::autoCaptureUSBDevices().
13255 *
13256 * Called by Console from the VM process upon VM startup.
13257 *
13258 * @note Locks what called methods lock.
13259 */
13260HRESULT SessionMachine::autoCaptureUSBDevices()
13261{
13262 LogFlowThisFunc(("\n"));
13263
13264#ifdef VBOX_WITH_USB
13265 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13266 AssertComRC(rc);
13267 NOREF(rc);
13268
13269 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13270 AssertReturn(service, E_FAIL);
13271 return service->autoCaptureDevicesForVM(this);
13272#else
13273 return S_OK;
13274#endif
13275}
13276
13277/**
13278 * Removes all machine filters from the USB proxy service and then calls
13279 * Host::detachAllUSBDevices().
13280 *
13281 * Called by Console from the VM process upon normal VM termination or by
13282 * SessionMachine::uninit() upon abnormal VM termination (from under the
13283 * Machine/SessionMachine lock).
13284 *
13285 * @note Locks what called methods lock.
13286 */
13287HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13288{
13289 LogFlowThisFunc(("\n"));
13290
13291#ifdef VBOX_WITH_USB
13292 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13293 AssertComRC(rc);
13294 NOREF(rc);
13295
13296 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13297 AssertReturn(service, E_FAIL);
13298 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13299#else
13300 NOREF(aDone);
13301 return S_OK;
13302#endif
13303}
13304
13305/**
13306 * @note Locks this object for writing.
13307 */
13308HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13309 ComPtr<IProgress> &aProgress)
13310{
13311 LogFlowThisFuncEnter();
13312
13313 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13314 /*
13315 * We don't assert below because it might happen that a non-direct session
13316 * informs us it is closed right after we've been uninitialized -- it's ok.
13317 */
13318
13319 /* get IInternalSessionControl interface */
13320 ComPtr<IInternalSessionControl> control(aSession);
13321
13322 ComAssertRet(!control.isNull(), E_INVALIDARG);
13323
13324 /* Creating a Progress object requires the VirtualBox lock, and
13325 * thus locking it here is required by the lock order rules. */
13326 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13327
13328 if (control == mData->mSession.mDirectControl)
13329 {
13330 /* The direct session is being normally closed by the client process
13331 * ----------------------------------------------------------------- */
13332
13333 /* go to the closing state (essential for all open*Session() calls and
13334 * for #i_checkForDeath()) */
13335 Assert(mData->mSession.mState == SessionState_Locked);
13336 mData->mSession.mState = SessionState_Unlocking;
13337
13338 /* set direct control to NULL to release the remote instance */
13339 mData->mSession.mDirectControl.setNull();
13340 LogFlowThisFunc(("Direct control is set to NULL\n"));
13341
13342 if (mData->mSession.mProgress)
13343 {
13344 /* finalize the progress, someone might wait if a frontend
13345 * closes the session before powering on the VM. */
13346 mData->mSession.mProgress->notifyComplete(E_FAIL,
13347 COM_IIDOF(ISession),
13348 getComponentName(),
13349 tr("The VM session was closed before any attempt to power it on"));
13350 mData->mSession.mProgress.setNull();
13351 }
13352
13353 /* Create the progress object the client will use to wait until
13354 * #i_checkForDeath() is called to uninitialize this session object after
13355 * it releases the IPC semaphore.
13356 * Note! Because we're "reusing" mProgress here, this must be a proxy
13357 * object just like for LaunchVMProcess. */
13358 Assert(mData->mSession.mProgress.isNull());
13359 ComObjPtr<ProgressProxy> progress;
13360 progress.createObject();
13361 ComPtr<IUnknown> pPeer(mPeer);
13362 progress->init(mParent, pPeer,
13363 Bstr(tr("Closing session")).raw(),
13364 FALSE /* aCancelable */);
13365 progress.queryInterfaceTo(aProgress.asOutParam());
13366 mData->mSession.mProgress = progress;
13367 }
13368 else
13369 {
13370 /* the remote session is being normally closed */
13371 bool found = false;
13372 for (Data::Session::RemoteControlList::iterator
13373 it = mData->mSession.mRemoteControls.begin();
13374 it != mData->mSession.mRemoteControls.end();
13375 ++it)
13376 {
13377 if (control == *it)
13378 {
13379 found = true;
13380 // This MUST be erase(it), not remove(*it) as the latter
13381 // triggers a very nasty use after free due to the place where
13382 // the value "lives".
13383 mData->mSession.mRemoteControls.erase(it);
13384 break;
13385 }
13386 }
13387 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13388 E_INVALIDARG);
13389 }
13390
13391 /* signal the client watcher thread, because the client is going away */
13392 mParent->i_updateClientWatcher();
13393
13394 LogFlowThisFuncLeave();
13395 return S_OK;
13396}
13397
13398HRESULT SessionMachine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
13399{
13400#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13401 ULONG uID;
13402 int rc = mParent->i_onClipboardAreaRegister(aParms, &uID);
13403 if (RT_SUCCESS(rc))
13404 {
13405 if (aID)
13406 *aID = uID;
13407 return S_OK;
13408 }
13409 return E_FAIL;
13410#else
13411 RT_NOREF(aParms, aID);
13412 ReturnComNotImplemented();
13413#endif
13414}
13415
13416HRESULT SessionMachine::clipboardAreaUnregister(ULONG aID)
13417{
13418#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13419 return mParent->i_onClipboardAreaUnregister(aID);
13420#else
13421 RT_NOREF(aID);
13422 ReturnComNotImplemented();
13423#endif
13424}
13425
13426HRESULT SessionMachine::clipboardAreaAttach(ULONG aID)
13427{
13428#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13429 return mParent->i_onClipboardAreaAttach(aID);
13430#else
13431 RT_NOREF(aID);
13432 ReturnComNotImplemented();
13433#endif
13434}
13435HRESULT SessionMachine::clipboardAreaDetach(ULONG aID)
13436{
13437#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13438 return mParent->i_onClipboardAreaDetach(aID);
13439#else
13440 RT_NOREF(aID);
13441 ReturnComNotImplemented();
13442#endif
13443}
13444
13445HRESULT SessionMachine::clipboardAreaGetMostRecent(ULONG *aID)
13446{
13447#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13448 ULONG uID = mParent->i_onClipboardAreaGetMostRecent();
13449 if (aID)
13450 *aID = uID;
13451 return S_OK;
13452#else
13453 RT_NOREF(aID);
13454 ReturnComNotImplemented();
13455#endif
13456}
13457
13458HRESULT SessionMachine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
13459{
13460#ifdef VBOX_WITH_SHARED_CLIPBOARD_URI_LIST
13461 ULONG uRefCount = mParent->i_onClipboardAreaGetRefCount(aID);
13462 if (aRefCount)
13463 *aRefCount = uRefCount;
13464 return S_OK;
13465#else
13466 RT_NOREF(aID, aRefCount);
13467 ReturnComNotImplemented();
13468#endif
13469}
13470
13471HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13472 std::vector<com::Utf8Str> &aValues,
13473 std::vector<LONG64> &aTimestamps,
13474 std::vector<com::Utf8Str> &aFlags)
13475{
13476 LogFlowThisFunc(("\n"));
13477
13478#ifdef VBOX_WITH_GUEST_PROPS
13479 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13480
13481 size_t cEntries = mHWData->mGuestProperties.size();
13482 aNames.resize(cEntries);
13483 aValues.resize(cEntries);
13484 aTimestamps.resize(cEntries);
13485 aFlags.resize(cEntries);
13486
13487 size_t i = 0;
13488 for (HWData::GuestPropertyMap::const_iterator
13489 it = mHWData->mGuestProperties.begin();
13490 it != mHWData->mGuestProperties.end();
13491 ++it, ++i)
13492 {
13493 char szFlags[GUEST_PROP_MAX_FLAGS_LEN + 1];
13494 aNames[i] = it->first;
13495 aValues[i] = it->second.strValue;
13496 aTimestamps[i] = it->second.mTimestamp;
13497
13498 /* If it is NULL, keep it NULL. */
13499 if (it->second.mFlags)
13500 {
13501 GuestPropWriteFlags(it->second.mFlags, szFlags);
13502 aFlags[i] = szFlags;
13503 }
13504 else
13505 aFlags[i] = "";
13506 }
13507 return S_OK;
13508#else
13509 ReturnComNotImplemented();
13510#endif
13511}
13512
13513HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13514 const com::Utf8Str &aValue,
13515 LONG64 aTimestamp,
13516 const com::Utf8Str &aFlags)
13517{
13518 LogFlowThisFunc(("\n"));
13519
13520#ifdef VBOX_WITH_GUEST_PROPS
13521 try
13522 {
13523 /*
13524 * Convert input up front.
13525 */
13526 uint32_t fFlags = GUEST_PROP_F_NILFLAG;
13527 if (aFlags.length())
13528 {
13529 int vrc = GuestPropValidateFlags(aFlags.c_str(), &fFlags);
13530 AssertRCReturn(vrc, E_INVALIDARG);
13531 }
13532
13533 /*
13534 * Now grab the object lock, validate the state and do the update.
13535 */
13536
13537 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13538
13539 if (!Global::IsOnline(mData->mMachineState))
13540 {
13541 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13542 VBOX_E_INVALID_VM_STATE);
13543 }
13544
13545 i_setModified(IsModified_MachineData);
13546 mHWData.backup();
13547
13548 bool fDelete = !aValue.length();
13549 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13550 if (it != mHWData->mGuestProperties.end())
13551 {
13552 if (!fDelete)
13553 {
13554 it->second.strValue = aValue;
13555 it->second.mTimestamp = aTimestamp;
13556 it->second.mFlags = fFlags;
13557 }
13558 else
13559 mHWData->mGuestProperties.erase(it);
13560
13561 mData->mGuestPropertiesModified = TRUE;
13562 }
13563 else if (!fDelete)
13564 {
13565 HWData::GuestProperty prop;
13566 prop.strValue = aValue;
13567 prop.mTimestamp = aTimestamp;
13568 prop.mFlags = fFlags;
13569
13570 mHWData->mGuestProperties[aName] = prop;
13571 mData->mGuestPropertiesModified = TRUE;
13572 }
13573
13574 alock.release();
13575
13576 mParent->i_onGuestPropertyChange(mData->mUuid,
13577 Bstr(aName).raw(),
13578 Bstr(aValue).raw(),
13579 Bstr(aFlags).raw());
13580 }
13581 catch (...)
13582 {
13583 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13584 }
13585 return S_OK;
13586#else
13587 ReturnComNotImplemented();
13588#endif
13589}
13590
13591
13592HRESULT SessionMachine::lockMedia()
13593{
13594 AutoMultiWriteLock2 alock(this->lockHandle(),
13595 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13596
13597 AssertReturn( mData->mMachineState == MachineState_Starting
13598 || mData->mMachineState == MachineState_Restoring
13599 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13600
13601 clearError();
13602 alock.release();
13603 return i_lockMedia();
13604}
13605
13606HRESULT SessionMachine::unlockMedia()
13607{
13608 HRESULT hrc = i_unlockMedia();
13609 return hrc;
13610}
13611
13612HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13613 ComPtr<IMediumAttachment> &aNewAttachment)
13614{
13615 // request the host lock first, since might be calling Host methods for getting host drives;
13616 // next, protect the media tree all the while we're in here, as well as our member variables
13617 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13618 this->lockHandle(),
13619 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13620
13621 IMediumAttachment *iAttach = aAttachment;
13622 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13623
13624 Utf8Str ctrlName;
13625 LONG lPort;
13626 LONG lDevice;
13627 bool fTempEject;
13628 {
13629 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13630
13631 /* Need to query the details first, as the IMediumAttachment reference
13632 * might be to the original settings, which we are going to change. */
13633 ctrlName = pAttach->i_getControllerName();
13634 lPort = pAttach->i_getPort();
13635 lDevice = pAttach->i_getDevice();
13636 fTempEject = pAttach->i_getTempEject();
13637 }
13638
13639 if (!fTempEject)
13640 {
13641 /* Remember previously mounted medium. The medium before taking the
13642 * backup is not necessarily the same thing. */
13643 ComObjPtr<Medium> oldmedium;
13644 oldmedium = pAttach->i_getMedium();
13645
13646 i_setModified(IsModified_Storage);
13647 mMediumAttachments.backup();
13648
13649 // The backup operation makes the pAttach reference point to the
13650 // old settings. Re-get the correct reference.
13651 pAttach = i_findAttachment(*mMediumAttachments.data(),
13652 ctrlName,
13653 lPort,
13654 lDevice);
13655
13656 {
13657 AutoCaller autoAttachCaller(this);
13658 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13659
13660 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13661 if (!oldmedium.isNull())
13662 oldmedium->i_removeBackReference(mData->mUuid);
13663
13664 pAttach->i_updateMedium(NULL);
13665 pAttach->i_updateEjected();
13666 }
13667
13668 i_setModified(IsModified_Storage);
13669 }
13670 else
13671 {
13672 {
13673 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13674 pAttach->i_updateEjected();
13675 }
13676 }
13677
13678 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13679
13680 return S_OK;
13681}
13682
13683HRESULT SessionMachine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
13684 com::Utf8Str &aResult)
13685{
13686 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13687
13688 HRESULT hr = S_OK;
13689
13690 if (!mAuthLibCtx.hAuthLibrary)
13691 {
13692 /* Load the external authentication library. */
13693 Bstr authLibrary;
13694 mVRDEServer->COMGETTER(AuthLibrary)(authLibrary.asOutParam());
13695
13696 Utf8Str filename = authLibrary;
13697
13698 int vrc = AuthLibLoad(&mAuthLibCtx, filename.c_str());
13699 if (RT_FAILURE(vrc))
13700 hr = setErrorBoth(E_FAIL, vrc,
13701 tr("Could not load the external authentication library '%s' (%Rrc)"),
13702 filename.c_str(), vrc);
13703 }
13704
13705 /* The auth library might need the machine lock. */
13706 alock.release();
13707
13708 if (FAILED(hr))
13709 return hr;
13710
13711 if (aAuthParams[0] == "VRDEAUTH" && aAuthParams.size() == 7)
13712 {
13713 enum VRDEAuthParams
13714 {
13715 parmUuid = 1,
13716 parmGuestJudgement,
13717 parmUser,
13718 parmPassword,
13719 parmDomain,
13720 parmClientId
13721 };
13722
13723 AuthResult result = AuthResultAccessDenied;
13724
13725 Guid uuid(aAuthParams[parmUuid]);
13726 AuthGuestJudgement guestJudgement = (AuthGuestJudgement)aAuthParams[parmGuestJudgement].toUInt32();
13727 uint32_t u32ClientId = aAuthParams[parmClientId].toUInt32();
13728
13729 result = AuthLibAuthenticate(&mAuthLibCtx,
13730 uuid.raw(), guestJudgement,
13731 aAuthParams[parmUser].c_str(),
13732 aAuthParams[parmPassword].c_str(),
13733 aAuthParams[parmDomain].c_str(),
13734 u32ClientId);
13735
13736 /* Hack: aAuthParams[parmPassword] is const but the code believes in writable memory. */
13737 size_t cbPassword = aAuthParams[parmPassword].length();
13738 if (cbPassword)
13739 {
13740 RTMemWipeThoroughly((void *)aAuthParams[parmPassword].c_str(), cbPassword, 10 /* cPasses */);
13741 memset((void *)aAuthParams[parmPassword].c_str(), 'x', cbPassword);
13742 }
13743
13744 if (result == AuthResultAccessGranted)
13745 aResult = "granted";
13746 else
13747 aResult = "denied";
13748
13749 LogRel(("AUTH: VRDE authentification for user '%s' result '%s'\n",
13750 aAuthParams[parmUser].c_str(), aResult.c_str()));
13751 }
13752 else if (aAuthParams[0] == "VRDEAUTHDISCONNECT" && aAuthParams.size() == 3)
13753 {
13754 enum VRDEAuthDisconnectParams
13755 {
13756 parmUuid = 1,
13757 parmClientId
13758 };
13759
13760 Guid uuid(aAuthParams[parmUuid]);
13761 uint32_t u32ClientId = 0;
13762 AuthLibDisconnect(&mAuthLibCtx, uuid.raw(), u32ClientId);
13763 }
13764 else
13765 {
13766 hr = E_INVALIDARG;
13767 }
13768
13769 return hr;
13770}
13771
13772// public methods only for internal purposes
13773/////////////////////////////////////////////////////////////////////////////
13774
13775#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13776/**
13777 * Called from the client watcher thread to check for expected or unexpected
13778 * death of the client process that has a direct session to this machine.
13779 *
13780 * On Win32 and on OS/2, this method is called only when we've got the
13781 * mutex (i.e. the client has either died or terminated normally) so it always
13782 * returns @c true (the client is terminated, the session machine is
13783 * uninitialized).
13784 *
13785 * On other platforms, the method returns @c true if the client process has
13786 * terminated normally or abnormally and the session machine was uninitialized,
13787 * and @c false if the client process is still alive.
13788 *
13789 * @note Locks this object for writing.
13790 */
13791bool SessionMachine::i_checkForDeath()
13792{
13793 Uninit::Reason reason;
13794 bool terminated = false;
13795
13796 /* Enclose autoCaller with a block because calling uninit() from under it
13797 * will deadlock. */
13798 {
13799 AutoCaller autoCaller(this);
13800 if (!autoCaller.isOk())
13801 {
13802 /* return true if not ready, to cause the client watcher to exclude
13803 * the corresponding session from watching */
13804 LogFlowThisFunc(("Already uninitialized!\n"));
13805 return true;
13806 }
13807
13808 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13809
13810 /* Determine the reason of death: if the session state is Closing here,
13811 * everything is fine. Otherwise it means that the client did not call
13812 * OnSessionEnd() before it released the IPC semaphore. This may happen
13813 * either because the client process has abnormally terminated, or
13814 * because it simply forgot to call ISession::Close() before exiting. We
13815 * threat the latter also as an abnormal termination (see
13816 * Session::uninit() for details). */
13817 reason = mData->mSession.mState == SessionState_Unlocking ?
13818 Uninit::Normal :
13819 Uninit::Abnormal;
13820
13821 if (mClientToken)
13822 terminated = mClientToken->release();
13823 } /* AutoCaller block */
13824
13825 if (terminated)
13826 uninit(reason);
13827
13828 return terminated;
13829}
13830
13831void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13832{
13833 LogFlowThisFunc(("\n"));
13834
13835 strTokenId.setNull();
13836
13837 AutoCaller autoCaller(this);
13838 AssertComRCReturnVoid(autoCaller.rc());
13839
13840 Assert(mClientToken);
13841 if (mClientToken)
13842 mClientToken->getId(strTokenId);
13843}
13844#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13845IToken *SessionMachine::i_getToken()
13846{
13847 LogFlowThisFunc(("\n"));
13848
13849 AutoCaller autoCaller(this);
13850 AssertComRCReturn(autoCaller.rc(), NULL);
13851
13852 Assert(mClientToken);
13853 if (mClientToken)
13854 return mClientToken->getToken();
13855 else
13856 return NULL;
13857}
13858#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13859
13860Machine::ClientToken *SessionMachine::i_getClientToken()
13861{
13862 LogFlowThisFunc(("\n"));
13863
13864 AutoCaller autoCaller(this);
13865 AssertComRCReturn(autoCaller.rc(), NULL);
13866
13867 return mClientToken;
13868}
13869
13870
13871/**
13872 * @note Locks this object for reading.
13873 */
13874HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13875{
13876 LogFlowThisFunc(("\n"));
13877
13878 AutoCaller autoCaller(this);
13879 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13880
13881 ComPtr<IInternalSessionControl> directControl;
13882 {
13883 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13884 if (mData->mSession.mLockType == LockType_VM)
13885 directControl = mData->mSession.mDirectControl;
13886 }
13887
13888 /* ignore notifications sent after #OnSessionEnd() is called */
13889 if (!directControl)
13890 return S_OK;
13891
13892 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13893}
13894
13895/**
13896 * @note Locks this object for reading.
13897 */
13898HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13899 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13900 IN_BSTR aGuestIp, LONG aGuestPort)
13901{
13902 LogFlowThisFunc(("\n"));
13903
13904 AutoCaller autoCaller(this);
13905 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13906
13907 ComPtr<IInternalSessionControl> directControl;
13908 {
13909 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13910 if (mData->mSession.mLockType == LockType_VM)
13911 directControl = mData->mSession.mDirectControl;
13912 }
13913
13914 /* ignore notifications sent after #OnSessionEnd() is called */
13915 if (!directControl)
13916 return S_OK;
13917 /*
13918 * instead acting like callback we ask IVirtualBox deliver corresponding event
13919 */
13920
13921 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13922 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13923 return S_OK;
13924}
13925
13926/**
13927 * @note Locks this object for reading.
13928 */
13929HRESULT SessionMachine::i_onAudioAdapterChange(IAudioAdapter *audioAdapter)
13930{
13931 LogFlowThisFunc(("\n"));
13932
13933 AutoCaller autoCaller(this);
13934 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13935
13936 ComPtr<IInternalSessionControl> directControl;
13937 {
13938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13939 if (mData->mSession.mLockType == LockType_VM)
13940 directControl = mData->mSession.mDirectControl;
13941 }
13942
13943 /* ignore notifications sent after #OnSessionEnd() is called */
13944 if (!directControl)
13945 return S_OK;
13946
13947 return directControl->OnAudioAdapterChange(audioAdapter);
13948}
13949
13950/**
13951 * @note Locks this object for reading.
13952 */
13953HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13954{
13955 LogFlowThisFunc(("\n"));
13956
13957 AutoCaller autoCaller(this);
13958 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13959
13960 ComPtr<IInternalSessionControl> directControl;
13961 {
13962 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13963 if (mData->mSession.mLockType == LockType_VM)
13964 directControl = mData->mSession.mDirectControl;
13965 }
13966
13967 /* ignore notifications sent after #OnSessionEnd() is called */
13968 if (!directControl)
13969 return S_OK;
13970
13971 return directControl->OnSerialPortChange(serialPort);
13972}
13973
13974/**
13975 * @note Locks this object for reading.
13976 */
13977HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13978{
13979 LogFlowThisFunc(("\n"));
13980
13981 AutoCaller autoCaller(this);
13982 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13983
13984 ComPtr<IInternalSessionControl> directControl;
13985 {
13986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13987 if (mData->mSession.mLockType == LockType_VM)
13988 directControl = mData->mSession.mDirectControl;
13989 }
13990
13991 /* ignore notifications sent after #OnSessionEnd() is called */
13992 if (!directControl)
13993 return S_OK;
13994
13995 return directControl->OnParallelPortChange(parallelPort);
13996}
13997
13998/**
13999 * @note Locks this object for reading.
14000 */
14001HRESULT SessionMachine::i_onStorageControllerChange(const Guid &aMachineId, const Utf8Str &aControllerName)
14002{
14003 LogFlowThisFunc(("\n"));
14004
14005 AutoCaller autoCaller(this);
14006 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14007
14008 ComPtr<IInternalSessionControl> directControl;
14009 {
14010 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14011 if (mData->mSession.mLockType == LockType_VM)
14012 directControl = mData->mSession.mDirectControl;
14013 }
14014
14015 mParent->i_onStorageControllerChanged(aMachineId, aControllerName);
14016
14017 /* ignore notifications sent after #OnSessionEnd() is called */
14018 if (!directControl)
14019 return S_OK;
14020
14021 return directControl->OnStorageControllerChange(Bstr(aMachineId.toString()).raw(), Bstr(aControllerName).raw());
14022}
14023
14024/**
14025 * @note Locks this object for reading.
14026 */
14027HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
14028{
14029 LogFlowThisFunc(("\n"));
14030
14031 AutoCaller autoCaller(this);
14032 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14033
14034 ComPtr<IInternalSessionControl> directControl;
14035 {
14036 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14037 if (mData->mSession.mLockType == LockType_VM)
14038 directControl = mData->mSession.mDirectControl;
14039 }
14040
14041 mParent->i_onMediumChanged(aAttachment);
14042
14043 /* ignore notifications sent after #OnSessionEnd() is called */
14044 if (!directControl)
14045 return S_OK;
14046
14047 return directControl->OnMediumChange(aAttachment, aForce);
14048}
14049
14050HRESULT SessionMachine::i_onVMProcessPriorityChange(VMProcPriority_T aPriority)
14051{
14052 LogFlowThisFunc(("\n"));
14053
14054 AutoCaller autoCaller(this);
14055 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14056
14057 ComPtr<IInternalSessionControl> directControl;
14058 {
14059 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14060 if (mData->mSession.mLockType == LockType_VM)
14061 directControl = mData->mSession.mDirectControl;
14062 }
14063
14064 /* ignore notifications sent after #OnSessionEnd() is called */
14065 if (!directControl)
14066 return S_OK;
14067
14068 return directControl->OnVMProcessPriorityChange(aPriority);
14069}
14070
14071/**
14072 * @note Locks this object for reading.
14073 */
14074HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
14075{
14076 LogFlowThisFunc(("\n"));
14077
14078 AutoCaller autoCaller(this);
14079 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14080
14081 ComPtr<IInternalSessionControl> directControl;
14082 {
14083 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14084 if (mData->mSession.mLockType == LockType_VM)
14085 directControl = mData->mSession.mDirectControl;
14086 }
14087
14088 /* ignore notifications sent after #OnSessionEnd() is called */
14089 if (!directControl)
14090 return S_OK;
14091
14092 return directControl->OnCPUChange(aCPU, aRemove);
14093}
14094
14095HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
14096{
14097 LogFlowThisFunc(("\n"));
14098
14099 AutoCaller autoCaller(this);
14100 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14101
14102 ComPtr<IInternalSessionControl> directControl;
14103 {
14104 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14105 if (mData->mSession.mLockType == LockType_VM)
14106 directControl = mData->mSession.mDirectControl;
14107 }
14108
14109 /* ignore notifications sent after #OnSessionEnd() is called */
14110 if (!directControl)
14111 return S_OK;
14112
14113 return directControl->OnCPUExecutionCapChange(aExecutionCap);
14114}
14115
14116/**
14117 * @note Locks this object for reading.
14118 */
14119HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
14120{
14121 LogFlowThisFunc(("\n"));
14122
14123 AutoCaller autoCaller(this);
14124 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14125
14126 ComPtr<IInternalSessionControl> directControl;
14127 {
14128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14129 if (mData->mSession.mLockType == LockType_VM)
14130 directControl = mData->mSession.mDirectControl;
14131 }
14132
14133 /* ignore notifications sent after #OnSessionEnd() is called */
14134 if (!directControl)
14135 return S_OK;
14136
14137 return directControl->OnVRDEServerChange(aRestart);
14138}
14139
14140/**
14141 * @note Locks this object for reading.
14142 */
14143HRESULT SessionMachine::i_onRecordingChange(BOOL aEnable)
14144{
14145 LogFlowThisFunc(("\n"));
14146
14147 AutoCaller autoCaller(this);
14148 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14149
14150 ComPtr<IInternalSessionControl> directControl;
14151 {
14152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14153 if (mData->mSession.mLockType == LockType_VM)
14154 directControl = mData->mSession.mDirectControl;
14155 }
14156
14157 /* ignore notifications sent after #OnSessionEnd() is called */
14158 if (!directControl)
14159 return S_OK;
14160
14161 return directControl->OnRecordingChange(aEnable);
14162}
14163
14164/**
14165 * @note Locks this object for reading.
14166 */
14167HRESULT SessionMachine::i_onUSBControllerChange()
14168{
14169 LogFlowThisFunc(("\n"));
14170
14171 AutoCaller autoCaller(this);
14172 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14173
14174 ComPtr<IInternalSessionControl> directControl;
14175 {
14176 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14177 if (mData->mSession.mLockType == LockType_VM)
14178 directControl = mData->mSession.mDirectControl;
14179 }
14180
14181 /* ignore notifications sent after #OnSessionEnd() is called */
14182 if (!directControl)
14183 return S_OK;
14184
14185 return directControl->OnUSBControllerChange();
14186}
14187
14188/**
14189 * @note Locks this object for reading.
14190 */
14191HRESULT SessionMachine::i_onSharedFolderChange()
14192{
14193 LogFlowThisFunc(("\n"));
14194
14195 AutoCaller autoCaller(this);
14196 AssertComRCReturnRC(autoCaller.rc());
14197
14198 ComPtr<IInternalSessionControl> directControl;
14199 {
14200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14201 if (mData->mSession.mLockType == LockType_VM)
14202 directControl = mData->mSession.mDirectControl;
14203 }
14204
14205 /* ignore notifications sent after #OnSessionEnd() is called */
14206 if (!directControl)
14207 return S_OK;
14208
14209 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
14210}
14211
14212/**
14213 * @note Locks this object for reading.
14214 */
14215HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
14216{
14217 LogFlowThisFunc(("\n"));
14218
14219 AutoCaller autoCaller(this);
14220 AssertComRCReturnRC(autoCaller.rc());
14221
14222 ComPtr<IInternalSessionControl> directControl;
14223 {
14224 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14225 if (mData->mSession.mLockType == LockType_VM)
14226 directControl = mData->mSession.mDirectControl;
14227 }
14228
14229 /* ignore notifications sent after #OnSessionEnd() is called */
14230 if (!directControl)
14231 return S_OK;
14232
14233 return directControl->OnClipboardModeChange(aClipboardMode);
14234}
14235
14236/**
14237 * @note Locks this object for reading.
14238 */
14239HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
14240{
14241 LogFlowThisFunc(("\n"));
14242
14243 AutoCaller autoCaller(this);
14244 AssertComRCReturnRC(autoCaller.rc());
14245
14246 ComPtr<IInternalSessionControl> directControl;
14247 {
14248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14249 if (mData->mSession.mLockType == LockType_VM)
14250 directControl = mData->mSession.mDirectControl;
14251 }
14252
14253 /* ignore notifications sent after #OnSessionEnd() is called */
14254 if (!directControl)
14255 return S_OK;
14256
14257 return directControl->OnDnDModeChange(aDnDMode);
14258}
14259
14260/**
14261 * @note Locks this object for reading.
14262 */
14263HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
14264{
14265 LogFlowThisFunc(("\n"));
14266
14267 AutoCaller autoCaller(this);
14268 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14269
14270 ComPtr<IInternalSessionControl> directControl;
14271 {
14272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14273 if (mData->mSession.mLockType == LockType_VM)
14274 directControl = mData->mSession.mDirectControl;
14275 }
14276
14277 /* ignore notifications sent after #OnSessionEnd() is called */
14278 if (!directControl)
14279 return S_OK;
14280
14281 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14282}
14283
14284/**
14285 * @note Locks this object for reading.
14286 */
14287HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14288{
14289 LogFlowThisFunc(("\n"));
14290
14291 AutoCaller autoCaller(this);
14292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14293
14294 ComPtr<IInternalSessionControl> directControl;
14295 {
14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14297 if (mData->mSession.mLockType == LockType_VM)
14298 directControl = mData->mSession.mDirectControl;
14299 }
14300
14301 /* ignore notifications sent after #OnSessionEnd() is called */
14302 if (!directControl)
14303 return S_OK;
14304
14305 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14306}
14307
14308/**
14309 * Returns @c true if this machine's USB controller reports it has a matching
14310 * filter for the given USB device and @c false otherwise.
14311 *
14312 * @note locks this object for reading.
14313 */
14314bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14315{
14316 AutoCaller autoCaller(this);
14317 /* silently return if not ready -- this method may be called after the
14318 * direct machine session has been called */
14319 if (!autoCaller.isOk())
14320 return false;
14321
14322#ifdef VBOX_WITH_USB
14323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14324
14325 switch (mData->mMachineState)
14326 {
14327 case MachineState_Starting:
14328 case MachineState_Restoring:
14329 case MachineState_TeleportingIn:
14330 case MachineState_Paused:
14331 case MachineState_Running:
14332 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14333 * elsewhere... */
14334 alock.release();
14335 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14336 default: break;
14337 }
14338#else
14339 NOREF(aDevice);
14340 NOREF(aMaskedIfs);
14341#endif
14342 return false;
14343}
14344
14345/**
14346 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14347 */
14348HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14349 IVirtualBoxErrorInfo *aError,
14350 ULONG aMaskedIfs,
14351 const com::Utf8Str &aCaptureFilename)
14352{
14353 LogFlowThisFunc(("\n"));
14354
14355 AutoCaller autoCaller(this);
14356
14357 /* This notification may happen after the machine object has been
14358 * uninitialized (the session was closed), so don't assert. */
14359 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14360
14361 ComPtr<IInternalSessionControl> directControl;
14362 {
14363 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14364 if (mData->mSession.mLockType == LockType_VM)
14365 directControl = mData->mSession.mDirectControl;
14366 }
14367
14368 /* fail on notifications sent after #OnSessionEnd() is called, it is
14369 * expected by the caller */
14370 if (!directControl)
14371 return E_FAIL;
14372
14373 /* No locks should be held at this point. */
14374 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14375 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14376
14377 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14378}
14379
14380/**
14381 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14382 */
14383HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14384 IVirtualBoxErrorInfo *aError)
14385{
14386 LogFlowThisFunc(("\n"));
14387
14388 AutoCaller autoCaller(this);
14389
14390 /* This notification may happen after the machine object has been
14391 * uninitialized (the session was closed), so don't assert. */
14392 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14393
14394 ComPtr<IInternalSessionControl> directControl;
14395 {
14396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14397 if (mData->mSession.mLockType == LockType_VM)
14398 directControl = mData->mSession.mDirectControl;
14399 }
14400
14401 /* fail on notifications sent after #OnSessionEnd() is called, it is
14402 * expected by the caller */
14403 if (!directControl)
14404 return E_FAIL;
14405
14406 /* No locks should be held at this point. */
14407 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14408 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14409
14410 return directControl->OnUSBDeviceDetach(aId, aError);
14411}
14412
14413// protected methods
14414/////////////////////////////////////////////////////////////////////////////
14415
14416/**
14417 * Deletes the given file if it is no longer in use by either the current machine state
14418 * (if the machine is "saved") or any of the machine's snapshots.
14419 *
14420 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14421 * but is different for each SnapshotMachine. When calling this, the order of calling this
14422 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14423 * is therefore critical. I know, it's all rather messy.
14424 *
14425 * @param strStateFile
14426 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14427 * the test for whether the saved state file is in use.
14428 */
14429void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14430 Snapshot *pSnapshotToIgnore)
14431{
14432 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14433 if ( (strStateFile.isNotEmpty())
14434 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14435 )
14436 // ... and it must also not be shared with other snapshots
14437 if ( !mData->mFirstSnapshot
14438 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14439 // this checks the SnapshotMachine's state file paths
14440 )
14441 RTFileDelete(strStateFile.c_str());
14442}
14443
14444/**
14445 * Locks the attached media.
14446 *
14447 * All attached hard disks are locked for writing and DVD/floppy are locked for
14448 * reading. Parents of attached hard disks (if any) are locked for reading.
14449 *
14450 * This method also performs accessibility check of all media it locks: if some
14451 * media is inaccessible, the method will return a failure and a bunch of
14452 * extended error info objects per each inaccessible medium.
14453 *
14454 * Note that this method is atomic: if it returns a success, all media are
14455 * locked as described above; on failure no media is locked at all (all
14456 * succeeded individual locks will be undone).
14457 *
14458 * The caller is responsible for doing the necessary state sanity checks.
14459 *
14460 * The locks made by this method must be undone by calling #unlockMedia() when
14461 * no more needed.
14462 */
14463HRESULT SessionMachine::i_lockMedia()
14464{
14465 AutoCaller autoCaller(this);
14466 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14467
14468 AutoMultiWriteLock2 alock(this->lockHandle(),
14469 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14470
14471 /* bail out if trying to lock things with already set up locking */
14472 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14473
14474 MultiResult mrc(S_OK);
14475
14476 /* Collect locking information for all medium objects attached to the VM. */
14477 for (MediumAttachmentList::const_iterator
14478 it = mMediumAttachments->begin();
14479 it != mMediumAttachments->end();
14480 ++it)
14481 {
14482 MediumAttachment *pAtt = *it;
14483 DeviceType_T devType = pAtt->i_getType();
14484 Medium *pMedium = pAtt->i_getMedium();
14485
14486 MediumLockList *pMediumLockList(new MediumLockList());
14487 // There can be attachments without a medium (floppy/dvd), and thus
14488 // it's impossible to create a medium lock list. It still makes sense
14489 // to have the empty medium lock list in the map in case a medium is
14490 // attached later.
14491 if (pMedium != NULL)
14492 {
14493 MediumType_T mediumType = pMedium->i_getType();
14494 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14495 || mediumType == MediumType_Shareable;
14496 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14497
14498 alock.release();
14499 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14500 !fIsReadOnlyLock ? pMedium : NULL /* pToLockWrite */,
14501 false /* fMediumLockWriteAll */,
14502 NULL,
14503 *pMediumLockList);
14504 alock.acquire();
14505 if (FAILED(mrc))
14506 {
14507 delete pMediumLockList;
14508 mData->mSession.mLockedMedia.Clear();
14509 break;
14510 }
14511 }
14512
14513 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14514 if (FAILED(rc))
14515 {
14516 mData->mSession.mLockedMedia.Clear();
14517 mrc = setError(rc,
14518 tr("Collecting locking information for all attached media failed"));
14519 break;
14520 }
14521 }
14522
14523 if (SUCCEEDED(mrc))
14524 {
14525 /* Now lock all media. If this fails, nothing is locked. */
14526 alock.release();
14527 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14528 alock.acquire();
14529 if (FAILED(rc))
14530 {
14531 mrc = setError(rc,
14532 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14533 }
14534 }
14535
14536 return mrc;
14537}
14538
14539/**
14540 * Undoes the locks made by by #lockMedia().
14541 */
14542HRESULT SessionMachine::i_unlockMedia()
14543{
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14546
14547 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14548
14549 /* we may be holding important error info on the current thread;
14550 * preserve it */
14551 ErrorInfoKeeper eik;
14552
14553 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14554 AssertComRC(rc);
14555 return rc;
14556}
14557
14558/**
14559 * Helper to change the machine state (reimplementation).
14560 *
14561 * @note Locks this object for writing.
14562 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14563 * it can cause crashes in random places due to unexpectedly committing
14564 * the current settings. The caller is responsible for that. The call
14565 * to saveStateSettings is fine, because this method does not commit.
14566 */
14567HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14568{
14569 LogFlowThisFuncEnter();
14570 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14571
14572 AutoCaller autoCaller(this);
14573 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14574
14575 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14576
14577 MachineState_T oldMachineState = mData->mMachineState;
14578
14579 AssertMsgReturn(oldMachineState != aMachineState,
14580 ("oldMachineState=%s, aMachineState=%s\n",
14581 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14582 E_FAIL);
14583
14584 HRESULT rc = S_OK;
14585
14586 int stsFlags = 0;
14587 bool deleteSavedState = false;
14588
14589 /* detect some state transitions */
14590
14591 if ( ( oldMachineState == MachineState_Saved
14592 && aMachineState == MachineState_Restoring)
14593 || ( ( oldMachineState == MachineState_PoweredOff
14594 || oldMachineState == MachineState_Teleported
14595 || oldMachineState == MachineState_Aborted
14596 )
14597 && ( aMachineState == MachineState_TeleportingIn
14598 || aMachineState == MachineState_Starting
14599 )
14600 )
14601 )
14602 {
14603 /* The EMT thread is about to start */
14604
14605 /* Nothing to do here for now... */
14606
14607 /// @todo NEWMEDIA don't let mDVDDrive and other children
14608 /// change anything when in the Starting/Restoring state
14609 }
14610 else if ( ( oldMachineState == MachineState_Running
14611 || oldMachineState == MachineState_Paused
14612 || oldMachineState == MachineState_Teleporting
14613 || oldMachineState == MachineState_OnlineSnapshotting
14614 || oldMachineState == MachineState_LiveSnapshotting
14615 || oldMachineState == MachineState_Stuck
14616 || oldMachineState == MachineState_Starting
14617 || oldMachineState == MachineState_Stopping
14618 || oldMachineState == MachineState_Saving
14619 || oldMachineState == MachineState_Restoring
14620 || oldMachineState == MachineState_TeleportingPausedVM
14621 || oldMachineState == MachineState_TeleportingIn
14622 )
14623 && ( aMachineState == MachineState_PoweredOff
14624 || aMachineState == MachineState_Saved
14625 || aMachineState == MachineState_Teleported
14626 || aMachineState == MachineState_Aborted
14627 )
14628 )
14629 {
14630 /* The EMT thread has just stopped, unlock attached media. Note that as
14631 * opposed to locking that is done from Console, we do unlocking here
14632 * because the VM process may have aborted before having a chance to
14633 * properly unlock all media it locked. */
14634
14635 unlockMedia();
14636 }
14637
14638 if (oldMachineState == MachineState_Restoring)
14639 {
14640 if (aMachineState != MachineState_Saved)
14641 {
14642 /*
14643 * delete the saved state file once the machine has finished
14644 * restoring from it (note that Console sets the state from
14645 * Restoring to Saved if the VM couldn't restore successfully,
14646 * to give the user an ability to fix an error and retry --
14647 * we keep the saved state file in this case)
14648 */
14649 deleteSavedState = true;
14650 }
14651 }
14652 else if ( oldMachineState == MachineState_Saved
14653 && ( aMachineState == MachineState_PoweredOff
14654 || aMachineState == MachineState_Aborted
14655 || aMachineState == MachineState_Teleported
14656 )
14657 )
14658 {
14659 /*
14660 * delete the saved state after SessionMachine::ForgetSavedState() is called
14661 * or if the VM process (owning a direct VM session) crashed while the
14662 * VM was Saved
14663 */
14664
14665 /// @todo (dmik)
14666 // Not sure that deleting the saved state file just because of the
14667 // client death before it attempted to restore the VM is a good
14668 // thing. But when it crashes we need to go to the Aborted state
14669 // which cannot have the saved state file associated... The only
14670 // way to fix this is to make the Aborted condition not a VM state
14671 // but a bool flag: i.e., when a crash occurs, set it to true and
14672 // change the state to PoweredOff or Saved depending on the
14673 // saved state presence.
14674
14675 deleteSavedState = true;
14676 mData->mCurrentStateModified = TRUE;
14677 stsFlags |= SaveSTS_CurStateModified;
14678 }
14679
14680 if ( aMachineState == MachineState_Starting
14681 || aMachineState == MachineState_Restoring
14682 || aMachineState == MachineState_TeleportingIn
14683 )
14684 {
14685 /* set the current state modified flag to indicate that the current
14686 * state is no more identical to the state in the
14687 * current snapshot */
14688 if (!mData->mCurrentSnapshot.isNull())
14689 {
14690 mData->mCurrentStateModified = TRUE;
14691 stsFlags |= SaveSTS_CurStateModified;
14692 }
14693 }
14694
14695 if (deleteSavedState)
14696 {
14697 if (mRemoveSavedState)
14698 {
14699 Assert(!mSSData->strStateFilePath.isEmpty());
14700
14701 // it is safe to delete the saved state file if ...
14702 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14703 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14704 // ... none of the snapshots share the saved state file
14705 )
14706 RTFileDelete(mSSData->strStateFilePath.c_str());
14707 }
14708
14709 mSSData->strStateFilePath.setNull();
14710 stsFlags |= SaveSTS_StateFilePath;
14711 }
14712
14713 /* redirect to the underlying peer machine */
14714 mPeer->i_setMachineState(aMachineState);
14715
14716 if ( oldMachineState != MachineState_RestoringSnapshot
14717 && ( aMachineState == MachineState_PoweredOff
14718 || aMachineState == MachineState_Teleported
14719 || aMachineState == MachineState_Aborted
14720 || aMachineState == MachineState_Saved))
14721 {
14722 /* the machine has stopped execution
14723 * (or the saved state file was adopted) */
14724 stsFlags |= SaveSTS_StateTimeStamp;
14725 }
14726
14727 if ( ( oldMachineState == MachineState_PoweredOff
14728 || oldMachineState == MachineState_Aborted
14729 || oldMachineState == MachineState_Teleported
14730 )
14731 && aMachineState == MachineState_Saved)
14732 {
14733 /* the saved state file was adopted */
14734 Assert(!mSSData->strStateFilePath.isEmpty());
14735 stsFlags |= SaveSTS_StateFilePath;
14736 }
14737
14738#ifdef VBOX_WITH_GUEST_PROPS
14739 if ( aMachineState == MachineState_PoweredOff
14740 || aMachineState == MachineState_Aborted
14741 || aMachineState == MachineState_Teleported)
14742 {
14743 /* Make sure any transient guest properties get removed from the
14744 * property store on shutdown. */
14745 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14746
14747 /* remove it from the settings representation */
14748 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14749 for (settings::GuestPropertiesList::iterator
14750 it = llGuestProperties.begin();
14751 it != llGuestProperties.end();
14752 /*nothing*/)
14753 {
14754 const settings::GuestProperty &prop = *it;
14755 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14756 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14757 {
14758 it = llGuestProperties.erase(it);
14759 fNeedsSaving = true;
14760 }
14761 else
14762 {
14763 ++it;
14764 }
14765 }
14766
14767 /* Additionally remove it from the HWData representation. Required to
14768 * keep everything in sync, as this is what the API keeps using. */
14769 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14770 for (HWData::GuestPropertyMap::iterator
14771 it = llHWGuestProperties.begin();
14772 it != llHWGuestProperties.end();
14773 /*nothing*/)
14774 {
14775 uint32_t fFlags = it->second.mFlags;
14776 if (fFlags & (GUEST_PROP_F_TRANSIENT | GUEST_PROP_F_TRANSRESET))
14777 {
14778 /* iterator where we need to continue after the erase call
14779 * (C++03 is a fact still, and it doesn't return the iterator
14780 * which would allow continuing) */
14781 HWData::GuestPropertyMap::iterator it2 = it;
14782 ++it2;
14783 llHWGuestProperties.erase(it);
14784 it = it2;
14785 fNeedsSaving = true;
14786 }
14787 else
14788 {
14789 ++it;
14790 }
14791 }
14792
14793 if (fNeedsSaving)
14794 {
14795 mData->mCurrentStateModified = TRUE;
14796 stsFlags |= SaveSTS_CurStateModified;
14797 }
14798 }
14799#endif /* VBOX_WITH_GUEST_PROPS */
14800
14801 rc = i_saveStateSettings(stsFlags);
14802
14803 if ( ( oldMachineState != MachineState_PoweredOff
14804 && oldMachineState != MachineState_Aborted
14805 && oldMachineState != MachineState_Teleported
14806 )
14807 && ( aMachineState == MachineState_PoweredOff
14808 || aMachineState == MachineState_Aborted
14809 || aMachineState == MachineState_Teleported
14810 )
14811 )
14812 {
14813 /* we've been shut down for any reason */
14814 /* no special action so far */
14815 }
14816
14817 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14818 LogFlowThisFuncLeave();
14819 return rc;
14820}
14821
14822/**
14823 * Sends the current machine state value to the VM process.
14824 *
14825 * @note Locks this object for reading, then calls a client process.
14826 */
14827HRESULT SessionMachine::i_updateMachineStateOnClient()
14828{
14829 AutoCaller autoCaller(this);
14830 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14831
14832 ComPtr<IInternalSessionControl> directControl;
14833 {
14834 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14835 AssertReturn(!!mData, E_FAIL);
14836 if (mData->mSession.mLockType == LockType_VM)
14837 directControl = mData->mSession.mDirectControl;
14838
14839 /* directControl may be already set to NULL here in #OnSessionEnd()
14840 * called too early by the direct session process while there is still
14841 * some operation (like deleting the snapshot) in progress. The client
14842 * process in this case is waiting inside Session::close() for the
14843 * "end session" process object to complete, while #uninit() called by
14844 * #i_checkForDeath() on the Watcher thread is waiting for the pending
14845 * operation to complete. For now, we accept this inconsistent behavior
14846 * and simply do nothing here. */
14847
14848 if (mData->mSession.mState == SessionState_Unlocking)
14849 return S_OK;
14850 }
14851
14852 /* ignore notifications sent after #OnSessionEnd() is called */
14853 if (!directControl)
14854 return S_OK;
14855
14856 return directControl->UpdateMachineState(mData->mMachineState);
14857}
14858
14859
14860/*static*/
14861HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14862{
14863 va_list args;
14864 va_start(args, pcszMsg);
14865 HRESULT rc = setErrorInternal(aResultCode,
14866 getStaticClassIID(),
14867 getStaticComponentName(),
14868 Utf8Str(pcszMsg, args),
14869 false /* aWarning */,
14870 true /* aLogIt */);
14871 va_end(args);
14872 return rc;
14873}
14874
14875
14876HRESULT Machine::updateState(MachineState_T aState)
14877{
14878 NOREF(aState);
14879 ReturnComNotImplemented();
14880}
14881
14882HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14883{
14884 NOREF(aProgress);
14885 ReturnComNotImplemented();
14886}
14887
14888HRESULT Machine::endPowerUp(LONG aResult)
14889{
14890 NOREF(aResult);
14891 ReturnComNotImplemented();
14892}
14893
14894HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14895{
14896 NOREF(aProgress);
14897 ReturnComNotImplemented();
14898}
14899
14900HRESULT Machine::endPoweringDown(LONG aResult,
14901 const com::Utf8Str &aErrMsg)
14902{
14903 NOREF(aResult);
14904 NOREF(aErrMsg);
14905 ReturnComNotImplemented();
14906}
14907
14908HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14909 BOOL *aMatched,
14910 ULONG *aMaskedInterfaces)
14911{
14912 NOREF(aDevice);
14913 NOREF(aMatched);
14914 NOREF(aMaskedInterfaces);
14915 ReturnComNotImplemented();
14916
14917}
14918
14919HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14920{
14921 NOREF(aId); NOREF(aCaptureFilename);
14922 ReturnComNotImplemented();
14923}
14924
14925HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14926 BOOL aDone)
14927{
14928 NOREF(aId);
14929 NOREF(aDone);
14930 ReturnComNotImplemented();
14931}
14932
14933HRESULT Machine::autoCaptureUSBDevices()
14934{
14935 ReturnComNotImplemented();
14936}
14937
14938HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14939{
14940 NOREF(aDone);
14941 ReturnComNotImplemented();
14942}
14943
14944HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14945 ComPtr<IProgress> &aProgress)
14946{
14947 NOREF(aSession);
14948 NOREF(aProgress);
14949 ReturnComNotImplemented();
14950}
14951
14952HRESULT Machine::finishOnlineMergeMedium()
14953{
14954 ReturnComNotImplemented();
14955}
14956
14957HRESULT Machine::clipboardAreaRegister(const std::vector<com::Utf8Str> &aParms, ULONG *aID)
14958{
14959 RT_NOREF(aParms, aID);
14960 ReturnComNotImplemented();
14961}
14962
14963HRESULT Machine::clipboardAreaUnregister(ULONG aID)
14964{
14965 RT_NOREF(aID);
14966 ReturnComNotImplemented();
14967}
14968
14969HRESULT Machine::clipboardAreaAttach(ULONG aID)
14970{
14971 RT_NOREF(aID);
14972 ReturnComNotImplemented();
14973}
14974HRESULT Machine::clipboardAreaDetach(ULONG aID)
14975{
14976 RT_NOREF(aID);
14977 ReturnComNotImplemented();
14978}
14979
14980HRESULT Machine::clipboardAreaGetMostRecent(ULONG *aID)
14981{
14982 RT_NOREF(aID);
14983 ReturnComNotImplemented();
14984}
14985
14986HRESULT Machine::clipboardAreaGetRefCount(ULONG aID, ULONG *aRefCount)
14987{
14988 RT_NOREF(aID, aRefCount);
14989 ReturnComNotImplemented();
14990}
14991
14992HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14993 std::vector<com::Utf8Str> &aValues,
14994 std::vector<LONG64> &aTimestamps,
14995 std::vector<com::Utf8Str> &aFlags)
14996{
14997 NOREF(aNames);
14998 NOREF(aValues);
14999 NOREF(aTimestamps);
15000 NOREF(aFlags);
15001 ReturnComNotImplemented();
15002}
15003
15004HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
15005 const com::Utf8Str &aValue,
15006 LONG64 aTimestamp,
15007 const com::Utf8Str &aFlags)
15008{
15009 NOREF(aName);
15010 NOREF(aValue);
15011 NOREF(aTimestamp);
15012 NOREF(aFlags);
15013 ReturnComNotImplemented();
15014}
15015
15016HRESULT Machine::lockMedia()
15017{
15018 ReturnComNotImplemented();
15019}
15020
15021HRESULT Machine::unlockMedia()
15022{
15023 ReturnComNotImplemented();
15024}
15025
15026HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
15027 ComPtr<IMediumAttachment> &aNewAttachment)
15028{
15029 NOREF(aAttachment);
15030 NOREF(aNewAttachment);
15031 ReturnComNotImplemented();
15032}
15033
15034HRESULT Machine::reportVmStatistics(ULONG aValidStats,
15035 ULONG aCpuUser,
15036 ULONG aCpuKernel,
15037 ULONG aCpuIdle,
15038 ULONG aMemTotal,
15039 ULONG aMemFree,
15040 ULONG aMemBalloon,
15041 ULONG aMemShared,
15042 ULONG aMemCache,
15043 ULONG aPagedTotal,
15044 ULONG aMemAllocTotal,
15045 ULONG aMemFreeTotal,
15046 ULONG aMemBalloonTotal,
15047 ULONG aMemSharedTotal,
15048 ULONG aVmNetRx,
15049 ULONG aVmNetTx)
15050{
15051 NOREF(aValidStats);
15052 NOREF(aCpuUser);
15053 NOREF(aCpuKernel);
15054 NOREF(aCpuIdle);
15055 NOREF(aMemTotal);
15056 NOREF(aMemFree);
15057 NOREF(aMemBalloon);
15058 NOREF(aMemShared);
15059 NOREF(aMemCache);
15060 NOREF(aPagedTotal);
15061 NOREF(aMemAllocTotal);
15062 NOREF(aMemFreeTotal);
15063 NOREF(aMemBalloonTotal);
15064 NOREF(aMemSharedTotal);
15065 NOREF(aVmNetRx);
15066 NOREF(aVmNetTx);
15067 ReturnComNotImplemented();
15068}
15069
15070HRESULT Machine::authenticateExternal(const std::vector<com::Utf8Str> &aAuthParams,
15071 com::Utf8Str &aResult)
15072{
15073 NOREF(aAuthParams);
15074 NOREF(aResult);
15075 ReturnComNotImplemented();
15076}
15077
15078com::Utf8Str Machine::i_controllerNameFromBusType(StorageBus_T aBusType)
15079{
15080 com::Utf8Str strControllerName = "Unknown";
15081 switch (aBusType)
15082 {
15083 case StorageBus_IDE:
15084 {
15085 strControllerName = "IDE";
15086 break;
15087 }
15088 case StorageBus_SATA:
15089 {
15090 strControllerName = "SATA";
15091 break;
15092 }
15093 case StorageBus_SCSI:
15094 {
15095 strControllerName = "SCSI";
15096 break;
15097 }
15098 case StorageBus_Floppy:
15099 {
15100 strControllerName = "Floppy";
15101 break;
15102 }
15103 case StorageBus_SAS:
15104 {
15105 strControllerName = "SAS";
15106 break;
15107 }
15108 case StorageBus_USB:
15109 {
15110 strControllerName = "USB";
15111 break;
15112 }
15113 default:
15114 break;
15115 }
15116 return strControllerName;
15117}
15118
15119HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
15120{
15121 /* it's assumed the machine already registered. If not, it's a problem of the caller */
15122
15123 AutoCaller autoCaller(this);
15124 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
15125
15126 HRESULT rc = S_OK;
15127
15128 /* get usb device filters from host, before any writes occurred to avoid deadlock */
15129 ComPtr<IUSBDeviceFilters> usbDeviceFilters;
15130 rc = getUSBDeviceFilters(usbDeviceFilters);
15131 if (FAILED(rc)) return rc;
15132
15133 NOREF(aFlags);
15134 com::Utf8Str osTypeId;
15135 ComObjPtr<GuestOSType> osType = NULL;
15136
15137 /* Get the guest os type as a string from the VB. */
15138 rc = getOSTypeId(osTypeId);
15139 if (FAILED(rc)) return rc;
15140
15141 /* Get the os type obj that coresponds, can be used to get
15142 * the defaults for this guest OS. */
15143 rc = mParent->i_findGuestOSType(Bstr(osTypeId), osType);
15144 if (FAILED(rc)) return rc;
15145
15146 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
15147
15148 /* Let the OS type select 64-bit ness. */
15149 mHWData->mLongMode = osType->i_is64Bit()
15150 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
15151
15152 /* Let the OS type enable the X2APIC */
15153 mHWData->mX2APIC = osType->i_recommendedX2APIC();
15154
15155 /* This one covers IOAPICEnabled. */
15156 mBIOSSettings->i_applyDefaults(osType);
15157
15158 /* Initialize default record settings. */
15159 mRecordingSettings->i_applyDefaults();
15160
15161 /* Initialize default BIOS settings here */
15162 /* Hardware virtualization must be ON by default */
15163 mHWData->mAPIC = true;
15164 mHWData->mHWVirtExEnabled = true;
15165
15166 rc = osType->COMGETTER(RecommendedRAM)(&mHWData->mMemorySize);
15167 if (FAILED(rc)) return rc;
15168
15169 rc = osType->COMGETTER(RecommendedGraphicsController)(&mHWData->mGraphicsControllerType);
15170 if (FAILED(rc)) return rc;
15171
15172 rc = osType->COMGETTER(RecommendedVRAM)(&mHWData->mVRAMSize);
15173 if (FAILED(rc)) return rc;
15174
15175 rc = osType->COMGETTER(Recommended2DVideoAcceleration)(&mHWData->mAccelerate2DVideoEnabled);
15176 if (FAILED(rc)) return rc;
15177
15178 rc = osType->COMGETTER(Recommended3DAcceleration)(&mHWData->mAccelerate3DEnabled);
15179 if (FAILED(rc)) return rc;
15180
15181 rc = osType->COMGETTER(RecommendedFirmware)(&mHWData->mFirmwareType);
15182 if (FAILED(rc)) return rc;
15183
15184 rc = osType->COMGETTER(RecommendedPAE)(&mHWData->mPAEEnabled);
15185 if (FAILED(rc)) return rc;
15186
15187 rc = osType->COMGETTER(RecommendedHPET)(&mHWData->mHPETEnabled);
15188 if (FAILED(rc)) return rc;
15189
15190 BOOL mRTCUseUTC;
15191 rc = osType->COMGETTER(RecommendedRTCUseUTC)(&mRTCUseUTC);
15192 if (FAILED(rc)) return rc;
15193
15194 setRTCUseUTC(mRTCUseUTC);
15195 if (FAILED(rc)) return rc;
15196
15197 /* the setter does more than just the assignment, so use it */
15198 ChipsetType_T enmChipsetType;
15199 rc = osType->COMGETTER(RecommendedChipset)(&enmChipsetType);
15200 if (FAILED(rc)) return rc;
15201
15202 rc = COMSETTER(ChipsetType)(enmChipsetType);
15203 if (FAILED(rc)) return rc;
15204
15205 rc = osType->COMGETTER(RecommendedTFReset)(&mHWData->mTripleFaultReset);
15206 if (FAILED(rc)) return rc;
15207
15208 /* Apply network adapters defaults */
15209 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
15210 mNetworkAdapters[slot]->i_applyDefaults(osType);
15211
15212 /* Apply serial port defaults */
15213 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
15214 mSerialPorts[slot]->i_applyDefaults(osType);
15215
15216 /* Apply parallel port defaults - not OS dependent*/
15217 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
15218 mParallelPorts[slot]->i_applyDefaults();
15219
15220 /* Audio stuff. */
15221 AudioControllerType_T audioController;
15222 rc = osType->COMGETTER(RecommendedAudioController)(&audioController);
15223 if (FAILED(rc)) return rc;
15224
15225 rc = mAudioAdapter->COMSETTER(AudioController)(audioController);
15226 if (FAILED(rc)) return rc;
15227
15228 AudioCodecType_T audioCodec;
15229 rc = osType->COMGETTER(RecommendedAudioCodec)(&audioCodec);
15230 if (FAILED(rc)) return rc;
15231
15232 rc = mAudioAdapter->COMSETTER(AudioCodec)(audioCodec);
15233 if (FAILED(rc)) return rc;
15234
15235 rc = mAudioAdapter->COMSETTER(Enabled)(true);
15236 if (FAILED(rc)) return rc;
15237
15238 rc = mAudioAdapter->COMSETTER(EnabledOut)(true);
15239 if (FAILED(rc)) return rc;
15240
15241 /* Storage Controllers */
15242 StorageControllerType_T hdStorageControllerType;
15243 StorageBus_T hdStorageBusType;
15244 StorageControllerType_T dvdStorageControllerType;
15245 StorageBus_T dvdStorageBusType;
15246 BOOL recommendedFloppy;
15247 ComPtr<IStorageController> floppyController;
15248 ComPtr<IStorageController> hdController;
15249 ComPtr<IStorageController> dvdController;
15250 Utf8Str strFloppyName, strDVDName, strHDName;
15251
15252 /* GUI auto generates controller names using bus type. Do the same*/
15253 strFloppyName = i_controllerNameFromBusType(StorageBus_Floppy);
15254
15255 /* Floppy recommended? add one. */
15256 rc = osType->COMGETTER(RecommendedFloppy(&recommendedFloppy));
15257 if (FAILED(rc)) return rc;
15258 if (recommendedFloppy)
15259 {
15260 rc = addStorageController(strFloppyName,
15261 StorageBus_Floppy,
15262 floppyController);
15263 if (FAILED(rc)) return rc;
15264 }
15265
15266 /* Setup one DVD storage controller. */
15267 rc = osType->COMGETTER(RecommendedDVDStorageController)(&dvdStorageControllerType);
15268 if (FAILED(rc)) return rc;
15269
15270 rc = osType->COMGETTER(RecommendedDVDStorageBus)(&dvdStorageBusType);
15271 if (FAILED(rc)) return rc;
15272
15273 strDVDName = i_controllerNameFromBusType(dvdStorageBusType);
15274
15275 rc = addStorageController(strDVDName,
15276 dvdStorageBusType,
15277 dvdController);
15278 if (FAILED(rc)) return rc;
15279
15280 rc = dvdController->COMSETTER(ControllerType)(dvdStorageControllerType);
15281 if (FAILED(rc)) return rc;
15282
15283 /* Setup one HDD storage controller. */
15284 rc = osType->COMGETTER(RecommendedHDStorageController)(&hdStorageControllerType);
15285 if (FAILED(rc)) return rc;
15286
15287 rc = osType->COMGETTER(RecommendedHDStorageBus)(&hdStorageBusType);
15288 if (FAILED(rc)) return rc;
15289
15290 strHDName = i_controllerNameFromBusType(hdStorageBusType);
15291
15292 if (hdStorageBusType != dvdStorageBusType && hdStorageControllerType != dvdStorageControllerType)
15293 {
15294 rc = addStorageController(strHDName,
15295 hdStorageBusType,
15296 hdController);
15297 if (FAILED(rc)) return rc;
15298
15299 rc = hdController->COMSETTER(ControllerType)(hdStorageControllerType);
15300 if (FAILED(rc)) return rc;
15301 }
15302 else
15303 {
15304 /* The HD controller is the same as DVD: */
15305 hdController = dvdController;
15306 }
15307
15308 /* Limit the AHCI port count if it's used because windows has trouble with
15309 * too many ports and other guest (OS X in particular) may take extra long
15310 * boot: */
15311
15312 // pParent = static_cast<Medium*>(aP)
15313 IStorageController *temp = hdController;
15314 ComObjPtr<StorageController> storageController;
15315 storageController = static_cast<StorageController *>(temp);
15316
15317 // tempHDController = aHDController;
15318 if (hdStorageControllerType == StorageControllerType_IntelAhci)
15319 storageController->COMSETTER(PortCount)(1 + (dvdStorageControllerType == StorageControllerType_IntelAhci));
15320 else if (dvdStorageControllerType == StorageControllerType_IntelAhci)
15321 storageController->COMSETTER(PortCount)(1);
15322
15323 /* USB stuff */
15324
15325 bool ohciEnabled = false;
15326
15327 ComPtr<IUSBController> usbController;
15328 BOOL recommendedUSB3;
15329 BOOL recommendedUSB;
15330 BOOL usbProxyAvailable;
15331
15332 getUSBProxyAvailable(&usbProxyAvailable);
15333 if (FAILED(rc)) return rc;
15334
15335 rc = osType->COMGETTER(RecommendedUSB3)(&recommendedUSB3);
15336 if (FAILED(rc)) return rc;
15337 rc = osType->COMGETTER(RecommendedUSB)(&recommendedUSB);
15338 if (FAILED(rc)) return rc;
15339
15340 if (!usbDeviceFilters.isNull() && recommendedUSB3 && usbProxyAvailable)
15341 {
15342#ifdef VBOX_WITH_EXTPACK
15343 /* USB 3.0 is only available if the proper ExtPack is installed. */
15344 ExtPackManager *aManager = mParent->i_getExtPackManager();
15345 if (aManager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15346 {
15347 rc = addUSBController("XHCI", USBControllerType_XHCI, usbController);
15348 if (FAILED(rc)) return rc;
15349
15350 /* xHci includes OHCI */
15351 ohciEnabled = true;
15352 }
15353#endif
15354 }
15355 if ( !ohciEnabled
15356 && !usbDeviceFilters.isNull() && recommendedUSB && usbProxyAvailable)
15357 {
15358 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15359 if (FAILED(rc)) return rc;
15360 ohciEnabled = true;
15361
15362#ifdef VBOX_WITH_EXTPACK
15363 /* USB 2.0 is only available if the proper ExtPack is installed.
15364 * Note. Configuring EHCI here and providing messages about
15365 * the missing extpack isn't exactly clean, but it is a
15366 * necessary evil to patch over legacy compatability issues
15367 * introduced by the new distribution model. */
15368 ExtPackManager *manager = mParent->i_getExtPackManager();
15369 if (manager->i_isExtPackUsable(ORACLE_PUEL_EXTPACK_NAME))
15370 {
15371 rc = addUSBController("EHCI", USBControllerType_EHCI, usbController);
15372 if (FAILED(rc)) return rc;
15373 }
15374#endif
15375 }
15376
15377 /* Set recommended human interface device types: */
15378 BOOL recommendedUSBHID;
15379 rc = osType->COMGETTER(RecommendedUSBHID)(&recommendedUSBHID);
15380 if (FAILED(rc)) return rc;
15381
15382 if (recommendedUSBHID)
15383 {
15384 mHWData->mKeyboardHIDType = KeyboardHIDType_USBKeyboard;
15385 mHWData->mPointingHIDType = PointingHIDType_USBMouse;
15386 if (!ohciEnabled && !usbDeviceFilters.isNull())
15387 {
15388 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15389 if (FAILED(rc)) return rc;
15390 }
15391 }
15392
15393 BOOL recommendedUSBTablet;
15394 rc = osType->COMGETTER(RecommendedUSBTablet)(&recommendedUSBTablet);
15395 if (FAILED(rc)) return rc;
15396
15397 if (recommendedUSBTablet)
15398 {
15399 mHWData->mPointingHIDType = PointingHIDType_USBTablet;
15400 if (!ohciEnabled && !usbDeviceFilters.isNull())
15401 {
15402 rc = addUSBController("OHCI", USBControllerType_OHCI, usbController);
15403 if (FAILED(rc)) return rc;
15404 }
15405 }
15406 return S_OK;
15407}
15408
15409/* This isn't handled entirely by the wrapper generator yet. */
15410#ifdef VBOX_WITH_XPCOM
15411NS_DECL_CLASSINFO(SessionMachine)
15412NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
15413
15414NS_DECL_CLASSINFO(SnapshotMachine)
15415NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
15416#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